3

I have declared a buffer as follows :

unsigned int buffer[512];

Now, later on in my code I used fread in the following manner

fread(buffer, 512, 1, infile) 

and everything worked as expected. Note the absence of the ampersand sign in front of the first argument, buffer.

My reasoning was that similar to when we use scanf, since buffer is an array, there is no need to use the ampersand sign. So far so good.

The weird thing is, even if I put an ampersand sign in front of buffer, it still works!! Ampersand sign or no ampersand sign, it doesn't seem to make a difference.

What is going on?

2

We already know that buffer is an array of ints, but underneath the hood, buffer is actually a pointer to the block of memory where these ints are stored.

For example, if the allocated block of memory for the 512 unsigned ints starts from 0x123, buffer will be equal to 0x123.

The pointer to a block of memory that's meant to store an array of values is also a pointer to the first element in that array since the first element in the array is stored at location 0x123 (considering the previous example).

When you use the bracket notation to access an element in the array (e.g., buffer[10]), this is a syntactic sugar meant to make things easier for us. But it worth knowing that this is exactly equivalent to

*(buffer + 10)

That is, go to the tenth location after buffer (i.e., 0x123) and get the value that's stored there!

The first parameter for fread() is a void pointer. And since buffer is actually a pointer, you don't get any errors!

To connect the dots, &buffer and buffer are practically the same! You may try this

#include <stdio.h>

int main(void)
{
    int array[10] = {};
    printf("array:  %p\n", array);
    printf("&array: %p\n", &array);

    if (array == (int *)&array)
    {
        printf("they're the same!\n");
    }
}

Output:

array:  0x7fff69087510
&array: 0x7fff69087510
they're the same!

Update: to give you certain answer, I asked this question on StackOverflow

And here's a quote to my accepted answer

With the scanf and printf family, you must pass the exact type that they expect (after the default argument promotions are applied - but that is not relevant in this example).

This is because the arguments correspond to a ... in the prototype, meaning that the compiler does not attempt to convert your argument to the expected type. If you use the wrong type you just get undefined behavior.

This happens in the second case, scanf("%d", &array). As your examples show &array does not have type int *, therefore the behavior is undefined.

In practice, it is likely to work anyway if your compiler uses the same representation for int * as it does for int (*)[10], which all modern compilers do AFAIK. But , of course, you should not rely on undefined behavior, especially when there is an easy fix available.

In the fread example, it matches the prototype parameter void *. Therefore the compiler converts your argument to void *. Since the first element of an array is at the same memory address as the array itself, (void *)&array == (void *)array, even though array and &array have different types (and possibly even different representations), they both point to the same memory address.

There are other answers though, so feel free to comment on/choose any of them!

8
  • I knew that buffer, underneath the hood, has the address of the first array value. So it is a pointer to the array. I also get that doing buffer[10] is equivalent to (buffer + 10). That is precisely why i passed in buffer without the ampersand sign. But then doing &buffer seems like taking the address of an address and that seems wrong to me. Also, suppose i declare a variable -> int data. If i want to pass this into buffer should i pass in only data or &data.. What if I declare a variable, int p; Now if i do something like p = &data, then what should i pass into fread, p or &p or *p? – Aditya Kashyap Jul 4 '14 at 10:17
  • @user1400 so does that answer your question? – Kareem Jul 4 '14 at 10:18
  • The point is that buffer and &buffer are both pointing to the same thing i.e. the first element of the buffer, they are just of different types and since they have the same values fread is accepting both these values. Do not think of &buffer as the address of a pointer to buffer, it is the address of the buffer itself which will be stored as the address of the first element. Moreover with fread you can pass values to a buffer/struct/array so if you are declaring data to be an array and then doing p = &data it would mean you are storing the address of data in p and so you will need to pass p. – Monkey D. Luffy Jul 4 '14 at 10:29
  • my point is that buffer and &buffer are not the same thing! For example, in the scanf command. Say we have declared a variable like, int array[10]. In scanf we can do, scanf("%d\n", array); and not scanf("%d\n", &array); – Aditya Kashyap Jul 4 '14 at 10:46
  • @user1400 probably fread() perform some sort of casting! – Kareem Jul 4 '14 at 11:15
1

The name of an array usually evaluates to the address of the first element of the array, so array and &array have the same value (but different types, so array+1 and &array+1 will not be equal if the array is more than 1 element long).

There are two exceptions to this: when the array name is an operand of sizeof or unary & (address-of), the name refers to the array object itself. Thus sizeof(array) gives you the size in bytes of the entire array, not the size of a pointer.

For an array defined as T array[size], it will have type T *. When/if you increment it, you get to the next element in the array.

&array evaluates to the same address, but given the same definition, it creates a pointer of the type T(*)[size] -- i.e., it's a pointer to an array, not to a single element. If you increment this pointer, it'll add the size of the entire array, not the size of a single element. For example, with code like this:

    char array[10];
    printf("%p\t%p\n", (void*)&array, (void*)(&array+1));

We can expect the second pointer to be 10 greater than the first (because it's an array of 10 char's). Since %p typically converts pointers in hexadecimal, it might look something like:

    0xbfcfb32e  0xbfcfb338
0

Ampersand sign or no ampersand sign, it doesn't seem to make a difference.

That's correct. When you declare the int array called buffer, the address of the array is stored in the variable buffer. When you want to access that array, you can simply use buffer. You can also use &buffer because it is equivalent in this case to buffer.

That would be different if you had declared a pointer to the array. If you had something like

unsigned int *bPtr = buffer; 

then bPtr and &bPtr aren't the same thing and would have different values.

3
  • And between bPtr and &bPtr, which should i passs in to fread? By my understanding, i should pass in bPtr only, not &bPtr. Am i right? – Aditya Kashyap Jul 4 '14 at 10:25
  • P.S. I tried the statement you wrote on my computer. unsigned int *bPtr = buffer; This gives an error saying incompatible types. – Aditya Kashyap Jul 4 '14 at 10:33
  • My example was hypothetical.. not meant to be an alternate way to do the fread. What you are doing is already correct. – curiouskiwi Jul 4 '14 at 21:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .