How C stores global struct variables? And weird behavior when converting to another pointer to struct type

Viewed 62

How does C store global struct variables? Also, if I try to assign a smaller struct object to a pointer of a larger struct? Basically this:

struct A{
int a;
};
struct B{
int b;
};

struct C{
struct A a;
struct B b;
};

struct A aa;
struct C * c = (struct C*) &aa;

What memory location does c->b gets to point to?

I tried a basic example and it seems, no matter in what order I declare/initialize two variables, the compiler is always coupling s1 to s3 in the following example:

#include <stdio.h>
 
struct One{
    int a;
    int b;
} s2,s1,*sp1;
 
int e;
int ff;
 
struct Two{
    int c, d;
} q,r,t, s4,s3;
 
struct Three
{
    struct One one;
    struct Two two;
};
 
int main()
{
    e=1;
    sp1=&s1;
    sp1->a=1;
    s4.c=22;
    s3.c=10;
    struct Three * ll = (struct Three *) sp1;
    ll->two.c = 1000;
    printf("\n%d\t%d", s3.c, s4.c);
   return 0;
}

Does anyone have an idea on why it might be happening? Link for running/output: https://www.ideone.com/YP4Y1e

Edit: Thanks everyone, I understand this is bad coding practice. But I was more puzzled since in the full code example I gave, compiler was always putting s1 and s3 together in memory so my pointer ll->two had data from s3. But it seems it is readonly. If I change value of ll->two.c the same is not reflected in s3.c. That lead me to believe that compiler might be copying the data to some other location in memory and ll might not be assigned a contiguous block of memory.

Is this behavior expected? You can read random block of memory with a bigger pointer type but cannot write to it? Or it is just undefined behavior as well?

1 Answers

Where variables are physically allocated in memory is beyond the scope of the C standard. Pretty much the only guarantees you have from the C standard are these:

  • An arrays or a raw chunk of data allocated by malloc have the items allocated in adjacent order.
  • Struct members are allocated in the specified order, with the first mentioned member on the lowest address. (Struct members may however not necessarily be allocated adjacently in relation to each other.)

Some systems may allocate variables in a certain order or allow you to allocate variables at absolute addresses, but that's all beyond the scope of the C standard and non-standard language extensions as well as linker scripts are needed for that.


struct C * c = (struct C*) &aa; happens to be a valid cast since the first member of a struct C is a struct A. Accessing c->a is well-defined because of this special first member rule, which also happens to be the one guaranteeing order of allocation of struct members:

ISO 9899:2018 6.2.1/15

Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. 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. There may be unnamed padding within a structure object, but not at its beginning.

But beyond that, c doesn't actually point at a struct C so you can't access c->b, because it doesn't exist. Doing so is similar to an out of bounds access, meaning undefined behavior. I believe that 6.5.3.2 also applies: "If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined".

Your second example has the same problems.

Related