64ä½æœºå™¨ä¸Šçš„结构填充

时间:2016-08-05 11:27:14

标签: c++ c structure padding

struct A
{
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint32_t var4;
    uint32_t var5;
};

在上é¢çš„结构中,编译器ä¸ä¼šå¡«å……并分é…20个字节。

现在我们有å¦ä¸€ä¸ªç»“构,其中包å«ä¸€ä¸ª8字节å˜é‡è€Œä¸æ˜¯ä¸¤ä¸ª4字节。在这ç§æƒ…况下,编译器填充并为此结构分é…24个字节。

struct B
{
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint64_t var5;
};

为什么会出现这ç§è¡Œä¸ºï¼Ÿ 如果编译器将数æ®å¯¹é½åˆ°8字节边界,那么第一个结构中应该有4个字节的填充 在这ç§æƒ…况下ä¸å¡«å……第二结构。如果编译器将数æ®å¯¹é½åˆ°4字节边界,那么为什么在第二个结构中有4个字节的填充?

编译器:GCC å¹³å°ï¼š64ä½linux,x86_64

6 个答案:

答案 0 :(得分:7)

对é½è§„则(在x86å’Œx86_64上)通常用于对é½å˜é‡å¤§å°ã€‚

æ¢å¥è¯è¯´ï¼Œ32ä½å˜é‡åœ¨4个字节上对é½ï¼Œ8ä½å­—节上的64ä½å˜é‡ç­‰ç­‰ã€‚

在第二ç§æƒ…况下,在

之间添加了4个字节的填充
uint32_t var3;
uint64_t var5;

让var5在8个字节上对é½ã€‚

由于这个原因,最好从大到å°è®¢è´­æ•°æ®æˆå‘˜ï¼ˆä½†ç”±äºŽæ•°æ®ä½ç½®ï¼Œå¯è¯»æ€§ç­‰åŽŸå› ï¼Œè¿™å¹¶ä¸æ˜¯é‚£ä¹ˆç®€å•ã€‚)

答案 1 :(得分:1)

首先,结构对é½ä¸æ˜¯ä¸€é—¨ç²¾ç¡®çš„科学,å¯èƒ½ä¾èµ–于架构和编译器。

在许多情况下,所有结构æˆå‘˜éƒ½æ ¹æ®æœ€å¤§å˜é‡ï¼ˆä»¥å­—节为å•ä½ï¼‰å¡«å……。在您的第一个结构上,所有å˜é‡éƒ½æ˜¯uint32_t,长度为4个字节。然åŽï¼Œæ‚¨çš„结构大å°ç­‰äºŽsizeof(uint32_t) * 5 = 4 * 5 = 20。

在你的第二个结构上,最大的元素是uint64_t,其大å°ä¸º8个字节。所以所有元素都将按8个字节填充。

å‰ä¸¤ä¸ªuint32_t被填充在一起,但第三个ä¸èƒ½æ­£ç¡®å¡«å……:如果用下一个整数填充,uint64_t将被分æˆä¸¤ä¸ªï¼å› æ­¤ï¼Œç¼–译器决定让它uint32_t自己使用,以é¿å…拆分uint64_t。

以下是您的结构示例以åŠæ‰€æœ‰å˜é‡çš„地å€ï¼š

struct A
{
  uint32_t var1;   /* ..00 */
  uint32_t var2;   /* ..04 */
  uint32_t var3;   /* ..08 */
  uint32_t var4;   /* ..12 */
  uint32_t var5;   /* ..16 */
};

struct B
{
  uint32_t var1;   /* ..00 */
  uint32_t var2;   /* ..04 */
  uint32_t var3;   /* ..08 */
  uint64_t var5;   /* ..16 */
};

答案 2 :(得分:1)

#include <stdio.h>

typedef struct __atribute__((packed)) A {
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint32_t var4;
    uint32_t var5;
} A ;


typedef struct __atribute__((packed)) B {
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint64_t var4;
} B;


int main()
{
    printf("sizeof(A): {%d} sizeof(B): {%d}", sizeof(A), sizeof(B));

    return 0;
}

试试这个,它对我有用

答案 3 :(得分:0)

虽然编译器å¯ä»¥æ ¹æ®éœ€è¦è‡ªç”±å¡«å……或ä¸å¡«å……,但通常它们会将å˜é‡å¯¹é½åœ¨å¯å˜å¤§å°çš„å€æ•°çš„边界上。

在struct的情况下,填充基于最大原始元素的大å°ã€‚对于第二个struct B,var5类型为uint64_t。

带有éšå¼å¡«å……çš„struct B布局如下:

struct B
{
    uint32_t var1;      // offset 0
    uint32_t var2;      // offset 4
    uint32_t var3;      // offset 8
    uint32_t padding;   // offset 12
    uint64_t var5;      // offset 16
};

如果var5紧跟var3,它将在字节å移12处,这ä¸æ˜¯8çš„å€æ•°ã€‚å› æ­¤var3之åŽéœ€è¦æœ‰4个字节的填充å…许var5正确对é½ã€‚

在struct A的情况下,所有字段的大å°éƒ½æ˜¯4个字节,因此ä¸éœ€è¦å¡«å……。如果您创建了此类型的数组,例如struct A a[5],a[1]将在a[0]之åŽä¸º20个字节,a[2]将在a[1]之åŽä¸º20个字节,并且等等。å‘struct A添加填充会浪费空间,因为所有å­å­—段ä»ç„¶åœ¨æ‰€éœ€çš„4字节边界上对é½ã€‚

答案 4 :(得分:0)

struct B中的填充几乎肯定ä¸åœ¨æœ€åŽï¼Œä½†åœ¨ç¬¬ä¸‰ä¸ª32ä½æˆå‘˜ä¹‹åŽï¼š

struct B
{
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    // padding here
    uint64_t var5;
};

这是因为var1到var3最多加24个字节,ä¸èƒ½è¢«8整除。你的编译器想è¦ä¸€ä¸ª8字节整数类型在一个å¯è¢«8整除的地å€ä¸Šã€‚

在这ç§æƒ…况下,您还将在结构的末尾获得填充:

struct C
{
   uint64_t memb1;
   uint32_t memb2;
   // Padding here
};

该填充是为memb1数组struct C中的struct C c_array[13]; 对é½è€Œè®¾çš„:

c_array[0].memb1

当然c_array[1].memb1是对é½çš„,因为它ä½äºŽæ•°ç»„的基å€ã€‚但是 Dim bm As New Bitmap(NomeFile) 呢?如果没有结构中的填充,它就ä¸ä¼šå¯¹é½ã€‚

C的定义方å¼æ˜¯ä¸èƒ½åœ¨æ•°ç»„元素之间添加填充;数组的元素紧密分é…。因此,如果需è¦å¡«å……,则必须将其拧入元件类型中。结构的布局必须考虑å¯èƒ½çš„数组èšåˆã€‚

答案 5 :(得分:0)

虽然我肯定有例外,但是对于结构中的字段以åŠæ•´ä¸ªç»“构,编译器通常会æ’入足够的填充以满足对é½è¦æ±‚。ä¸å…许C编译器对结构中的字段é‡æ–°æŽ’åºã€‚

ä¸åŒçš„类型å¯èƒ½å…·æœ‰ä¸åŒçš„对é½è¦æ±‚,类型的大å°å¿…须是其对é½è¦æ±‚çš„å€æ•°ã€‚在大多数64ä½ç³»ç»Ÿä¸Šï¼Œæ ‡å‡†C原语类型的对é½è¦æ±‚等于其大å°ã€‚通常,结构的对é½è¦æ±‚等于其æˆå‘˜çš„最高对é½è¦æ±‚。

有时必须填充结构以确ä¿å…¶æˆå‘˜æ»¡è¶³å¯¹é½è¦æ±‚,并且结构的整体大å°æ˜¯å…¶å¯¹é½è¦æ±‚çš„å€æ•°ã€‚

因此,让我们看一下您的结构以åŠç¬¬ä¸‰ä¸ªç»“构。

struct A
{
    uint32_t var1; //size 4, alignment 4, offset 0
    uint32_t var2; //size 4, alignment 4, offset 4
    uint32_t var3; //size 4, alignment 4, offset 8
    uint32_t var4; //size 4, alignment 4, offset 12
    uint32_t var5; //size 4, alignment 4, offset 16
};

所有字段的类型å‡ä¸ºuint32_t,其大å°ä¸º4,对é½æ–¹å¼ä¸º4。因此,无需填充,该结构的总体大å°ä¸º20字节,总体对é½æ–¹å¼ä¸º4个字节。

struct B
{
    uint32_t var1; //size 4, alignment 4, offset 0
    uint32_t var2; //size 4, alignment 4, offset 4
    uint32_t var3; //size 4, alignment 4, offset 8
    //4 bytes of padding.
    uint64_t var5; //size 8, alignment 8, offset 16
};

å‰ä¸‰ä¸ªå­—段的大å°å’Œå¯¹é½æ–¹å¼ä¸º4,因此å¯ä»¥åˆ†é…它们而无需填充。但是var5的大å°å’Œå¯¹é½æ–¹å¼ä¸º8,因此无法在å移é‡12处分é…。必须æ’入填充,而var5分é…在å移é‡16处。整个结构的大å°ä¸º24,对é½æ–¹å¼ä¸º8。 >

struct C
{
    uint32_t var1; //size 4, alignment 4, offset 0
    uint32_t var2; //size 4, alignment 4, offset 4
    uint64_t var3; //size 8, alignment 8, offset 8
    uint32_t var5; //size 4, alignment 4, offset 16
    //4 bytes of padding
};

在这ç§æƒ…况下,å¯ä»¥åœ¨ä¸æ’入填充的情况下将所有å˜é‡åˆ†é…ç»™åˆé€‚çš„å移é‡ã€‚但是,结构的总大å°å¿…须是其对é½è¦æ±‚çš„å€æ•°ï¼ˆå¦åˆ™æ•°ç»„将破å对é½ï¼‰ï¼Œå› æ­¤å¿…须填充结构的末端。åŒæ ·ï¼Œæ•´ä¸ªç»“构的大å°ä¸º24,对é½æ–¹å¼ä¸º8。

æŸäº›ç¼–译器具有é‡å†™ç»“构的正常打包的机制,例如,å¦ä¸€ä¸ªç­”案中æ到的__attribute__((packed))。但是,必须谨慎使用这些功能,因为它们很容易导致对é½å†²çªã€‚