Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

My question is based on the third case presented in this page:

https://www.geeksforgeeks.org/is-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member/

// C program to illustrate 
// size of struct 
#include <stdio.h> 

int main() 
{ 

    struct C { 
        // sizeof(double) = 8 
        double z; 

        // sizeof(short int) = 2 
        short int y; 
        // Padding of 2 bytes 

        // sizeof(int) = 4 
        int x; 
    }; 

    printf("Size of struct: %ld", sizeof(struct C)); 

    return 0; 
} 

Why does it require a padding after y, instead of having a padding at the end (after x)?

I can see why it's needed on the cases 1st and 2nd, but I fail to see it on the 3rd.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
135 views
Welcome To Ask or Share your Answers For Others

1 Answer

In some computer architectures, instructions that access values in memory will only accept a subset of all addresses due to alignment restrictions. For example, an instruction that copies a 32-bit value from memory into a register might require the value to be at an address that's divisible by 4. (You might still be able to obtain the value byte-by-byte, but that would far slower as it would require multiple instructions). Other architectures might merely perform better if the value are aligned properly. And in yet other architectures, it might not matter at all.

As such, the C standard allows for implementation-specific padding to be used in structures. By adding padding, the compiler can assure that each member will be properly aligned (since it can enforce an alignment on the structure itself). This allows us to declare the following and let the compiler figure out the exact size and offsets:

struct A { 
    int x; 
    short y; 
    double z;
};

Let's look at what a compiler might do.

Let's say your system uses 2 bytes for short values, 4 bytes for int values and 8 bytes for double values. And let's say values of size N are required to be placed at an address evenly divisible by N.

struct A { 
    int x;      // 4 bytes, address must be divisible by 4.
    double z;   // 8 bytes, address must be divisible by 8.
    short y;    // 2 bytes, address must be divisible by 2.
};

If we just put the members end to end, z would be found at offset 4, which isn't divisible by 8, so the computer would be unable to access this field efficiently. The compiler might therefore utilize padding.

struct A { 
    int x;      // 4 bytes, address must be divisible by 4.   // At offset  0.
                // 4 bytes of padding.                        // At offset  4.
    double z;   // 8 bytes, address must be divisible by 8.   // At offset  8.
    short y;    // 2 bytes, address must be divisible by 2.   // At offset 16.
};

Now, z is at offset 8, which is divisible by 8.


But that's not quite it.

The alignment restrictions are imposed on the absolute address of the members, not merely their offset. So the members of struct C are only properly aligned if the address of the structure itself is at an address evenly divisible by 8. The compiler can take care of that when you do

struct A a;

But what if you do

struct A *array = malloc(sizeof(struct A) * n);

malloc will return a pointer that meets all possible alignment restrictions, so array[0] will be properly aligned, but what about array[1]? For that to be properly aligned, sizeof(struct A) needs to be a multiple of 8! So padding will be added to the end to make the size of the structure a multiple of 8, and we end up with this:

// Address must be divisible by 8, so sizeof(struct A) must be divisible by 8.
struct A { 
    int x;      // 4 bytes, address must be divisible by 4.   // At offset  0.
                // 4 bytes of padding.                        // At offset  4.
    double z;   // 8 bytes, address must be divisible by 8.   // At offset  8.
    short y;    // 2 bytes, address must be divisible by 2.   // At offset 16.
                // 2 bytes of padding.                        // At offset 18.
};

Finally, you asked about struct C. Applying the above, we get:

// Address must be divisible by 8, so sizeof(struct C) must be divisible by 8.
struct C { 
    double z;   // 8 bytes, address must be divisible by 8.   // At offset  0.
    short y;    // 2 bytes, address must be divisible by 2.   // At offset  8.
                // 2 bytes of padding.                        // At offset 10.
    int x;      // 4 bytes, address must be divisible by 4.   // At offset 12.
                // 0 bytes of padding.                        // At offset 16.
};

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...