Sizeof &a vs *a
Asked Answered
D

3

6

I searched thoroughly but could not find the solution to this: Assuming sizeof(int) = 4, we define:

int a[10] = {0};

What is the output of the following:
1. sizeof(&a)
2. sizeof(*a)
3. sizeof(a)

I know that sizeof(a) is equal to sizeof(int) * 10 = 40.
I also understand that *a is actually a the first element in the array, therefore sizeof(*a) is actually size of the int which resides in there, i.e. 4.

However, after running the code, I do not understand why size of &a is 8. I know that the '&' operator returns the address of the variable a, but why the sizeof the address is 8?

Dulciedulcify answered 8/8, 2019 at 7:17 Comment(2)
&a is an address which is 8 bytes if you run IA-64 (64 bit) and will be 4 in case of x86 (32 bit)Secretin
This isn't as trivial to explain as it may seem, definitely not a question you can expect a beginner to answer. There's various subtle crap involved, namely exceptions to the rule of array decay. Did you get the question from a C beginner-level teacher?Godforsaken
V
9

The size of the address depends on your architecture and is not directly related to the size of an int itself. So it’s 8 in your case, which seems pretty normal (64 bits).

Volkslied answered 8/8, 2019 at 7:21 Comment(5)
I see, so it does not depend on the type of the array at all? We were not taught this .. Thank you Michael!Dulciedulcify
@Dulciedulcify most systems use the same way of storing any addressBargeman
Yep; it doesn't depend from the type. An address is just a number that points to a memory location, so it doesn't matter if it is an int, a bool or a struct: data will be placed in a cell of the memory that is identified with a number (the memory address).Flagstad
@TheDeveloper that's not quite true on all systems, so it is better to not learn it like that. A pointer to int is a pointer to int and a pointer to char is a different thing, and they can be converted to each other.Bradytelic
Yep, but I was talking about what is an address, not about pointers in C.Flagstad
G
4

The key to understanding this is to know that arrays usually "decay" into pointers to the first element, whenever they are used in an expression. Except for some special cases. The rule of array decay and the exceptions are found in the C standard (C17 6.3.2.1/3):

Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.

As we can see, sizeof and & are exceptions to the rule.

This means that a in sizeof(&a) does not decay to a pointer. We can know this because sizeof and unary & are both unary operators with the same precdence, but with right-to-left operator associativity. Meaning that &a gets interpreted first and there is no array decay when & is used.

So we get the address of the array. In your case, the size of an int pointer of type int(*)[10]. What size a pointer got has nothing to do with the size of an int, but it is likely to be either 4 or 8 bytes on 32 and 64 bit systems respectively.

In case of sizeof(*a), then just like in the &a case, the rigth-to-left associativity of unary operators means that *a gets interpreted first. And so the array does decay into a pointer to the first element, before sizeof is applied. So *a gives the size of the first element, which is the size of an int.

And finally in case of sizeof(a) there is no array decay since a is used with sizeof and no other operator is present. Meaning we get the size of the whole array.

Godforsaken answered 8/8, 2019 at 7:44 Comment(5)
Hmmm, maybe you should mention that operator precedence and associativity would only be relevant if OP didn't use parentheses. As the question is written, it is no doubt that & will be computed before sizeof. This would only be relevant for sizeof &a instead of sizeof(&a)Bello
@Bello No, the parenthesis here is not a primary expression, but part of the sizeof operator syntax. The two cases of allowed syntax for sizeof is sizeof unary-expression or sizeof ( type-name ). But well, the right-to-left associativity of these operators is quite intuitive to understand regardless of that.Godforsaken
@Lundin: Yes, the parentheses in sizeof(a) or sizeof(&a) are a primary expression, not part of the sizeof operator syntax. The grammar for sizeof ( type-name ) requires a type name, and a and &a are not a type names. sizeof(a) or sizeof(&a) only matches sizeof unary-expression. The automatic conversion of an array to a pointer does not occur because the specification of parenthesized expressions in C 2018 6.5.1 5 says “A parenthesized expression is a primary expression. Its type and value are identical to those of the unparenthesized expression…”Albuminous
@klutt: You are correct that precedence and associativity are irrelevant; the structure of the expression is compelled by the nesting of the operations. For unary operators, the so-called precedence is relevant only when the operators are on different sides of an operand, as in * a ++, where there is nominally a choice between (* a) ++ and * (a ++). (“So-called” because the grammar actually resolves this; there is not a separate rule in C about precedence.) With sizeof & a or * ++ a, there is no way to bind the first operator to the operand without binding the second operator first.Albuminous
@klutt: Associativity is not relevant because it is only a property of binary operations (and also is resolved by the grammar, not a separate rule).Albuminous
K
2
sizeof &a == sizeof (int (*)[10]) // pointer to 10-element array of int
sizeof *a == sizeof a[0] == sizeof (int)
sizeof  a == sizeof (int [10])

and just to be complete

 sizeof &a[0] == sizeof (int *)

All of these sizes depend on the underlying platform. Pointer type sizes do not depend on the size of the pointed-to type (there’s no relation between the sizes of an int and an int *). Different pointer types may have different sizes - the only requirements are:

  • char * and void * have the same size and alignment;
  • pointers to qualified types have the same sizes and alignments as their unqualified equivalents (e.g., sizeof (const int *) == sizeof (int *);
  • all struct pointer types have the same size and alignment;
  • all union pointer types have the same size and alignment;

On any modern desktop or server system you're likely to encounter (read: x86), all object pointer types have the same size and alignment. Just be aware that there are oddball architectures out there where that may not be true.

Kerbela answered 8/8, 2019 at 14:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.