C语言之结构体相关

结构体相关

  1. 结构体成员offset和size
  2. 结构体对齐
  3. 结构体成员比较

结构体成员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字节对齐
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值