2

I wrote this simple program

#include <stdio.h>

int main(void)
{
    int array[10];

    printf("sizeof(array):  %lu\n", sizeof(array));
    printf("sizeof(&array): %lu\n", sizeof(&array));

    printf("array:          %p\n", array);
    printf("&array:         %p\n", &array);

    printf("value:          ");
    scanf("%d", array); // 1
    //scanf("%d, &array"); // 2
}

Output:

sizeof(array):  40
sizeof(&array): 8
array:          0x7fffaab6f480
&array:         0x7fffaab6f480
value:          10

It compiles when I use 1. However, it doesn't when I use 2!

I get this compilation warning

warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type 
‘int (*)[10]’ [-Wformat=]
  scanf("%d", &array);
  ^

although array and &array have the same value, they have different sizes (40 and 8 respectively) on my system.

On the other hand, this code

int array[10];
fread(&array, x, y, z);

compiles and works perfectly as

int array[10];
fread(array, x, y, z);

I noticed in the warning message that &array has the type int (*)[10]. Now what does that mean?

I also believe I have no troubles with fread() because it accepts a void *, but what about scanf()? How does it differentiate between pointer types?

And why doesn't it treat array as &array even though they're practically have the same value?

kzidane
  • 753
  • 3
  • 11
  • 29

4 Answers4

3

Arrays decays to pointers, so using the array as argument is the same as passing a pointer to the first element.

When you're doing &array, you get a pointer to the array (the int (*)[10] thing), and not a pointer to an integer.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I thought about this, but then I asked myself if `&array` is regarded as a pointer to the array and `array` is regarded as a pointer to the first int in that array, then why is `sizeof(&array)` evaluated to `8` while `sizeof(array)` is evaluated to 40? And if they're both can be used interchangeably then why am I getting a warning when using it with `scanf()`, but not with `fread()`? – kzidane Jul 04 '14 at 13:18
  • 1
    Because `sizeof` is not a function. Here the array does not decay. – undur_gongor Jul 04 '14 at 13:18
  • So it decays sometimes and sometimes not? How should I know that it decays in a specific situation then? – kzidane Jul 04 '14 at 13:19
  • Yes, sometimes. You'll have to understand/learn when. In function arguments it decays. As operand to some operators (`sizeof`) it doesn't. – undur_gongor Jul 04 '14 at 13:29
  • @KareemMesbah in general, `&array` and `&array[0]` cannot be used interchangeably. However it appears so in your `scanf` example because modern PCs use the same size and representation for all pointers. But the C standard does not require that; e.g. it's possible that `sizeof(&array)` is 8, while `sizeof(&array[0])` is 9. – M.M Jul 04 '14 at 13:30
  • @undur_gongor it does decay as operand of `[]` . `array[4]` does pointer arithmetic on the pointer that `array` decayed to. – M.M Jul 04 '14 at 13:32
  • 2
    @KareemMesbah See e.g. [this answer from yesterday](http://stackoverflow.com/a/24544722/440558) for some. – Some programmer dude Jul 04 '14 at 13:35
  • @MattMcNabb I didn't mean `&array` and `&array[0]`, but rather I meant `&array` and `array` from my `fread()` example. I think it's clear now that they're definitely of different types. However, as I get, `fread()` performs some sort of casting to `void *`. And no problems with that since they're (i.e., `array` and `&array` and even `&array[0]`) having the same value. Is that right? – kzidane Jul 04 '14 at 13:41
  • 2
    @KareemMesbah In theory `array`, `&array` and `&array[0]` are different, in practice they will all be the same pointer on a modern PC. The important thing is to know that they can behave differently, and to know when you should use one or the other. – Some programmer dude Jul 04 '14 at 13:48
  • @KareemMesbah `array` means `&array[0]` in your fread example – M.M Jul 05 '14 at 02:56
  • @JoachimPileborg They must be the same after conversion to `(void *)` – M.M Jul 05 '14 at 02:56
2

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 behaviour.

This happens in the second case, scanf("%d", &array). As your examples show &array does not have type int *, therefore the behaviour 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 behaviour, 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.

M.M
  • 138,810
  • 21
  • 208
  • 365
0

Both are syntactically correct C, and do exactly the same thing, as you suggest. However, one is more correct than the other. That said, neither is perfect.

To be properly pedantic, you should have this:

scanf("%d", &array[0]);

.. because that's a pointer to an integer, array[0], and what you really want.

In fact, you can pass anything to scanf (if you ignore the documentation), and the compiler is expected to accept it (misuse of the standard library does not trigger an error, traditionally). If you pass something invalid then your program is expected to fail at runtime (or not, if you're lucky -- undefined behaviour is like that).

However, your compiler is trying to be helpful and interpret the scanf format string for you to make sure you did the right thing, but if you look carefully, it's not an error, its a warning. You're free to ignore it, if you choose.

The reason you don't get an error with fread is because that function does not take an integer, it takes void *, and both forms can be converted to that type just as easily.

ams
  • 24,923
  • 4
  • 54
  • 75
  • 2
    Both are **not** valid C. `scanf` requires a pointer to an integer, not a pointer to a pointer. – R.. GitHub STOP HELPING ICE Jul 04 '14 at 13:19
  • OK, it's valid C *language*. `scanf` is defined as a variadic function and will take whatever junk you give it. It's not necessarily a valid use of the *library* standard. – ams Jul 04 '14 at 13:24
  • 2
    "valid C" usually means "C that does not cause undefined behaviour". Not just "does not give a compiler error". – M.M Jul 04 '14 at 13:29
  • I think the difference is between syntactically valid code and (also) semantically valid code. – undur_gongor Jul 04 '14 at 13:30
  • @ams: If the actual type of the argument does not match the type required by the conversion specifier (`%d`), the behavior is undefined. I consider a C program which reaches a statement that invokes undefined behavior not to be "valid C". – R.. GitHub STOP HELPING ICE Jul 04 '14 at 15:38
  • Well, that'll be all of them them ;-) – ams Jul 04 '14 at 15:46
0

First, your program was compiled very well - warning is not an error.

The warning message tells you that there's mismatch between format and type: "%d" format requires int *, but you sent &array, whose type is int (*)[10].


array is 10-length array of int. Its type is int [10]. However, array can be converted to pointer, that is, &array[0].

When you try to pass array to function, you actually pass a pointer, &array[0]. So both this code

printf("%d", array);
scanf("%d", array);

and this another code

printf("%d", &array[0]);
scanf("%d", &array[0]);

are the same.

Now let's look at another thing, &array. Its type is int (*)[10], that is, pointer to array.

Of course, the actual values of both &array[0] and &array are the same, but they have different types. So they're diferent things, as 'A' and 65 are different.

Altough it looks okay since the values are the same, I don't think it is such a good practice - Use array or &array[0].

ikh
  • 10,119
  • 1
  • 31
  • 70