Mike L
Mike L

Reputation: 2035

understanding struct in memory

#include <stdio.h>
  typedef struct ss {
    int a;
    char b;
    int c;
  } ssa;

int main(){
  ssa *ss;
  int *c=&ss->a;
  char *d=&ss->b;
  int *e=&ss->c;

  *c=1;
  *d=2;
  *e=3;

  printf("%d=%p %d=%p %d=%p\n",*c,c++,*c,c++,*c,c);
  return 0;
}


//prints 1=0x4aaa4333ac68 2=0x4aaa4333ac6c 3=0x4aaa4333ac70



 My thinking of how should be the memory structure:
 int        |  char  | int
(68 69 6A 6B)  (6C)  (6D 6E 6F 70)

I'm trying to understand how this code works in memory.

Why int *e starts from 0x...70?

Why c++ (increment) from char (6C) goes 4 bytes more?

Thanks.

Upvotes: 1

Views: 123

Answers (2)

Filipe Gon&#231;alves
Filipe Gon&#231;alves

Reputation: 21213

First of all, these lines are illegal:

  *c=1;
  *d=2;
  *e=3;

All you have is a pointer to ssa, but you haven't actually allocated any space for the pointed-to object. Thus, these 3 lines are trying to write into unallocated memory, and you have undefined behavior.

Structure layout in memory is such that member fields are in increasing memory addresses, but the compiler is free to place any amount of padding in between for alignment reasons, although 2 structures sharing the same initial elements will have the corresponding members at the same offset. This is one reason that could justify the "gaps" between member addresses.

You should be more careful with how you call printf(). Argument evaluation order is undefined. You are changing the value of c more than once in between 2 sequence points (see Undefined behavior and sequence points). Furthermore, pointer arithmetic is only guaranteed to work correctly when performed with pointers that point to elements of the same array or one past the end.

So, in short: the code has undefined behavior all over the place. Anything can happen. A better approach would have been:

#include <stdio.h>
  typedef struct ss {
    int a;
    char b;
    int c;
  } ssa;

int main() {
  ssa ss = { 0, 0, 0 };
  int *c = &ss.a;
  char *d = &ss.b;
  int *e = &ss.c;

  printf("c=%p d=%p e=%p\n", (void *) c, (void *) d, (void *) e);
  return 0;
}

The cast to void * is necessary. You will probably see a gap of 3 bytes between the value of d and e, but keep in mind that this is highly platform dependant.

Upvotes: 3

unwind
unwind

Reputation: 399833

There is often padding inside structures, you cannot assume that each field follows the one before it immediately.

The padding is added by the compiler to make structure member access quick, and sometimes in order to make it possible. Not all processors support unaligned accesses, and even those that do can have performance penalties for such accesses.

You can use offsetof() to figure out where there is padding, but typically you shouldn't care.

Upvotes: 1

Related Questions