You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

原因分析

  1. 枚举类型的内存大小:在C语言中,枚举类型enum的默认底层类型是int(通常为4字节,具体取决于编译器和平台),并非1字节或2字节。
  2. 结构体内存对齐:编译器会为结构体成员做内存对齐,保证每个成员的地址符合其类型的对齐要求。第一种结构体的内存布局如下:
    • Curr_Bar:占用4字节(地址0-3)
    • Pres_Value:uint16_t占2字节(地址4-5),为满足后续4字节对齐要求,编译器会在Pres_Value后填充2字节(地址6-7)
    • Prev_Value:占用2字节(地址8-9)
  3. 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的格式符与目标变量类型不匹配**,结合结构体内存对齐规则,导致内存越界写入,破坏了后续成员的数值。

修正方法

有两种规范的修正方式:

  1. 使用匹配的格式符:对于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);
    
  2. 统一变量类型:如果业务允许,将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 17:20:27