-
2018-04-23 17:34:08关于stm32结构体对齐最近使用华大MCU hc32l110系列单片机,该单片机位M0+ 内核,由于该单片机存在4字节对齐问题(后来发现的),定义结构体及变量的地址为4的倍数.比如我定义一个8字节的结构体(8个char型变量),当我从结构体第一个字节复制到8字节的数组的时候,能够正常复制,但当我从第二个字节复制到数组的时候,却从第四个字节复制,但stm32却不存在这种问题,为了解决这种问题需在结构体前后加入强制字节对齐.如下,最终问题得以解决
#pragma pack(push) //保存对齐状态
#pragma pack(1) // 1 bytes对齐typedef struct{...................
}
#pragma pack(pop)//恢复对齐状态
更多相关内容 -
【IoT】STM32 结构体对齐规则
2019-04-01 09:18:40在相同的对齐方式下,结构体内部数据定义的顺序不同,结构体整体占据内存空间也不同。 如下结构体定义: struct A { // a 的自身对齐值为 4,偏移地址为 0x00~0x03,a 的起始地址 0x00 满足 0x00%4=0 int a; // ...1、对齐算法
在相同的对齐方式下,结构体内部数据定义的顺序不同,结构体整体占据内存空间也不同。
如下结构体定义:
struct A { // a 的自身对齐值为 4,偏移地址为 0x00~0x03,a 的起始地址 0x00 满足 0x00%4=0 int a; // b 的自身对齐值为 1,由于紧跟 a 之后的地址,即 0x04 满足 0x04%1=0,所以 b 存放在 0x04 地址空间 char b; // c的自身对齐值为2,由于紧跟b之后的地址0x05%2不是0,而0x06%2=0 // 因此c的存放起始地址为0x06,存放在0x06~0x07空间。 // 在b和c之间的0x05地址则补空字节 short c; };
结构体A中包含:
1)4 字节长度的 int;
2)1 字节长度的 char;
3)2 字节长度的 short 型数据一个。
所以 A 用到的空间应该是 7 字节。
但是因为编译器要对数据成员在空间上进行对齐,由于结构体自身对齐值取数据成员中自身对齐值的最大值,即 4,并且 0x00~0x07 的 8 字节空间满足 8%4=0,所以 sizeof(strcut A) 值为 8。
现在把该结构体调整成员变量的顺序:
struct B { // b的自身对齐值为1,其起始地址为0x00,由于满足0x00%1=0,所以b存放在0x00地址空间 char b; // a的自身对齐值为4,由于紧跟b之后的地址0x01%4不是0,而0x04%4=0 // 因此c的存放起始地址为0x04,存放在0x04~0x07空间。 // 在b和a之间的0x01~0x03地址则补3个空字节。 int a; // c的自身对齐值为2,由于紧跟a之后的地址0x08%2=0,因此c的存放起始地址为0x08,存放在0x08~0x09空间 short c; };
这时候同样是总共7个字节的变量,但是由于结构体自身对齐值取数据成员中自身对齐值的最大值,即4;
并且0x00~0x09的10字节空间不满足10%4=0,而12%4=0,所以sizeof(struct B)的值却是12,即在紧跟c之后的0x0A~0x0B地址还需补两个空字节,使得整个结构体占用的字节空间为12个字节。
下面我们使用预编译指令#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。#pragma pack (2) /*指定按2字节对齐,等价于#pragma pack(push,2)*/ struct C { char b; int a; short c; };
#pragma pack () /*取消指定对齐,恢复缺省对齐,等价于#pragma pack(pop)*/
sizeof(struct C) 值是8。修改对齐值为1:
#pragma pack (1) /*指定按1字节对齐*/ struct D { char b; int a; short c; };
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct D) 值为7。
对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,long类型,其自身对齐值为4,double,long long类型,其自身对齐值为8单位字节。
2、四个概念值
1)数据类型自身的对齐值,就是上面交代的基本数据类型的自身对齐值;
2)指定对齐值:#pragma pack (value)时的指定对齐值value;
3)结构体或者类的自身对齐值:其数据成员中自身对齐值最大的那个值;
4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值;
有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。
有效对齐值N是最终用来决定数据存放地址方式的值,最重要。
有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0"。
而数据结构中的数据变量都是按定义的先后顺序来排放的。
第一个数据变量的起始地址就是数据结构的起始地址。
结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整,就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍。
这样就不难理解上面的几个例子的值了。
例子分析:
struct B { char b; int a; short c; };
假设B从地址空间0x0000开始排放。
该例子中没有定义指定对齐值,该值默认为4。
第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.
第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,符合0x0004%4=0, 且紧靠第一个变量。
第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。
再看数据结构B的自身对齐值为其变量中最大对齐值(这里是a)和指定对齐值(这里是4)中较小的那个,所以就是4,所以结构体的有效对齐值也是4。
根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。
所以0x0000A到0x000B也为结构体B所占用,故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;、
#pragma pack (2) /*指定按2字节对齐*/ struct C { char b; int a; short c; };
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1=0;
第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。
第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在0x0006、0x0007中,符合0x0006%2=0。
所以从0x0000到0x00007共八字节存放的是C的变量,又C的自身对齐值为4;
所以C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.
更改c编译器的缺省字节对齐方式:在缺省情况下,c编译器为每一个变量或数据单元按其自然对界条件分配空间;
一般地可以通过下面的两种方法来改变缺省的对界条件:
方法一:
使用#pragma pack(n),指定c编译器按照n个字节对齐;
使用#pragma pack(),取消自定义字节对齐方式。方法二:
__attribute(aligned(n)),让所作用的数据成员对齐在n字节的自然边界上;
如果结构中有成员的长度大于n,则按照最大成员的长度来对齐;
__attribute((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
比如:
typedef struct { ... }__attribute__((aligned(4))) param_t;
3、示例
例子一:
#pragma pack(4) class TestB { public: int aa; //第一个成员,放在[0,3]偏移的位置, char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。由于下一成员short b是按2字节对齐,因此char a后面只需补一个空字节 short b; //第三个成员,自身长2,#pragma pack(4),取小值为2,按2字节对齐,所以放在偏移[6,7]的位置。 char c; //第四个,自身长为1,放在[8]的位置。 };
可见,此类实际占用的内存空间是9个字节。
根据规则5,结构整体的对齐是min( sizeof( int ), pack_value ) = 4,所以sizeof( TestB ) = 12;
char a 占用一个字节,后面补3个空字节;
short b和char c可以放在同一4字节空间中,后面只需补一个空字节。
例子二:
#pragma pack(2) class TestB { public: int aa; //第一个成员,放在[0,3]偏移的位置, char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。由于下一成员short b按2字节对齐,因此char a后面只需补一个空字节 short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。 char c; //第四个,自身长为1,放在[8]的位置。 };
可见结果与例子一相同,各个成员的位置没有改变,此时结构整体的对齐是min( sizeof( int ), pack_value ) = 2,所以sizeof( TestB ) = 10;char a后面补一个空字节;char c后面补一个空字节。
例子三:
#pragma pack(4) class TestC { public: char a; //第一个成员,放在[0]偏移的位置,由于下一成员short b按2字节对齐,因此char a后面只弱补一个空字节 short b; //第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。 char c; //第三个,自身长为1,放在[4]的位置。 };
整个类的实际内存消耗是5个字节,整体按照min( sizeof( short ), 4 ) = 2对齐,所以结果是sizeof( TestC ) = 6。
例子四:
struct Test { char x1; //第一个成员,放在[0]位置。由于下一成员short x2按2字节对齐,因此char x1后面只需补一个空字节, short x2; //第二个成员,自身长度为2,按2字节对齐,所以放在偏移[2,3]的位置, float x3; //第三个成员,自身长度为4,按4字节对齐,所以放在偏移[4,7]的位置, char x4; //第四个成员,自身长度为1,按1字节对齐,所以放在偏移[8]的位置, };
所以整个结构体的实际内存消耗是9个字节,考虑到结构整体的对齐是4个字节,所以整个结构占用的空间是12个字节。char x1和short x2共用一个4字节空间,后面补一个空字节;char x4后面需要补3个空字节。
例子五:
#pragma pack(8) struct s1 { short a; //第一个,放在[0,1]位置, long b; //第二个,自身长度为4,按min(4, 8) = 4对齐,所以放在[4,7]位置 };
所以结构体的实际内存消耗是8个字节;
结构体的对齐是min( sizeof( long ), pack_value ) = 4字节,所以整个结构占用的空间是8个字节。
struct s2 { char c; //第一个,放在[0]位置, s1 d; //第二个,根据规则四,对齐是min( 4, pack_value ) = 4字节(由于s1占用了8个字节,这里为什么不是8字节??),所以放在[4,11]位置, long long e; //第三个,自身长度为8字节,所以按8字节对齐,所以放在[16,23]位置, };
所以实际内存消耗是24字节。整体对齐方式是8字节,所以整个结构占用的空间是24字节。
#pragma pack()
所以:sizeof(s2) = 24, char c后面是空了3个字节接着是s1 d, s1 d后面又补了4个空字节,char c占一个字节,后面补7个字节;s1 d占8个字节;long long e占8个字节。
refer:https://www.cnblogs.com/King-Gentleman/p/5297355.html
-
STM32学习笔记10——stm32中结构体字节对齐问题
2017-12-11 18:41:09stm32做串口或网络传输数据...所以,在定义结构体类型时,最好把结构体对齐方式改为1字节对齐,防止出错。理论和方法见下面转载的文章。 转载自:https://www.cnblogs.com/King-Gentleman/p/5297355.html #pragmastm32做串口或网络传输数据时,经常需要用结构体定义帧格式。如果按照keil默认的对齐方式(4字节对齐),经常会出现结构体中补零的问题,造成帧格式错误。所以,在定义结构体类型时,最好把结构体对齐方式改为1字节对齐,防止出错。理论和方法见下面转载的文章。
转载自:https://www.cnblogs.com/King-Gentleman/p/5297355.html
#pragma pack(4) //按4字节对齐,但实际上由于结构体中单个成员的最大占用字节数为2字节,因此实际还是按2字节对齐
typedef struct
{
char buf[3];//buf[1]按1字节对齐,buf[2]按1字节对齐,由于buf[3]的下一成员word a是按两字节对齐,因此buf[3]按1字节对齐后,后面只需补一空字节
word a; //#pragma pack(4),取小值为2,按2字节对齐。
}kk;
#pragma pack() //取消自定义字节对齐方式
对齐的原则是min(sizeof(word ),4)=2,因此是2字节对齐,而不是我们认为的4字节对齐。
这里有三点很重要:
1.每个成员分别按自己的方式对齐,并能最小化长度
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度
3.对齐后的结构体整体长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐补充一下,对于数组,比如:
char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐.
如果写: typedef char Array3[3];
Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度.
不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个.声明:
整理自网络达人们的帖子,部分参照MSDN。作用:
指定结构体、联合以及类成员的packing alignment;语法:
#pragma pack( [show] | [push | pop] [, identifier], n )说明:
1,pack提供数据声明级别的控制,对定义不起作用;
2,调用pack时不指定参数,n将被设成默认值;
3,一旦改变数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降;语法具体分析:
1,show:可选参数;显示当前packing aligment的字节数,以warning message的形式被显示;
2,push:可选参数;将当前指定的packing alignment数值进行压栈操作,这里的栈是the internal compiler stack,同时设置当前的packing alignment为n;如果n没有指定,则将当前的packing alignment数值压栈;
3,pop:可选参数;从internal compiler stack中删除最顶端的record;如果没有指定n,则当前栈顶record即为新的packing alignment数值;如果指定了n,则n将成为新的packing aligment数值;如果指定了identifier,则internal compiler stack中的record都将被pop直到identifier被找到,然后pop出identitier,同时设置packing alignment数值为当前栈顶的record;如果指定的identifier并不存在于internal compiler stack,则pop操作被忽略;
4,identifier:可选参数;当同push一起使用时,赋予当前被压入栈中的record一个名称;当同pop一起使用时,从internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作;
5,n:可选参数;指定packing的数值,以字节为单位;缺省数值是8,合法的数值分别是1、2、4、8、16。重要规则:
1,复杂类型中各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个类型的地址相同;
2,每个成员分别对齐,即每个成员按自己的方式对齐,并最小化长度;规则就是每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐;
3,结构体、联合体或者类的数据成员,第一个放在偏移为0的地方;以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度两个中比较小的那个进行;也就是说,当#pragma pack指定的值等于或者超过所有数据成员长度的时候,这个指定值的大小将不产生任何效果;4,复杂类型(如结构体)整体的对齐是按照结构体中长度最大的数据成员和#pragma pack指定值之间较小的那个值进行;这样当数据成员为复杂类型(如结构体)时,可以最小化长度;
5,复杂类型(如结构体)整体长度的计算必须取所用过的所有对齐参数的整数倍,不够补空字节;也就是取所用过的所有对齐参数中最大的那个值的整数倍,因为对齐参数都是2的n次方;这样在处理数组时可以保证每一项都边界对齐;对齐的算法: 由于各个平台和编译器的不同,现以本人使用的gcc version 3.2.2编译器(32位x86平台)为例子,来讨论编译器对struct数据结构中的各成员如何进行对齐的。在相同的对齐方式下,结构体内部数据定义的顺序不同,结构体整体占据内存空间也不同,如下:设结构体如下定义:struct A {int a; //a的自身对齐值为4,偏移地址为0x00~0x03,a的起始地址0x00满足0x00%4=0;char b; //b的自身对齐值为1,由于紧跟a之后的地址,即0x04满足0x04%1=0,所以b存放在0x04地址空间short c; //c的自身对齐值为2,由于紧跟b之后的地址0x05%2不是0,而0x06%2=0,因此c的存放起始地址为0x06,存放在0x06~0x07空间。在b和c之间的0x05地址 则补空字节。};结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个。所以A用到的空间应该是7字节。但是因为编译器要对数据成员在空间上进行对齐。 由于结构体自身对齐值取数据成员中自身对齐值的最大值,即4,并且0x00~0x07的8字节空间满足8%4=0,所以sizeof(strcut A)值为8。现在把该结构体调整成员变量的顺序。struct B {char b; //b的自身对齐值为1,其起始地址为0x00,由于满足0x00%1=0,所以b存放在0x00地址空间int a; //a的自身对齐值为4,由于紧跟b之后的地址0x01%4不是0,而0x04%4=0,因此c的存放起始地址为0x04,存放在0x04~0x07空间。在b和a之间的0x01~0x03地址则补3个空字节。short c; //c的自身对齐值为2,由于紧跟a之后的地址0x08%2=0,因此c的存放起始地址为0x08,存放在0x08~0x09空间。};这时候同样是总共7个字节的变量,但是由于结构体自身对齐值取数据成员中自身对齐值的最大值,即4,并且0x00~0x09的10字节空间不满足10%4=0,而12%4=0,所以sizeof(struct B)的值却是12, 即在紧跟c之后的0x0A~0x0B地址还需补两个空字节,使得整个结构体占用的字节空间为12个字节。下面我们使用预编译指令#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。#pragma pack (2) /*指定按2字节对齐,等价于#pragma pack(push,2)*/struct C {char b;int a;short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐,等价于#pragma pack(pop)*/sizeof(struct C)值是8。修改对齐值为1:#pragma pack (1) /*指定按1字节对齐*/struct D {char b;int a;short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐*/sizeof(struct D)值为7。对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,long类型,其自身对齐值为4,double,long long类型,其自身对齐值为8,单位字节。这里面有四个概念值:
1. 数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。2. 指定对齐值:#pragma pack (value)时的指定对齐值value。3. 结构体或者类的自身对齐值:其数据成员中自身对齐值最大的那个值。4. 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解)。这样就不难理解上面的几个例子的值了。例子分析: 分析例子B;struct B {char b;int a;short c;};假设B从地址空间0x0000开始排放。 该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小, 所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4, 所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,符合0x0004%4=0, 且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2, 可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是a)和指定对齐值(这里是4)中较小的那个,所以就是4, 所以结构体的有效对齐值也是4。 根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;同理,分析上面例子C:#pragma pack (2)/*指定按2字节对齐*/struct C {char b;int a;short c;};#pragma pack () /*取消指定对齐,恢复缺省对齐*/第一个变量b的自身对齐值为1,指定对齐值为2, 所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1=0;第二个变量,自身对齐值为4,指定对齐值为2, 所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2, 所以有效对齐值为2,顺序存放在0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量,又C的自身对齐值为4;所以C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.更改c编译器的缺省字节对齐方式:
在缺省情况下,c编译器为每一个变量或数据单元按其自然对界条件分配空间;一般地可以通过下面的两种方法来改变缺省的对界条件:
方法一:
使用#pragma pack(n),指定c编译器按照n个字节对齐;
使用#pragma pack(),取消自定义字节对齐方式。方法二:
__attribute(aligned(n)),让所作用的数据成员对齐在n字节的自然边界上;如果结构中有成员的长度大于n,则按照最大成员的长度来对齐;
__attribute((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。比如:
typedef struct
{
...
}__attribute__((aligned(4))) param_t;
综上所述,下面给出例子并详细分析:
例子一:
#pragma pack(4)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。由于下一成员short b是按2字节对齐,因此char a后面只需补一个空字节
short b; //第三个成员,自身长2,#pragma pack(4),取小值为2,按2字节对齐,所以放在偏移[6,7]的位置。
char c; //第四个,自身长为1,放在[8]的位置。
};
可见,此类实际占用的内存空间是9个字节。(这里有疑问?按我的理解,根据规则5,结构整体的对齐是min( sizeof( int ), pack_value ) = 4,所以sizeof( TestB ) = 12; char a 占用一个字节,后面补3个空字节;short b和char c可以放在同一4字节空间中,后面只需补一个空字节。)
例子二:
#pragma pack(2)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。由于下一成员short b按2字节对齐,因此char a后面只需补一个空字节
short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
char c; //第四个,自身长为1,放在[8]的位置。
};
可见结果与例子一相同,各个成员的位置没有改变,(这里有疑问?按我的理解,此时结构整体的对齐是min( sizeof( int ), pack_value ) = 2,所以sizeof( TestB ) = 10;char a后面补一个空字节;char c后面补一个空字节。)
例子三:
#pragma pack(4)
class TestC
{
public:
char a; //第一个成员,放在[0]偏移的位置,由于下一成员short b按2字节对齐,因此char a后面只弱补一个空字节
short b; //第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。
char c; //第三个,自身长为1,放在[4]的位置。
};
整个类的实际内存消耗是5个字节,(这里有疑问?按我的理解,整体按照min( sizeof( short ), 4 ) = 2对齐,所以结果是sizeof( TestC ) = 6。)
例子四:
struct Test
{
char x1; //第一个成员,放在[0]位置。由于下一成员short x2按2字节对齐,因此char x1后面只需补一个空字节,
short x2; //第二个成员,自身长度为2,按2字节对齐,所以放在偏移[2,3]的位置,
float x3; //第三个成员,自身长度为4,按4字节对齐,所以放在偏移[4,7]的位置,
char x4; //第四个成员,自身长度为1,按1字节对齐,所以放在偏移[8]的位置,
};
所以整个结构体的实际内存消耗是9个字节,(这里有疑问?按我的理解,考虑到结构整体的对齐是4个字节,所以整个结构占用的空间是12个字节。char x1和short x2共用一个4字节空间,后面补一个空字节;char x4后面需要补3个空字节。)
例子五:
#pragma pack(8)struct s1
{
short a; //第一个,放在[0,1]位置,
long b; //第二个,自身长度为4,按min(4, 8) = 4对齐,所以放在[4,7]位置
};
所以结构体的实际内存消耗是8个字节。结构体的对齐是min( sizeof( long ), pack_value ) = 4字节,所以整个结构占用的空间是8个字节。struct s2
{
char c; //第一个,放在[0]位置,
s1 d; //第二个,根据规则四,对齐是min( 4, pack_value ) = 4字节(由于s1占用了8个字节,这里为什么不是8字节??),所以放在[4,11]位置,
long long e; //第三个,自身长度为8字节,所以按8字节对齐,所以放在[16,23]位置,
};
所以实际内存消耗是24字节。整体对齐方式是8字节,所以整个结构占用的空间是24字节。#pragma pack()
所以:sizeof(s2) = 24, char c后面是空了3个字节接着是s1 d, s1 d后面又补了4个空字节(这里有疑问??按我的理解,char c占一个字节,后面补7个字节;s1 d占8个字节;long long e占8个字节。)
-
【STM32】结构体的字节对齐和大小端
2020-06-01 16:52:08本次实验的平台以32位CortexM0单片机,不同的平台的实验结果可能是不一样的,但是思路都一样。 2、基础概念 先介绍三个概念:自身对齐值、指定对齐值、有效对齐值。 自身对齐值:数据类型本身的对齐值,例如char文章的知识点来源于:https://www.cnblogs.com/heart-flying/p/9556401.html,我在原文提供的方法上自己做了一些验证以及思考,以作为自己的学习记录,方便日后回顾。这篇文章讲得简单明了,解决了我很久以来的疑惑,因此万分感谢,如有侵权的地方请私信我删除。
1、测试平台
本次实验的平台以32位CortexM0单片机,不同的平台的实验结果可能是不一样的,但是思路都一样。
2、基础概念
先介绍三个概念:自身对齐值、指定对齐值、有效对齐值。
自身对齐值:数据类型本身的对齐值,例如char类型的自身对齐值是1,short类型是2,int类型是4;
指定对齐值:编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4;
有效对齐值:自身对齐值和指定对齐值中较小的那个。对齐有两个规则:
1、不但结构体的成员有有效对齐值,结构体本身也有对齐值,这主要是考虑结构体的数组,对于结构体或者类,要将其补齐为其有效对齐值的整数倍。而结构体的有效对齐值是其最大数据成员的自身对齐值;
2、存放成员的起始地址必须是该成员有效对齐值的整数倍。3、开始测试
先举几个例子,假设以下所有的结构体的起始地址都是0x0000 0000
#pragma pack(4) typedef struct A { u8 a[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 0地址存放a u8 b[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 1地址存放b u8 c[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 2地址存放c u8 d[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 3地址存放d u8 e[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 4地址存放e 此时结构体的长度是5字节 //最后整个结构体长度进行对齐,结构体的有效对齐值是其最大数据成员的自身对齐值 //最大数据成员是1字节,因此最后结构体本身长度要进行1字节对齐,因此结构体总长度是5字节 }structA; #pragma pack()
#pragma pack(4) typedef struct B { u8 a[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 0地址存放a u16 b[1]; //自身对齐是2,指定对齐是4,因此有效对齐是2, 1地址进行填充,2、3地址存放b(思考题1:2地址存放高字节还是3地址存放高字节?) u8 c[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 4地址存放c u8 d[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 5地址存放d u8 e[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 6地址存放e 此时结构体的长度是7字节 //最后整个结构体长度进行对齐,结构体的有效对齐值是其最大数据成员的自身对齐值 //最大数据成员是2字节,因此最后结构体本身长度要进行2字节对齐,因此结构体总长度是8字节 }structB; #pragma pack()
#pragma pack(4) typedef struct C { u8 a[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 0地址存放a u32 b[1]; //自身对齐是4,指定对齐是4,因此有效对齐是2, 1、2、3地址进行填充,4、5、6、7地址存放b u8 c[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 8地址存放c u8 d[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 9地址存放d u8 e[1]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 10地址存放e 此时结构体的长度是11字节 //最后整个结构体长度进行对齐,结构体的有效对齐值是其最大数据成员的自身对齐值 //最大数据成员是4字节,因此最后结构体本身长度要进行4字节对齐,因此结构体总长度是12字节 }structC; #pragma pack()
4、测试代码
structA A; structB B; structC C; volatile u32 len_A, len_B, len_C; void align_test(void) { len_A = sizeof(A); len_B = sizeof(B); len_C = sizeof(C); A.a[0] = B.a[0] = C.a[0] = 1; A.b[0] = B.b[0] = C.b[0] = 2; A.c[0] = B.c[0] = C.c[0] = 3; A.d[0] = B.d[0] = C.d[0] = 4; A.e[0] = B.e[0] = C.e[0] = 5; }
5、测试结果
通过MDK的在线调试可以清楚地看到结构体这几个结构体的地址以及它的成员的地址,用sizeof(xxx)可以计算结构体的总大小,可以看出来我们以上的分析是正确的。
同时我们也能看到,所有的由编译器自己分配的结构体的首地址,一定是4字节对齐的,例如结构体A的首地址是0x100040F4,结构体B的首地址是0x100040FA,结构体C的首地址是0x10004B18,因此不用担心结构体的首地址的字节对齐问题,而只需要关心结构体成员和结构体总长度的字节对齐问题。
6、思考题
思考题1:结构体B中,2地址存放高字节还是3地址存放高字节?
这里涉及到大小端的问题,对于小端的MCU,高地址存放高字节,低地址存放低字节,例如本试验中,0x0002存放在2、3地址中,3地址存放0x00,2地址存放0x02,请看下图。
如果是大端的MCU,高地址存放低字节,低地址存放高字节,结果就会反过来,这也是编程中常常需要留意的问题了。 -
基于STM32使用嵌套结构体摸索内存对齐
2021-08-20 15:06:07导读一、结构体的声明以及初始化二、联合体以及嵌套结构体的用法位段小端模式三、数据跨地址内存导致的内存对齐现象 一、结构体的声明以及初始化 联合体实际上来说是特殊数据结构的一类,通过关键词struct来定义。... -
【IoT】STM32 结构体位域操作 (int a:4)
2019-03-25 19:24:48首先通过这种现象我们可以肯定的是:带有'位域'的结构体并不是按照每个域对齐的,而是将一些位域 成员'捆绑'在一起做对齐的。 以foo2为例,这个结构体中所有的成员都是char型的,而且三个位域占用的总空间为6 bit... -
对STM32 32位单片机中结构体struct的字节对齐(RAM)的理解
2019-10-28 15:13:57本示例均采用编译器默认字节对齐方式来说明,STM32 在KEIL中内存为4字节对齐。 #pragma pack() //取消自定义字节对齐方式 各字符类型占用字节数 类型 占用内存(对齐值) /字节 char 1 short 2 int 4 ... -
STM32字节对齐更改方法
2020-01-10 16:14:52STM32裸板程序默认是4字节对齐,但是STM32如果跑RTOS的话,可能改成单字节对齐。导致协议数据错位错误。 typedef struct{ int a; char b; double c; char d[10]; }Test_TypeDef; 在STM32裸板下该 Test_... -
STM32 终极字节对齐解析
2021-11-09 22:09:31uint32_t定义变量地址要4字节对齐。 uint64_t定义变量地址要8字节对齐。 指针变量是4字节对齐。 二、结构体成员对齐问题: 首先明白一点,结构体里面的变量是什么类型,此变量的位置就是至少要几字节对齐,所以就... -
STM32 把结构体存入Flash,并读取(微薄之力,已验证)
2019-11-29 20:10:56经过分析应该是这种情况,就是结构体对齐的问题 存储模式:小端:较高的有效字节存储在较高的存储器地址,较低的有效字节存储在较低的存储器地址。 大端:较高的有效字节存储在较低的存储器地址,较低的有效... -
STM32遇到的结构体数组成员未被赋值问题
2021-05-09 22:05:52这几天用STM32F411做BP神经网络,在电脑上训练好之后移植到STM32里得到的预测值全部都是0.5,用keil仿真发现隐含层的输出数据全部都是2.3648316684e-240之类的数值。 继续往前追溯,查到权值数组的时候发现需要用的... -
C 语言在 STM32 下字节对齐
2018-01-25 00:04:51参考: wiki Data_structure_alignment. C 语言字节对齐问题详解, 这篇文章写的很详细。 ... 2、stm32 中使用 #pragma pack(非常有用的字节对齐用法说明) 3、正点原子开源电子论坛_ mymalloc -
结构体 内存对齐 keil & STM32
2017-07-17 09:53:00直接 上图 不废话: 第二张图: 把16位 类型的数据 换成 数组 ,这样 达到 节约内存和 方便处理 缓冲区数据 转载于:https://www.cnblogs.com/suozhang/p/7192926.html... -
结构体内存对齐——2
2018-09-07 21:30:32对齐到4字节 = 3+3+2+4+1+1+1+1 = 16 struct gif_hdr v1 = {1,2,3,4,5,6,7,8,9,10,11}; struct gif_hdr *dsptr; printf("Size of structure data = %d\n", sizeof(struct gif_hdr)); dsptr = (struct gif_... -
结构体在STM32串口接收中的妙用
2020-10-04 12:32:49结构体C程序中在STM32单片机串口接收的妙用之一 结构体是一种数据的归类方式,相比数组或变量更具有整体全面性,例如一个数组只可以放一些按照元素顺序存放的单元变量,即tab={x,x,x,x,x......},i有多大,数组内元素就... -
STM32F407因为结构体字节对齐导致的HardFault_Handler异常
2019-01-09 11:34:56STM32因为结构体字节对齐导致的HardFault_Handler异常 该问题遇到了好多次,前几次都因为赶,没有深究,前几天刚好空闲下来便排查了下。 现象是这样的:我使用到stm32的can通信,代码本来是运行正常的。后来添加了... -
结构体对齐规则
2019-11-29 20:32:30指定对齐值:编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4; 有效对齐值:自身对齐值和指定对齐值中较小的那个。 存储模式:小端:较高的有效字节存储在较高的存储器地址,较低的有效字节存储在... -
[013] [STM32] armclang对结构体位域的过渡优化——导致未按字对齐访问寄存器引发的异常
2022-03-16 12:43:56GPIO外设寄存器必须按字对齐方式访问,否则会引发异常(不同外设对齐方式不一样,需查看手册) 结构体位域方式操作寄存器时,即便用volatile关键字告知编译器必须按字访问,但是armclang依然会优化成按字节访问... -
【IoT】STM32 字节对齐 #pragma pack
2019-03-29 19:14:511、对齐原则 min(sizeof(word ), 4) = 2,因此是 2 字节对齐,而不是我们认为的 4 字节对齐...3)对齐后的结构体整体长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。 对于数... -
STM32F407由于结构体字节对齐致使的HardFault_Handler异常
2021-05-26 01:34:32STM32由于结构体字节对齐致使的HardFault_Handler异常该问题遇到了好屡次,前几回都由于赶,没有深究,前几天恰好空闲下来便排查了下。现象是这样的:我使用到stm32的can通讯,代码原本是运行正常的。后来添加了一个... -
解决STM32因字节对齐问题导致读写Flash失败进入HardFault的问题
2021-10-22 14:14:05背景:项目中使用的MCU是STM32L431,在进行升级时需要往Flash中写数据,其他业务代码都是可以正常读写,但是在写升级数据时进入HardFault_Handler,程序跑飞。 由于项目需要,获取升级包的文件所有结构体采用一字节... -
HAL库STM32内部EEPROM保存结构体
2022-04-01 23:00:45STM32内部Flash读写,替代外部24C02等磁链存储,掉电不丢失,HLA库 -
蓝桥杯STM32G431——定时器编程常用结构体
2022-01-29 16:31:02定时器基本初始化结构体 Prescaler:定时器预分频设置,时钟源经过该分频器才是定时器时钟,它设定TIMx_Psc寄存器的值。可设置值范围为0~65535,实现1至65536分频(实际分频系数=TIMx_PSC+1),假设设置Prescaler=... -
c语言学习笔记(6)结构体对齐理论
2020-03-16 14:12:451:平台在STM32 1、对齐算法 在相同的对齐方式下,结构体内部数据定义的顺序不同,结构体整体占据内存空间也不同。 如下结构体定义: struct A { // a 的自身对齐值为 4,偏移地址为 0x00~0x03,a 的起始地址 0x00 ... -
KEIL中结构体对齐笔记
2022-05-15 11:02:50但是一般在32位系统一般是4字节对齐,如果能指定结构体对齐数则可以解决在数据解析中数据位错乱的问题。 1.未使用结构体对齐操作 (1)首先定义一个结构体。 struct TestStruct2 { unsigned char head1; ... -
STM32F0非对齐访问引起的硬件错误及其排除
2016-09-28 21:41:01前段时间在用STM32L0芯片时遇到一个硬件错误,主要是内存的非对齐访问导致的。 注:CM3内核支持非对齐访问,而CM0不支持非对齐访问。 先科普一下,什么是非对齐访问 如上图所示,若要访问的变量是int、double、u32...