当前位置:首页 > 嵌入式 > 嵌入式分享
[导读]在嵌入式开发的日常中,sizeof()看似是最简单的运算符,然而当它作用于结构体时,结果往往与开发者直觉相去甚远。一个包含四个字节成员的结构体,sizeof的结果可能是12甚至16,而非预期的4。这种"膨胀"并非编译器的bug,而是内存对齐机制在后台默默工作的结果。理解这一机制,不仅关乎内存的精打细算,更直接影响MCU上结构体数组的布局、DMA传输的正确性以及通信协议的字节对齐。

在嵌入式开发的日常中,sizeof()看似是最简单的运算符,然而当它作用于结构体时,结果往往与开发者直觉相去甚远。一个包含四个字节成员的结构体,sizeof的结果可能是12甚至16,而非预期的4。这种"膨胀"并非编译器的bug,而是内存对齐机制在后台默默工作的结果。理解这一机制,不仅关乎内存的精打细算,更直接影响MCU上结构体数组的布局、DMA传输的正确性以及通信协议的字节对齐。

内存对齐的根源在于硬件架构。32位MCU的数据总线宽度为32位,CPU一次读取4字节最为高效。若一个4字节变量存放在地址0x00000001处,CPU需要两次总线周期才能拼凑完整数据,性能减半。为此,编译器强制要求:基本数据类型的起始地址必须是其自身大小的整数倍——char占1字节可放任意地址,short占2字节须从偶数地址开始,int和float占4字节须从4的倍数地址开始,double占8字节须从8的倍数地址开始。这条规则在结构体中同样生效,编译器会在成员之间插入"填充字节"以满足对齐要求。

结构体的总大小还须满足其最大成员对齐值的整数倍,这是为了保证结构体数组中每个元素都能正确对齐。以一个典型例子说明:包含char、int、char三个成员的结构体,char占1字节放在偏移0,int需4字节对齐故跳过偏移1至3插入3字节填充,第二个char放在偏移4,但结构体总大小须为4的倍数,故末尾再补3字节填充,最终sizeof为12而非6。这种隐性膨胀在成员众多时尤为惊人,一个包含十个成员的传感器数据结构体,若排列不当,填充量可能超过实际数据的两倍。

在STM32F103这类Cortex-M3内核上,默认对齐方式为4字节(#pragma pack(4)),但若切换为1字节对齐(#pragma pack(1)),同样的结构体可能缩小至6字节。然而1字节对齐虽节省空间,却会导致CPU访问未对齐地址时触发硬件异常或产生额外的对齐修正指令,反而降低运行速度。因此工程实践中,通常在Flash存储或通信协议中使用pack(1)消除填充,在运行时RAM中保持默认对齐以换取速度。

以下为针对STM32F103平台的完整实测对比程序,通过定义不同排列顺序的结构体并打印sizeof结果,直观展示成员顺序与对齐方式对结构体大小的影响:

#include "stm32f1xx_hal.h"

#include

#pragma pack(push, 1)

typedef struct {

char a;

int b;

char c;

} Packed_BadOrder_t;

typedef struct {

int b;

char a;

char c;

} Packed_GoodOrder_t;

#pragma pack(pop)

typedef struct {

char a;

int b;

char c;

} Aligned_BadOrder_t;

typedef struct {

int b;

char a;

char c;

} Aligned_GoodOrder_t;

typedef struct {

double d;

char a;

int b;

} Aligned_Mixed_t;

static void print_sizeof(const char *name, size_t size) {

printf("%-25s sizeof = %2u bytes\r\n", name, (unsigned)size);

}

static void print_offset(const char *name, void *ptr) {

printf("%-25s offsets: a=%u b=%u c=%u d=%u\r\n", name,

(unsigned)((char*)&((Aligned_BadOrder_t*)ptr)->a - (char*)ptr),

(unsigned)((char*)&((Aligned_BadOrder_t*)ptr)->b - (char*)ptr),

(unsigned)((char*)&((Aligned_BadOrder_t*)ptr)->c - (char*)ptr),

0);

}

int main(void) {

HAL_Init();

SystemClock_Config();

printf("=== Memory Alignment Test on STM32F103 ===\r\n\r\n");

printf("--- Packed (1-byte align) ---\r\n");

print_sizeof("Packed_BadOrder_t", sizeof(Packed_BadOrder_t));

print_sizeof("Packed_GoodOrder_t", sizeof(Packed_GoodOrder_t));

printf("\r\n--- Aligned (4-byte default) ---\r\n");

print_sizeof("Aligned_BadOrder_t", sizeof(Aligned_BadOrder_t));

print_sizeof("Aligned_GoodOrder_t", sizeof(Aligned_GoodOrder_t));

print_sizeof("Aligned_Mixed_t", sizeof(Aligned_Mixed_t));

printf("\r\n--- Offset analysis (Aligned_BadOrder) ---\r\n");

Aligned_BadOrder_t sample;

print_offset("Aligned_BadOrder_t", &sample);

printf("\r\n--- Offset analysis (Aligned_GoodOrder) ---\r\n");

print_offset("Aligned_GoodOrder_t", &sample);

printf("\r\n--- Memory waste comparison ---\r\n");

printf("Bad order aligned: data=6 waste=%u (%.1f%%)\r\n",

sizeof(Aligned_BadOrder_t) - 6,

(float)(sizeof(Aligned_BadOrder_t) - 6) / sizeof(Aligned_BadOrder_t) * 100);

printf("Good order aligned: data=6 waste=%u (%.1f%%)\r\n",

sizeof(Aligned_GoodOrder_t) - 6,

(float)(sizeof(Aligned_GoodOrder_t) - 6) / sizeof(Aligned_GoodOrder_t) * 100);

while (1) { HAL_Delay(1000); }

}

在STM32F103实测中,Aligned_BadOrder_t的sizeof为12(填充6字节),而将int移至首位的Aligned_GoodOrder_t仅为8(填充2字节),节省33%内存。Aligned_Mixed_t因double需8字节对齐,总大小被撑至24。若切换为pack(1),三个结构体分别为6、6、13字节,但CPU每次访问int成员都需额外指令处理未对齐地址。

这组数据揭示了一个朴素却常被忽视的工程真理:结构体成员的排列顺序不是随意的艺术,而是影响内存效率的科学。在RAM仅20KB的MCU上,一个设计不当的结构体数组可能无声吞噬数百字节,而调整成员顺序或合理使用pack指令,便能在不改变功能的前提下释放宝贵空间。sizeof()不会说谎,它忠实地丈量着每一字节的去向,提醒开发者在追求功能的同时,永远不要忘记脚下那片寸土寸金的内存。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

近日,兆讯恒达(MEGAHUNT)推出全新MH242x系列32位高性能MCU。新品面向工业控制及智能终端应用打造,在运算性能、实时控制、模拟性能、高速通信及软硬件兼容性等方面全面升级,为客户提供更加高效、灵活的产品选择。

关键字: 工业控制 智能终端 MCU

在设计安全相关系统(SRS)时使用符合功能安全(FS)的组件能够带来诸多优势,但大多数设计仍然依赖于标准IC,即未按照FS标准开发的IC。从系统级角度来看,通过使用标准部件进行功能安全设计,设计人员便能够始终实现合规性,...

关键字: 监控电路 FS MCU

乍一看,你可能会以为我们造了一个复杂的纸笔筒,但实际上它可能连这么重都不够。这是我们和朋友们第一次尝试设计MCU电路板,当然我们不得不过度设计,让它变得尽可能复杂。但做简单的事,又有什么乐趣呢?

关键字: MCU 电路板 nRF52840

当数字控制技术被引入DC-DC电源领域,工程师面对的核心命题不再是“是否选用数字方案”,而是“如何在数字域中实现比模拟控制更优的环路性能”。数字控制DC-DC相比传统模拟方案,在参数可编程性、抗干扰能力和系统集成度上展现...

关键字: DSP MCU

MSP-EXP432P401R 在平衡功耗与性能方面表现出色。在这个项目中,我开发了一个智能环境监测器,能够以高精度实时追踪温度和湿度,同时在99%的时间内保持超低功耗状态。

关键字: MCU 电源 MSP432P401R

中国北京(2026年6月25日)—— 业界领先的半导体器件供应商兆易创新GigaDevice(股票代码:603986.SH;3986.HK)宣布,推出全新GD33AP236x系列车规级SBC(系统基础芯片)。该系列集成L...

关键字: 汽车电子 LIN收发器 MCU

Qt for MCUs 预集成于兆易创新 GD32H7 平台,缩短工业自动化与消费电子领域从原型到量产的开发周期。 芬兰埃斯波2026年6月24日 /美通社/ -- Qt Group(Nasdaq Helsinki:...

关键字: GROUP GUI MCU 消费电子

当前,嵌入式系统正向多架构、高复杂度与全生命周期管理快速演进,研发团队面临维护多款产品、适配不同架构、承接新旧迭代项目、兼顾功能安全认证、跨团队协作、代码长期维护等一系列挑战。传统单套工具难以满足上述诸多需求,多套独立工...

关键字: 嵌入式系统 半导体 MCU

MCU作为嵌入式系统的核心,从诞生之初就面临着固件被逆向、拷贝的风险。在上世纪八九十年代,MCU的应用刚在工业控制、消费电子领域普及,芯片厂商还没有成熟的硬件加密技术,早期工程师们只能在有限的硬件条件下,想出各种巧妙的加...

关键字: MCU CPU

在MCU(微控制单元)的硬件设计中,晶体振荡器(晶振)是系统时钟的核心源,而晶振两端并联的对地电容(通常称为“负载电容”)是确保时钟稳定、精准工作的关键元件。这两个看似简单的电容,实则是MCU时钟系统的“稳定器”与“滤波...

关键字: MCU 晶振
关闭