C语言基础:结构体对齐规则与0字节数组
- 不同的编译器和系统默认的对齐规则会有差异,这里我使用的win32的MinGW
- C语言结构体一般是默认四字节对其的。
结构体对其规则
一般的,C语言结构体默认是以4字节对其方式,以此默认4字节为依据,结构体对其规则有以下三项:
- 规则一:struct内的第一个成员在偏移地址0处,随后成员的偏移地址在其本身类型大小整数倍处
- 规则二:struct的总大小为内部最大成员类型的整数倍
- 规则三:当A结构内含有结构B时,B在A中的偏移地址为B结构内最大元素类型的大小的整数倍
示例1:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define debug_printf(value) printf(#value " ---==> %d\n", value)
#define struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))
typedef struct
{
uint8_t a; // 偏移地址0
uint32_t b; // 偏移地址4
uint8_t c; // 偏移地址8
uint8_t d; // 偏移地址9
}test_t;
int main()
{
int a = struct_member_offset(test_t, a);
int b = struct_member_offset(test_t, b);
int c = struct_member_offset(test_t, c);
int d = struct_member_offset(test_t, d);
debug_printf(sizeof(test_t));
debug_printf(a);
debug_printf(b);
debug_printf(c);
debug_printf(d);
return 0;
}
输出结果:
解析:
- a为第一个成员在偏移地址0处
- b成员类型为无符号整形占用四字节,根据规则一b要对齐到4字节地址处,所以偏移地址为4,相当于在a成员后面补齐了3字节,不过这补齐的3个字节没用到
- c和d成员大小都为1字节,根据规则一要对齐到1字节,所以c偏移为9,d偏移为10
- 四个成员目前所占用的空间加起来是10字节,但是!此时还没完!根据规则二结构体的总大小为结构内最大成员的整数倍,test_t这个结构内最大成员是b占用4字节,所以10字节还要补齐2字节去对齐4字节,所以sizeof(test_t)=12。
示例二:
现在将d成员改为uint64类型:
typedef struct
{
uint8_t a;
uint32_t b;
uint8_t c;
uint64_t d;
}test_t;
输出结果:
还是根据规则解析:
- a在偏移地址0处
- b在4处
- c在8处
- abc所占用的空间为9个字节,d为uint64类型占8个字节,所以要对齐到8字节处,也就是偏移地址16处,相当于c成员后补齐了7字节。
- 目前四个成员所占用的空间为24字节,24是d类型的倍数大小,所以sizeof(test_t)=24
示例三:
现在将结构改为如下,多了一个数据
typedef struct
{
uint8_t a;
uint32_t b;
uint8_t c[3];
uint8_t d;
uint64_t e;
}test_t;
输出结果:
c成员为3字节,占用偏移地址8,9,10三个空间,所以总大小还是24。
示例四:
将结构改为如下,现在test_t结构内包含结构sub_t。
typedef struct
{
uint8_t sub_a;
uint32_t sub_b;
}sub_t;
typedef struct
{
uint8_t a;
uint32_t b;
sub_t c;
uint8_t d;
uint64_t e;
}test_t;
输出结果:
解析:
- 根据规则一和二解析sizeof(sub_t)=8字节
- 根据规则三确定sub_t在test_t中的偏移地址肯定是4的整数倍处,所以c这个结构成员偏移地址为8
- d偏移地址为17
- e偏移地址为24
- sizeof(test_t)=32
字节为0的数组与结构体
先看一下0字节数组的大小:
int main()
{
int array[0];
debug_printf(sizeof(array));
return 0;
}
输出结果:
sizeof(array) ---==> 0
说明0字节数据占用空间为0,那么看下面这个例子:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define debug_printf(value) printf(#value " ---==> %d\n", value)
#define struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))
typedef struct
{
uint8_t a;
uint32_t b;
uint8_t c;
uint64_t d[0];
}test_t;
int main()
{
int a = struct_member_offset(test_t, a);
int b = struct_member_offset(test_t, b);
int c = struct_member_offset(test_t, c);
int d = struct_member_offset(test_t, d);
debug_printf(sizeof(test_t));
debug_printf(a);
debug_printf(b);
debug_printf(c);
debug_printf(d);
return 0;
}
输出结果:
!!奇怪的问题出现了!!
但道理说d成员占用空间为0,sizeof(test_t)应该是12才对,但是sizeof(test_t)却是16!!
原因:d成员虽然占用空间为0,但是他是uint64类型的,结构体的总大小是按照内部最大成员进行对齐的!test_t对齐到了8字节,所以sizeof(test_t)的大小为16字节!
ends…