【日志记录】---编译器内存对齐优化导致结构体成员引用异常

一:问题现象

在一个跨线程数据处理消息的时候出现了以下内存错位现象,在结构体指针引用的时候出现了成员数据异常

1.【数据源】线程A消息里面赋值的数据

//字节流
message.data[0] = (unsigned char)model_brake_disable_type_read();
message.data[1] = (unsigned char)(temp & 0xff);
message.data[2] = (unsigned char)((temp >> 8) & 0xff);

2.【目标结构体】线程B知道线程A该条消息的结构体,定义了专门的结构体来套字节流中的数据 

typedef struct    //目标结构体
{
	unsigned char break_disable_type;
	unsigned short break_disable_time;
}break_disable_info_t;

3.【问题出现】线程B通过直接打印和结构体成员引用的方式分别输出内容,发现结构体第二个成员输出内容异常❌

//套结构体体指针,以成员的形式引用data内容
break_disable_info_t *disableFrame = (break_disable_info_t *)message->data;

printf("[pad] type%d\r\n", message->data[0]);    //输出1,正确
printf("[pad] time%d\r\n", (*((short*)(message->data+1))));    //输出60,正确

printf("[pad] type%d\r\n", disableFrame->break_disable_type);    //输出1,正确
printf("[pad] time%d\r\n", disableFrame->break_disable_time);    //输出0,错误

4.【反复调试】卡这里一直想不通,语法各方面都不存在问题,一直调试打印成员变量地址,结构体和成员大小发现了异常地方

发现两个结构体成员并不连续,中间出现了一个空位

进一步调试发现原本3个字节大小的结构体,在内存中占用了四个字节的位置,这正是导致输出数据异常的原因,多出的一个字节导致数据引用出现了错位

printf("[pad] p:%p, size:%d\r\n", disableFrame_p, sizeof(break_disable_info_t_));

二:原因解析

起初反复检查,查看之前代码也是这样处理的,没有任何问题,反复调试差点放弃打算换一种写法放过这个问题,最终还是比较犟,经过上面一顿懵逼和调试后,怀疑可能是编译器内存优化,找了一下资料得到一下结论:

在C语言中,struct的大小通常取决于其成员的类型、数量以及可能的内存对齐(padding)。当你定义了如下的结构体:

typedef struct  
{  
    char break_disable_type;  
    unsigned short break_disable_time;  
} break_disable_info_t_;

char类型通常占用1个字节,而unsigned short类型在大多数现代系统上占用2个字节。但是,由于数据对齐的需要,编译器可能会在char成员后面插入填充字节(padding),以确保unsigned short从适当的地址开始。

数据对齐是许多系统为了提高访问性能而采取的一种策略。对于某些处理器,从一个对齐的地址读取或写入数据比从一个未对齐的地址更快。此外,一些处理器甚至无法从未对齐的地址访问数据,或者这样做会导致硬件异常。

在你的例子中,char后面可能有一个填充字节,以确保unsigned short从2的倍数地址开始(这取决于编译器的具体实现和平台的对齐要求)。因此,整个结构体的大小可能是:

  • char:1字节
  • 填充(padding):1字节(为了对齐unsigned short
  • unsigned short:2字节

总共 4 字节。

你可以通过sizeof运算符来检查结构体的大小,如下所示:

#include <stdio.h>  
  
typedef struct  
{  
    char break_disable_type;  
    unsigned short break_disable_time;  
} break_disable_info_t_;  
  
int main() {  
    printf("Size of break_disable_info_t_: %zu bytes\n", sizeof(break_disable_info_t_));  
    return 0;  
}

如果你在输出中看到4,那么说明编译器确实在charunsigned short之间插入了一个填充字节。如果你想要避免这种填充(在某些情况下,比如内存使用非常关键时),你可以使用特定的编译器指令或属性来控制对齐,但这通常不是推荐的做法,除非你有明确的理由。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值