uint16_t数组赋值时内存地址异常变更致段错误问题求助
问题场景
尝试将uint16_t数组的所有值初始化为0时,处理到索引4出现异常:该位置初始垃圾值为58248,赋值为0后内存地址发生58248的偏移,随后触发段错误。其他类似场景无此问题。
代码实现
头文件声明
typedef struct my_struct { //various struct members } my_struct; typedef union my_struct_union { my_struct struct_instance; uint16_t struct_overlay[6]; } my_struct_union; extern my_struct_union *struct_pointer; extern my_struct struct_instance;
源文件代码
my_struct_union *struct_pointer; my_struct struct_instance; void init_struct() { struct_pointer = (my_struct_union *)&struct_instance; for(uint16_t indx = 0; indx < 6; indx++) { printf("indx: %d", indx); printf("address: %d", &struct_pointer->struct_overlay[indx]); printf("value: %d", struct_pointer->struct_overlay[indx]); struct_pointer->struct_overlay[indx] = 0; printf("address: %d", &struct_pointer->struct_overlay[indx]); printf("value: %d", struct_pointer->struct_overlay[indx]); } }
异常输出
indx: 3 address: 6415246 value: 0 address: 6415246 value: 0 indx: 4 address: 6415248 value 58248 address: 6357000
随后触发段错误。
排查思路
检查
my_struct的实际大小与布局:共用体大小由最大成员决定,struct_overlay是6个uint16_t(共12字节,假设uint16_t为2字节)。用sizeof(my_struct)确认结构体实际大小,如果小于12字节,那么struct_overlay[4]和struct_overlay[5]属于越界访问,会篡改结构体之外的内存。同时要考虑内存对齐,比如结构体成员是否有uint32_t、指针等类型,编译器会插入填充字节,需对齐计算实际大小。验证指针转换的合法性:将
my_struct*强制转为my_struct_union*需满足对齐要求。用_Alignof(my_struct)和_Alignof(my_struct_union)检查两者的对齐规则是否一致,如果共用体对齐要求更高,强制转换后访问成员会触发未定义行为。排查内存覆盖问题:
- 确认
struct_pointer本身是否被篡改:在循环中打印struct_pointer的地址,看其值是否在赋值struct_overlay[4]后发生变化。如果struct_overlay[4]的内存位置恰好覆盖了struct_pointer的存储地址,赋值0会直接修改指针的值,导致后续访问地址偏移。 - 检查是否有其他代码意外修改
struct_instance的内存区域,比如全局变量的内存重叠、栈溢出(如果是栈变量的话)。
- 确认
修正printf格式化错误:代码中用
%d打印内存地址是错误的,应该用%p并转换为void*类型,错误的格式化符可能导致地址显示异常,干扰排查。修正后的打印语句:printf("address: %p", (void*)&struct_pointer->struct_overlay[indx]);检查编译器优化与严格别名规则:
- 尝试关闭编译器优化(如
-O0)重新编译,看问题是否消失。激进优化可能对类型双关代码产生未定义行为。 - 确认是否违反严格别名规则:虽然共用体的方式理论上允许类型双关,但部分编译器的严格模式会对此报错,需检查编译警告信息。
- 尝试关闭编译器优化(如
确认内存越界的连锁影响:如果
struct_overlay[4]访问的是其他变量的内存,赋值0会修改该变量的值。如果该变量是指针或地址相关数据,会直接导致后续内存访问出错。可以打印&struct_pointer的地址,对比&struct_pointer->struct_overlay[4]的地址,看是否存在重叠。
内容的提问来源于stack exchange,提问作者mgrenier

