4

Why do these yield the same result?

uint8_t header[HEADER_SIZE];
fread(&header, sizeof(uint8_t), HEADER_SIZE, input);
fwrite(&header, sizeof(uint8_t), HEADER_SIZE, output);
uint8_t header[HEADER_SIZE];
fread(header, sizeof(uint8_t), HEADER_SIZE, input);
fwrite(header, sizeof(uint8_t), HEADER_SIZE, output);

In the first version, would the & not give access just to the location of the array, and not the array itself like in the second version? Were it a single valuable I understand why an & would be used, but I assumed an array is itself a "pointer", generally speaking, so there would be no reason to get its address again? Is it just redundancy in that case?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Dharmapala
  • 65
  • 4
  • 4
    [How come an array's address is equal to its value in C?](https://stackoverflow.com/q/2528318) – 001 Jun 13 '22 at 19:32
  • 1
    What is the difference between "the location of the array" and "the array itself"? – Scott Hunter Jun 13 '22 at 19:33
  • I did think there would be no difference but I wasn't exactly sure. That is however why I don't understand why some choose to put & in front of the array name – Dharmapala Jun 13 '22 at 19:35
  • Adding to the existing answers: an array name like `header` by itself usually means the same as `&header[0]`. – aschepler Jun 13 '22 at 20:28
  • @Dharmapala maybe to make the code clearer, make it explicit that an address is being passed? To make passing array arguments consistent with every other type? Sometimes, I hate C:) – Martin James Jun 13 '22 at 23:01

2 Answers2

2

The function fread is declared the following way

size_t fread(void * restrict ptr, size_t size, 
             size_t nmemb, 
             FILE * restrict stream);

Its first parameter has the unqualified type void * (actually the parameter type is the qualified type void * restrict) . A pointer to object of any type may be assigned to a pointer of the type void *.

The expression &header has the pointer type uint8_t ( * )[HEADER_SIZE] and yields the initial address of the extent of memory occupied by the array,

The expression header used as an argument of a call of fread is implicitly converted to a pointer of the type uint8_t * and yields the same initial address of the extent of memory occupied by the array.

Thus these calls

fread(&header, sizeof(uint8_t), HEADER_SIZE, input);
fwrite(header, sizeof(uint8_t), HEADER_SIZE, output);

are equivalent in sense that the function fread gets the same values for its parameters.

Consider the following simple demonstration program.

#include <stdio.h>

int main( void )
{
    char s[6] = "Hello";

    printf( "The value of the expression  s is %p\n", ( void * )s );
    printf( "The value of the expression &s is %p\n", ( void * )&s );
}

The program output might look like

The value of the expression  s is 0x7fffa4577a2a
The value of the expression &s is 0x7fffa4577a2a

As you can see the both outputted values are equal each other though in the first call the used argument expression has the type char * while in the second case the used argument expression has the type char ( * )[6].

However if you will write for example

#include <stdio.h>

int main( void )
{
    char s[6] = "Hello";

    printf( "%s\n", s );
    printf( "%s\n", &s );
}

then the compiler can issue a message for the second call of printf that the function expects an argument of the type char * instead of the type char ( * )[6] though the both values of the expressions are equal each other.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

header is an array of char. Passing an array as an argument effectively passes a pointer to its first element, fread and fwrite accept pointers to void as their first argument so fread(header, sizeof(uint8_t), HEADER_SIZE, input); is fine although for consistency with the array definition it would be safer to write:

    fread(header, sizeof(*header), HEADER_SIZE, input);

&header has a different type: pointer to one or more arrays of HEADER_SIZE characters (uint8_t (*)[HEADER_SIZE]). Since fread accepts any kind of pointer, the compiler will accept the call fread(&header, sizeof(uint8_t), HEADER_SIZE, input); without a warning.

Because header and &header have the same address, fread will behave as expected, albeit this behavior is not guaranteed by the C Standard. Note however that if header was a pointer to char, passing &header would produce a very different result, undefined behavior indeed as the value of the pointer would be overwritten and probably many more bytes beyond...

chqrlie
  • 131,814
  • 10
  • 121
  • 189