user1612078
user1612078

Reputation: 585

Accessing structure members using base address

Can you please help explain why following program correctly prints the values of all the structure members?

struct st
{
  int i;
  char c1;
  int j;
  char c2;
};

int main()
{
  struct st a = {5, 'i', 11, 'H'};
  struct st * pa = &a;

  int first;
  char second;
  int third;
  char fourth;

  first = *((int*)pa);
  second = *((char*)pa + 4); /* offset = 4 bytes = sizeof(int) */
  third = *((int*)pa + 2); /* why (pa + 2) here? */
  fourth = *((char*)pa + 12); /* why (pa + 12) here? */
  printf ("first = %d, second = %c, third = %d, fourth = %c\n", first, second, third, fourth);
  return 0;
}

Output: first = 5, second = i, third = 11, fourth = H

How can I make above program generalized?

Upvotes: 1

Views: 1071

Answers (5)

How can I make above program generalized?

The only way to make it work reliably is by not guessing at the offset. Use the standard offsetof macro, and always do the pointer arithmetic with a character pointer:

first = *(int*)((char*)pa + offsetof(struct st, i));

You don't have to name the field at the point you do the access, but you should definitely use the macro to compute the offest if you intend to pass it into your function.

Upvotes: 1

0___________
0___________

Reputation: 67546

As in another answers - padding.

But some compilers allow you to pack your structures removing (in most cases) the padding.

gcc:

struct __attribute__((packed)) st
{
....
}

The code which access the packed structs may be less efficient and longer.

Upvotes: 0

Alexander James Pane
Alexander James Pane

Reputation: 648

When creating a struct, all variables occupy the same amount of space (32 bits), the remaining unused bits are padding. So even if you define a char in the struct, this will occupy 4 bytes. This is due to the fact that your processor addresses data at 32 bits, even if afterwards less bits are used. The memory on the other side stores 1 byte for each address, but when data is fetched by the CPU, data will be adapted to the bus architecture (that depends on the processor).

Also note that the offset depends on the pointer you are using. a char* in this case will increase by 1, while a int* by 4.

This also means that the code is not portable, since, for example, int may not be defined of the same size on different architectures.

Upvotes: -1

haccks
haccks

Reputation: 106032

That's because of the padding bytes added to the structure. Three padded bytes will be added after char second;, this is because the char is followed by an int (member with larger alignment) so padding bytes will be inserted to make the alignment multiple of the alignment of larger member.

Upvotes: 1

kiran Biradar
kiran Biradar

Reputation: 12732

It is because of structure padding.

After padding your structure will look like below.

struct st
{
  int i;
  char c1;
  char padding[3]; // for alignment of j.
  int j;
  char c2; 
  char padding[3]; // for alignment of structure.
};

Hence

first = *((int*)pa);
  second = *((char*)pa + 4); /* offset = 4 bytes = sizeof(int) */
  third = *((int*)pa + 2); /* offset = 8 bytes(pointer arithmetic) to point to int j*/
  fourth = *((char*)pa + 12); /* offset = 12 bytes to point to char c2*/

For more info on structure padding read Data_structure_alignment

Upvotes: 1

Related Questions