6

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

#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 - 我一直认为结构的大小被舍入到最大类型并乘以字段数,例如这里:8(字节)* 4(字段)= 32(字节) — 默认情况下,没有编译指示包指令?

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

4

5 回答 5

4

在现代的 32 位机器上,如 SPARC 或 Intel [34]86,或 68020 以上的任何摩托罗拉芯片,每个数据项通常必须是“自对齐”的,从一个为其倍数的地址开始类型大小。因此,32 位类型必须在 32 位边界上开始,16 位类型在 16 位边界上开始,8 位类型可以从任何地方开始,结构/数组/联合类型具有它们最严格的成员的对齐方式。

结构的总大小将取决于包装。在您的情况下,它将为 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。

于 2013-05-15T13:57:55.410 回答
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 个字节填充之后)。

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

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

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

它基本上使用聚合类型的虚构基地址使用地址差异来计算偏移量。

于 2013-05-15T13:53:48.083 回答
0

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

所以前 2 个字段在一个 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
于 2013-05-15T13:42:43.127 回答
0

与对齐有关。

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

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

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

unsigned int 需要从一个 4 的倍数的地址开始。

char 可以在任何地方。

然后 long long 需要是 8 的倍数。

如果你看一下地址,情况就是这样。

于 2013-05-15T13:45:21.953 回答
0

编译器只关心结构成员的个别对齐,一一对应。它没有考虑整个结构。因为在二进制级别上不存在结构,只是在某个地址偏移处分配的一大块单独的变量。没有“结构汇总”之类的东西,只要所有结构成员都正确对齐,编译器就不会关心结构有多大。

C 标准没有说明填充的方式,除了不允许编译器在结构的最开始添加填充字节。除此之外,编译器可以自由地在结构的任何位置添加任意数量的填充字节。它可以有 999 个填充字节,并且仍然符合标准。

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

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

于 2013-05-15T14:21:44.360 回答