在64位机器上的内存中的结构表示

时间:2013-05-15 13:33:05

标签: c

为了我的好奇心,我写了一个程序来显示我的结构的每个字节。这是代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>

#define MAX_INT 2147483647
#define MAX_LONG 9223372036854775807

typedef struct _serialize_test{
   char a;
   unsigned int b;
   char ab;
   unsigned long long int c;
}serialize_test_t;


int main(int argc, char**argv){
   serialize_test_t *t;
   t = malloc(sizeof(serialize_test_t));
   t->a = 'A';
   t->ab = 'N';
   t->b = MAX_INT;
   t->c = MAX_LONG;

   printf("%x %x %x %x %d %d\n", t->a, t->b, t->ab, t->c, sizeof(serialize_test_t), sizeof(unsigned long long int));

   char *ptr = (char *)t;

   int i;
   for (i=0; i < sizeof(serialize_test_t) - 1; i++){
      printf("%x = %x\n", ptr + i, *(ptr + i));
   }

   return 0;
}

这是输出:

41 7fffffff 4e ffffffff 24 8
26b2010 = 41
26b2011 = 0
26b2012 = 0
26b2013 = 0
26b2014 = ffffffff
26b2015 = ffffffff
26b2016 = ffffffff
26b2017 = 7f
26b2018 = 4e
26b2019 = 0
26b201a = 0
26b201b = 0
26b201c = 0
26b201d = 0
26b201e = 0
26b201f = 0
26b2020 = ffffffff
26b2021 = ffffffff
26b2022 = ffffffff
26b2023 = ffffffff
26b2024 = ffffffff
26b2025 = ffffffff
26b2026 = ffffffff

以下是问题: 如果sizeof(long long int) is 8,那么为什么sizeof(serialize_test_t) is 24而不是32 - 我总是认为struct的大小被舍入到最大类型并乘以像这里的字段数例如:8(字节)* 4(字段) = 32(字节) - 默认情况下,没有pragma pack指令?

当我将该结构转换为char *时,我可以从输出中看到内存中值之间的偏移量不是8个字节。你能给我一个线索吗?或者这只是一些编译器优化?

5 个答案:

答案 0 :(得分:4)

在现代32位机器上,如SPARC或英特尔[34] 86,或68020以上的任何摩托罗拉芯片,每个数据通常必须是“自对齐”,从一个地址开始多种类型的大小。因此,32位类型必须从32位边界开始,16位边界上的16位类型,8位类型可以从任何地方开始,struct / array / union类型具有其最严格成员的对齐。 / p>

结构的总大小将取决于打包。在你的情况下,它将为8字节,因此最终结构将如下所示

typedef struct _serialize_test{

   char a;//size 1 byte

   padding for 3 Byte;

   unsigned int b;//size 4 Byte

   char ab;//size 1 Byte again

   padding of 7 byte;

   unsigned long long int c;//size 8 byte

}serialize_test_t;

以这种方式,前两个和后两个正确对齐,总大小达到24。

答案 1 :(得分:2)

取决于编译器选择的对齐方式。但是,您可以合理地期望以下默认值:

typedef struct _serialize_test{
   char a;                       // Requires 1-byte alignment
   unsigned int b;               // Requires 4-byte alignment
   char ab;                      // Requires 1-byte alignment
   unsigned long long int c;     // Requires 4- or 8-byte alignment, depending on native register size
}serialize_test_t;

鉴于上述要求,第一个字段将位于偏移零点。

字段b将从偏移量4开始(在3个字节填充之后)。

下一个字段从偏移量8开始(不需要填充)。

下一个字段从偏移12(32位)或16(64位)开始(在另外3或7个字节填充之后)。

这使您的总大小为20或24,具体取决于您平台上long long的对齐要求。

GCC有一个offsetof函数可用于识别任何特定成员的偏移量,或者您可以自己定义一个:

// modulo errors in parentheses...
#define offsetof(TYPE,MEMBER) (int)((char *)&((TYPE *)0)->MEMBER - (char *)((TYPE *)0))

它基本上使用地址差来计算偏移量,使用虚拟基本地址作为聚合类型。

答案 2 :(得分:0)

通常会添加填充,以便结构是字大小的倍数(在本例中为8)

所以前两个字段在一个8字节的块中。第三个字段位于另一个8字节块中,最后一个字段位于一个8字节块中。总共24个字节。

char 
padding
padding
padding
unsigned int
unsigned int
unsigned int
unsigned int
char                            // Word Boundary
padding
padding
padding
padding
padding
padding
padding
unsigned long long int           // Word Boundary
unsigned long long int
unsigned long long int
unsigned long long int
unsigned long long int
unsigned long long int
unsigned long long int
unsigned long long int

答案 3 :(得分:0)

与对齐有关。

结构的大小未舍入为最大类型并乘以字段。字节按各自的类型对齐: http://en.wikipedia.org/wiki/Data_structure_alignment#Architectures

对齐的工作原理是类型必须出现在内存地址中,该内存地址是其大小的倍数,因此:

Char是1字节对齐的,因此它可以出现在内存中的任意位置(1)的任意位置。

unsigned int需要从4的倍数开始。

char可以在任何地方。

然后长期需要是8的倍数。

如果您查看地址,就是这种情况。

答案 4 :(得分:0)

编译器只关心结构成员的个别对齐,一个接一个。它没有考虑结构整体。因为在二进制级别上不存在结构,只是在特定地址偏移处分配的一大块单个变量。没有“struct round-up”这样的东西,只要所有结构成员都正确对齐,编译器就不会关心大型struct的大小。

C标准没有提到填充的方式,除了不允许编译器在结构的最开头添加填充字节。除此之外,编译器可以在struct中的任何地方自由添加任意数量的填充字节。它可以是999个填充字节,它仍然符合标准。

所以编译器遍历结构并看到:这里是一个char,它需要对齐。在这种情况下,CPU可能可以处理32位访问,即4字节对齐。因为它只增加了3个填充字节。

接下来它会发现一个32位的int,不需要对齐,它保持原样。然后是另一个char,3个填充字节,然后是64位int,不需要对齐。