链接行为与结构体填充字节:该行为是否具有确定性?
结构体填充字节的确定性与初始化问题
源文件内容
src1.c
#include <stdio.h> typedef struct my_struct { char a; short b; int c; } my_struct; my_struct x; void add1(); int main() { x.a = 0; x.b = 0; x.c = 0; add1(); printf("%x, %x, %x\n", x.a, x.b, x.c); }
src2.c
#include <stdio.h> typedef struct my_struct { int c; short b; char a; } my_struct; extern my_struct x; void add1(){ x.a += 1; x.b += 1; x.c += 1; }
问题解答
1. 该行为是否具有确定性保证?
完全没有确定性保证。首先,两个源文件中对my_struct的定义不一致,这直接违反了C语言的一处定义规则(ODR)——同一个外部可见类型在整个程序中必须保持定义一致。这种情况下,程序行为属于未定义行为,编译器和链接器没有义务保证任何特定输出,甚至可能出现崩溃、输出乱码等任意结果。
退一步说,就算不考虑ODR违规,结构体的填充字节布局也不是C标准规定的,而是由具体编译器、目标平台的内存对齐规则决定的。不同编译器(比如GCC和MSVC)、不同架构(x86和ARM)的填充策略可能不同,甚至同一编译器的不同编译选项(比如优化级别、对齐参数)都可能改变填充的位置和大小,所以依赖填充字节的行为完全不可靠。
2. 填充字节通常会被初始化为0吗?
部分场景下会,但这不是标准强制的:
- 全局/静态存储期的变量(比如示例中的
x):如果没有显式初始化,C标准规定它们会被自动初始化为0值,包括填充字节在内。这也是当前示例中填充字节为0的原因。 - 自动存储期的变量(比如函数内的局部结构体变量):如果没有显式初始化,包括填充字节在内的所有内存都是未初始化的垃圾值。
3. 存在填充字节不会被初始化为0的情况吗?
当然存在,常见场景包括:
- 局部结构体变量:未显式初始化时,填充字节是栈上残留的垃圾值,不会自动置0。
- 动态分配的结构体:用
malloc分配的内存,填充字节属于未初始化的垃圾值;只有calloc分配的内存会被全部置0。 - 部分初始化的结构体:用初始化列表只初始化部分成员时,剩余成员(包括填充字节)的初始化规则遵循对应存储期的规则——全局变量会置0,局部变量则不会。
- 违反ODR的情况:像示例中这样,两个源文件定义不一致的结构体,链接后填充字节的行为完全是未定义的,可能出现任何值。
内容的提问来源于stack exchange,提问作者user20332975
相关产品推荐
相关产品推荐

