12

Consider the following code:

  #include <stdio.h>
  int main(void)
  {
    int a[10];
    printf("%d",(int)sizeof(a)); //prints 10*sizeof(int) (40 on my compiler)
    printf("%d",(int)sizeof(a-3)); //prints sizeof(int) (4 on my compiler)

  }

I know that sizeof() is a compile time operator but I was surprised to see the output of second printf(). What could be the reason? Is there an implicit conversion of the argument of sizeof() from an array-type to an integer type?

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • 3
    The second one actually prints `sizeof(int *)`, not `sizeof(int)`. `sizeof(int *)` just happens to be 4 too on your platform. – AnT stands with Russia Feb 06 '10 at 03:14
  • Also, the proper way to print a `size_t` value (the value returned by `sizeof`) is with `"%zu"`, assuming your compiler supports that particular C99 feature. If not, a better bet would be to cast it to an `unsigned` or `unsigned long`. – Chris Lutz Feb 06 '10 at 09:29

4 Answers4

29

The sizeof operator doesn't evaluate its argument, it only looks at the type of its operand.

Let's say you have an array a with type "array [N] of type T". Then, in most cases, the type of the name a is "pointer to T" (T *), and the value of the pointer is the address of the first element of the array (&a[0]). That is, the name of an array "decays" to a pointer to its first element. The "decaying" doesn't happen in the following cases:

  • when a is used with the address-of (&) operator,
  • in the initialization of a (it is illegal to assign to arrays in C), and
  • when a is the operand of the sizeof operator.

So, sizeof a gives you N times sizeof(T).

When you do sizeof(a-3), the type of the operand to sizeof is determined by the expression a-3. Since a in a-3 is used in a value context (i.e., none of the three contexts above), its type is "pointer to int", and the name a decays to a pointer to a[0]. As such, calculating a-3 is undefined behavior, but since sizeof doesn't evaluate its argument, a-3 is used only to determine the type of the operand, so the code is OK (see the first link above for more).

From the above, sizeof(a-3) is equivalent to sizeof(int *), which is 4 on your computer.

The "conversion" is due to the subtraction operator. You can see a similar, and perhaps more surprising, result with the comma operator:

printf("%zu\n", sizeof(1, a));

will also print sizeof(int *), because of the comma operator resulting in a getting used in a value context.

Community
  • 1
  • 1
Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
5

(a-3) has type int*, and it prints you sizeof(int*) which is 4 on your platform.

And note that sizeof() is no longer compile-time constant in C99 (due to variadic-length arrays).

Alexander Poluektov
  • 7,844
  • 1
  • 28
  • 32
  • 6
    To be clear - the result of `sizeof` is not a compiler time constant only when the operand is a variable length array. It still is a constant for other operand types. – Michael Burr Feb 05 '10 at 16:13
1

Nope, in the second case the argument is interpreted as an int* pointer which happens to also have size equal to 4 on your machine.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
1

sizeof() returns the size of a type, so the type is what's important.

It also shouldn't be printed with %d. At the very least, explicitly cast it to unsigned long or unsigned long long and use the appropriate format specifier. When teaching C, I had a student get the wrong answer by printing size_t with %d as the textbook mistakenly said to do.

Anyway, a is an array type. In C, array types decay to pointer types if you do almost anything with them or sneeze loudly, so almost anything you do to a will yield a pointer type. As you've found out, adding or subtracting a number will decay. (After all, an array can't be used in arithmetic, but a pointer can.)

David Thornley
  • 56,304
  • 9
  • 91
  • 158
  • @David: as long as the result doesn't overflow, if one casts the expression as `(int)`, then printing it with `"%d"` is okay. In this case, it is almost certain that the size will fit in an `int`. In C99, one would use `"%zu"` of course. For C89, your suggestion is better than printing with `"%d"` after casting as `(int)` because it is more overflow-prone. – Alok Singhal Feb 05 '10 at 15:42
  • @Alok: True, but doing the casting is much more important than what you cast to. Also, I'm very thoroughly familiar with C89, and haven't used C99, and it probably shows. – David Thornley Feb 05 '10 at 17:26
  • The OP has the cast in his question. I agree with you - without the cast, `"%d"` is wrong, but the OP's `printf()` call is OK as long as there is no overflow. – Alok Singhal Feb 05 '10 at 17:29