结构体相关
- 结构体成员offset和size
- 结构体对齐
- 结构体成员比较
结构体成员offset和size
如何计算结构体成员在结构体中的偏移
s为结构体,m为结构体成员
- ((s *)0):强制转化成数据结构指针,并使其指向地址0;
- ((s *)0)->m:使该指针指向成员m
- &(((s *)0)->m):获取该成员m的地址
- (size_t)&(((s *)0)->m):转化这个地址为合适的类型
结构体中包含结构体成员情况见下面的定义,example_struct为结构体类型的成员
#ifdef exfo
#undef exfo
#endif
#define exfo(s,m) ((size_t)&(((s *)0)->m)),(sizeof(((s *)0)->m))
#ifdef info
#undef info
#endif
#define info(s,m) ((size_t)&( (( (s *)0 )->example_struct).m)), ((sizeof(((s *)0)->example_struct).m))
//example_struct是结构体内的成员,但成员本身是另一个结构体
typedef struct off_size
{
int off;
int size;
}Off_Size
typedef struct _Example_0
{
char a;
int b;
char c[10];
char *s;
}Example_0;
typedef struct _Example_1
{
Example_0 example_struct;
char x;
int y;
char z[10];
}Example_1;
Off_Size example0_off_size[]=
{
{exfo(Example_0,a)}, //{0,1},
{exfo(Example_0,b)}, //{1,4},
{exfo(Example_0,c)}, //{5,10},
{exfo(Example_0,d)}, //{15,4},
};
Off_Size example1_off_size[]=
{
{info(Example_1,a)},
{info(Example_1,b)},
{info(Example_1,c)},
{info(Example_1,d)},
{exfo(Example_1,x)},
{exfo(Example_1,y)},
{exfo(Example_1,z)},
}
结构体对齐
- 32位CPU取四个字节比一个字节更高效方便
- #pragma pack(n);//n=1,2,4,8,16,其中n就是你想要指定的系数
- #pragma unpack()
复杂类型(如结构体)整体的对齐是按照结构体中长度最大的数据成员和#pragma pack指定值之间较小的那个值进行;Pack的主要目的是挤压达到最小化数据类型的长度,而不是保证数据类型的字节对齐。 - IAR编译属性__attribute__((aligned(n)))显式声明
对齐规则
1.第一个成员在与结构体变量偏移量(offset)为0的地址处。
2.其他成员变量要对齐到对齐数的整数倍的地址处。
3.对齐数 = 对齐系数 与 该成员大小的较小值。
4.#pragma pack(n);中的n就是对齐系数。
5.VS中默认的值为8;linux中的默认值为4。
6.结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
————————————————
上述对齐规则片段截取原文链接:https://blog.csdn.net/kuimzzs/article/details/81605160
结构体成员比较
在coding中需要比较两个结构体,比较其内部成员是否相同。
- memset
void *memset(void *s, int c, unsigned long n);
- memcmp
int memcmp(const void *buf1, const void *buf2, size_t count))
//比较内存区域buf1和buf2的前count个字节
//当buf1<buf2时,返回值<0
//当buf1=buf2时,返回值=0
//当buf1>buf2时,返回值>0
/*
对于memcmp(),如果两个字符串相同而且count大于字符串长度的话,memcmp不会在\0处停下来,会继续比较\0后面的内存单元,直到_res不为零或者达到count次数。
如果想使用memcmp比较字符串,要保证count不能超过最短字符串的长度,否则结果有可能是错误的
*/
如果不能保证结构体用memset来初始化,那么尽量不要用memcpy来直接比较两个结构体,因为会存在字节对齐的情况。
- memcpy
void *memcpy(void *destin, void *source, unsigned n)
- Example
typedef struct Params
{
int a;
char b[10];
char c[6];
int d;
}Params
typedef struct APPTContent
{
Params params;
Time time;
Info info;
}APPTContent ;
typedef struct _APP_Params
{
Params params; //公共结构体
uint8 check;
char *plus;
}APP_Params ;
APPTContent app_content;
APP_Params app_params;
- 需求:app_params是定时循环更新的,app_content里面保留了app_params的上一次更新数据。
- 当app_params新的数据到来时, 和app_content中的数据比较,不同则更新。
for(int i=0;i< param_item_num;i++)
{
if(memcmp((&(app_params.params.a)+Item[i].offset),(&(app_content.params.a)+Item[i].offset),Item[i].size)!=0)
{
memset((&(app_content.params.a)+Item[i].offset), 0, Item[i].size);
memcpy((&(app_content.params.a)+Item[i].offset), (&(app_params.params.a)+Item[i].offset), Item[i].size);
}
}
另一种获取结构体内部成员的方法
#include <stddef.h>
struct Person
{
int a;
char b;
char buf[64];
int d;
}
void test()
{
struct Person p = {10,'a',"Hellp workld",100};
printf("b off:%d\n", offsetof(struct Person, b)); //b off:4
printf("d = %d\n", *(int *)(&p+offsetof(struct Person, d))); //d = 100
}
从而上面的结构体成员比较可以写成
if(memcmp(((char *)&app_params+Item[i].offset),((char *)&app_content+Item[i].offset),Item[i].size)!=0)
{
...
}
如果app_params是一个指向结构体的指针
APP_Params app_params;
APP_Params *app_params_ptr = &app_params;
if(memcmp(((char *)&(*app_params_ptr)+Item[i].offset),((char *)&app_content+Item[i].offset),Item[i].size)!=0)
{
...
}
为什么会有内存对齐
字,双字,4字在自然边界上不需要在内存中对齐。
为了访问未对齐的内存,处理器需要做两次内存的访问,即需要额外的内存总线周期来访问内存中未对齐的数据;然而,对齐的内存访问仅需要一次访问。
缺省情况下,编译器默认将结构、栈中的成员数据进行内存对齐。编译器将未对齐的成员向后移,将每一个成员都对齐到自然边界上,从而也导致整个结构的尺寸变大。用牺牲控件来换取性能。因此,sizeof(One_struct)时,得到是经过偏移后的占用内存的大小。
在谈#progma pack()
- #progma pack(n) //编译器将按照n字节对齐
- #progma pack() //编译器将取消自定义字节对齐方式
在#progma pack(n)和#progma pack()之间的代码按n字节对齐