C语言结构体成员顺序导致Prev_Value恒为0的问题咨询
C语言结构体成员访问异常问题分析与解决
问题现象
定义如下结构体时,为三个成员赋值后,访问Prev_Value始终返回0:
typedef struct { Fuel_Intrl_IDs Curr_Bar; /*enum variable*/ uint16_t Pres_Value; uint16_t Prev_Value; }Bar_Dync_Data;
但将结构体成员顺序调整为以下形式后,功能恢复正常:
typedef struct { uint16_t Pres_Value; Fuel_Intrl_IDs Curr_Bar; /*enum variable*/ uint16_t Prev_Value; }Bar_Dync_Data;
复现代码:
#include <stdio.h> #include <stdlib.h> #include <stdint-gcc.h> typedef enum /*Fuel Internal IDs*/ { Fuel_Intrl_ID_End }Fuel_Intrl_IDs; typedef struct /*Structure for Storing BAR ON/OFF Values*/ { Fuel_Intrl_IDs Curr_Bar; uint16_t Pres_Value; uint16_t Prev_Value; }Bar_Dync_Data; Bar_Dync_Data FBar_Dync_Data; int main() { printf("Hello world!\n"); FBar_Dync_Data.Prev_Value = 255; while(1) { printf("\n Enter Pres value "); scanf("%d",&FBar_Dync_Data.Pres_Value); printf("\n Present value: %d",FBar_Dync_Data.Pres_Value); printf("\n Previous value: %d",FBar_Dync_Data.Prev_Value); FBar_Dync_Data.Prev_Value = FBar_Dync_Data.Pres_Value; // getch(); } return 0; }
原因分析
- 枚举类型的内存大小:在C语言中,枚举类型
enum的默认底层类型是int(通常为4字节,具体取决于编译器和平台),并非1字节或2字节。 - 结构体内存对齐:编译器会为结构体成员做内存对齐,保证每个成员的地址符合其类型的对齐要求。第一种结构体的内存布局如下:
Curr_Bar:占用4字节(地址0-3)Pres_Value:uint16_t占2字节(地址4-5),为满足后续4字节对齐要求,编译器会在Pres_Value后填充2字节(地址6-7)Prev_Value:占用2字节(地址8-9)
scanf格式符不匹配导致越界写入:代码中使用scanf("%d", &FBar_Dync_Data.Pres_Value),%d是读取int类型(4字节)的格式符,但Pres_Value是uint16_t(2字节)。执行scanf时会从Pres_Value的地址开始写入4字节数据,覆盖Pres_Value本身、后面的填充空间,甚至Prev_Value的前2字节,直接破坏Prev_Value的值,导致其始终显示为0。
调整成员顺序后,结构体布局变为:
Pres_Value:2字节(地址0-1)Curr_Bar:4字节(地址2-5,编译器在Pres_Value后填充2字节以满足int的4字节对齐)Prev_Value:2字节(地址6-7)
此时scanf("%d")写入的4字节只会覆盖Pres_Value和Curr_Bar的前2字节,不会影响Prev_Value,所以功能看似正常,但Curr_Bar的值已被破坏,属于潜在bug。
问题定位
核心问题是**scanf的格式符与目标变量类型不匹配**,结合结构体内存对齐规则,导致内存越界写入,破坏了后续成员的数值。
修正方法
有两种规范的修正方式:
- 使用匹配的格式符:对于
uint16_t类型,应使用%hu格式符(对应无符号短整型),或者包含<inttypes.h>头文件后使用标准宏PRIu16,确保格式符与类型严格匹配:// 方式1:用%hu scanf("%hu", &FBar_Dync_Data.Pres_Value); printf("\n Present value: %hu", FBar_Dync_Data.Pres_Value); // 方式2:用PRIu16(更规范,跨平台) #include <inttypes.h> scanf("%" PRIu16, &FBar_Dync_Data.Pres_Value); printf("\n Present value: %" PRIu16, FBar_Dync_Data.Pres_Value); - 统一变量类型:如果业务允许,将
Pres_Value和Prev_Value的类型改为int,与%d格式符匹配,同时避免内存对齐带来的潜在问题:typedef struct { Fuel_Intrl_IDs Curr_Bar; int Pres_Value; int Prev_Value; }Bar_Dync_Data;
不推荐使用__attribute__((packed))强制取消结构体对齐,这会降低内存访问性能,且属于编译器扩展,不具备跨平台兼容性。
内容的提问来源于stack exchange,提问作者naren ramasamy
相关产品推荐
相关产品推荐

