whether 's' is a decay pointer…
s
is a structure. Structures are not automatically converted to pointers the way arrays are. (Some people say “decay,” but this is a colloquial term and is inaccurate.)
… or a separate stack frame is getting allocated for the structure,…
With s
defined as you show inside main
, the compiler automatically allocates space for it. In common C implementations, the compiler uses stack space for this (except that optimization may result in alternatives including using processor registers instead, incorporating use of the structure into other expressions, or eliminating part or all of the structure entirely). If the compiler does use stack space for s
, it uses space within the stack frame for main
; it does not allocate a separate stack frame.
(Generally, a stack frame is the space on the stack used by one function, sometimes with some modifications such as “leaf” functions that do not call other routines and may not establish a full stack frame.)
printf("%d", *((int *) &s + 1));
*((int *) &s + 1)
is not proper C code because its behavior is not defined by the C standard and likely not by your compiler either.
&s
is the address of the structure s
. When we convert it to (int *)
, that gives the address of the first member of the structure, s.roll_no
. That is because the C standard gives us a rule that says we can convert a pointer to a structure to a pointer to its first member. C 2018 6.7.2.1 15 says:
… A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa…
Then you add 1 to this pointer, (int *) &s + 1
. That also has defined behavior. From above (int *) &s
points to s.roll_no
, so it is equivalent to &s.roll_no
. Then (int *) &s + 1
is equivalent to &s.roll_no + 1
. C 2018 6.5.6 specifies what happens with addition. I will omit specifics of the rules, but essentially &s.roll_no + 1
is defined and produces a pointer that points just beyond s.roll_no
.
However, even if the next member of the structure, physics
, is just beyond s.roll_no
1, the C standard does not define what happens when *
is applied to it. This is because C 2018 6.5.6 8 says:
If the result [of the addition] points one past the last element of the array object, it shall not be used as the operand of a unary *
operator that is evaluated.
In the context of 6.5.6, s.roll_no
is an “array object” that contains just one element, and &s.roll_no + 1
points one beyond its last element. So *(&s.roll_no + 1)
and, equivalently, *((int *) &s + 1)
violates this rule. C 2018 4 2 says that when a rule like this is violated, the behavior is not defined:
If a "shall" or "shall not" requirement that appears outside of a constraint or runtime-constraint is violated, the behavior is undefined…
Footnote
1 This is not guaranteed because C implementations are allowed to insert padding between members in structures. However, typical C implementations will not insert padding between members of identical types, as it is not needed for alignment, and this can be checked at compile time. But the statements above apply even if there is no padding.