Is there a way to access members of a struct

235 views Asked by At

I want to be able to find the size of the individual members in a struct. For example

struct A {
    int  a0;
    char a1;
}

Now sizeof(A) is 8, but let's assume I am writing a function that will print the alignment of A as shown below where "aa" represents the padding.

data A:
0x00: 00 00 00 00
0x04: 00 aa aa aa
*-------------------------
size: 8 padding: 3

In order for me to calculate padding, I need to know the size of each individual members of a struct. So my question is how can I access to individual members of a given struct.

Also, let me know if there is another way to find the number of padding.

4

There are 4 answers

1
Peter On

A simple approach would be to use sizeof operator (exploiting the fact that it does not evaluate its operand, only determines the size of the type that would result if it was evaluated) and the offsetof() macro (from <cstddef>).

For example;

#include <iostream>
#include <cstddef>

struct A
{
    int  a0;
    char a1;
};

int main()
{
    // first calculate sizes
    size_t size_A = sizeof(A);
    size_t size_a0 = sizeof(((A *)nullptr)->a0);    // sizeof will not dereference null
    size_t size_a1 = sizeof(((A *)nullptr)->a1);

    //   calculate positions

    size_t pos_a0 = offsetof(A, a0);    //  will be zero, but calculate it anyway
    size_t pos_a1 = offsetof(A, a1);

    // now calculate padding amounts
    size_t padding_a0 = pos_a1 - pos_a0 - size_a0;    // padding between a0 and a1 members
    size_t padding_a1 = size_A - pos_a1 - size_a1;

    std::cout << "Data A:\n";
    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << pos_a0;
    size_t i = pos_a0;
    while (i < pos_a0 + size_a0)      // print out zeros for bytes of a0 member
    {
        std::cout << " 00";
        ++i;
    }

    while (i < pos_a1)      //  print out aa for each padding byte after a_0
    {
        std::cout << " aa";
        ++i;
    }
    std::cout << std::endl;

    std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << pos_a1;

    while (i < pos_a1 + size_a1)      // print out zeros for bytes of a1 member
    {
        std::cout << " 00";
        ++i;
    }

    while (i < size_A)      //  print out aa for each padding byte after a_1
    {
        std::cout << " aa";
        ++i;
    }
    std::cout << std::endl;
    std::cout << "size: " << size_A << " padding: " << padding_a0 + padding_a1 << std::endl;


}
0
Elliott On

As other answers have said, the offsetof macro is clearly the best solution here, but just to demonstrate that you could find the positions of your members at run time by looking at the pointers:

#include <iostream>

struct A
{
    char x;
    int y;
    char z;
};

template <typename T>
void PrintSize ()
{
    std::cout << "    size = " << sizeof(T) << std::endl;
}

void PrintPosition (char * ptr_mem, char * ptr_base)
{
    std::cout << "    position = " << ptr_mem - ptr_base << std::endl;
}

template <typename T>
void PrintDetails (char member, T * ptr_mem, A * ptr_base)
{
    std::cout << member << ":" << std::endl;

    PrintSize<T>();
    PrintPosition((char*) ptr_mem, (char*) ptr_base);
    
    std::cout << std::endl;
}

int main()
{
    A a;
    
    PrintDetails('x', &a.x, &a);
    PrintDetails('y', &a.y, &a);
    PrintDetails('z', &a.z, &a);
}

Output on my machine:

x:
size = 1
position = 0

y:
size = 4
position = 4

z:
size = 1
position = 8

(Surprisingly, on my intel, with gcc/clang, A is of size 12! I thought that the compiler did a better job of rearranging elements)

3
Kaz On

To calculate the padding of a structure, you need to know the offset of the last member, and the size:

Concisely, if type T has a member last which is of type U, the padding size is:

sizeof(T) - (offsetof(T, last) + sizeof(U))

To calculate the total amount of padding in a structure, if that is what this question is about, I would use a GCC extension: declare the same structure twice (perhaps with the help of a macro), once without the packed attribute and once with. Then subtract their sizes.

Here is a complete, working sample:

#include <stdio.h>
  
#define X struct { char a; int b; char c; }

int main(void)
{
  printf("%zd\n", sizeof(X) - sizeof(X __attribute__((packed))));
  return 0;
}

For the above structure, it outputs 6. This corresponds to the 3 bytes of padding after a necessary for the four-byte alignment of b and at the end of the structure, necessary for the alignment of b if the structure is used as an array member.

The packed attribute defeats all padding, and so the difference between the packed and unpacked structure gives us the total amount of padding.

0
D-RAJ On

You can work this out if you know the content of the struct. Passing usually works like this, Assume that your struct is this.

struct A {
    int  a0;    // 32 bits (4 bytes)
    char a1;    // 8 bits (1 byte)
};

But this is not memory efficient as if you pack these structs in memory, you might get some fragmentation issues thus making the application slower. So the compiler optimizes the struct like this and the final struct to the compiler would look something like this.

struct A {
    int  a0;
    char a1;
    
    // padding
    char __padd[3]; // 3 * 1 byte = 3 bytes.
    /// Adding this padding makes it 32 bit aligned. 
};

Now using this knowledge, you can see why its not easy to get the padding of an object without knowing the content of it. And paddings aren't always placed at the end of the object. For example,

struct Obj {
    int a = 0;
    char c = 'b';
    // Padding is set here by the compiler. It may look something like this: char __padd[3];
    int b = 10;
};

So how to get the padding of the struct?
You can use something called Reflection to get the content of the struct at runtime. Then workout the sizes of the data types in the struct and then you can calculate the padding by deducting the size of the previous type and the next type which gives you how the padding would look like.