C语言结构体内存对齐和offsetof宏、container-创新互联

1.结构体变量中的元素如何访问

(1)数组中元素的访问方式:表面上有2种方式(数组下标方式和指针方式);实质上都是指针方式访问。
(2)结构体变量中的元素访问方式:只有一种,用.或者->的方式来访问。
(C语言规定用结构体变量来访问元素用. 用结构体变量的指针来访问元素用->。)

创新互联长期为上千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为滨海企业提供专业的网站制作、成都网站设计,滨海网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制开发。2.结构体的对齐访问

总结下:结构体对齐的分析要点和关键:
1、结构体对齐要考虑:结构体整体本身必须安置在4字节对齐处,结构体对齐后的大小必须4的倍数(编译器设置为4字节对齐时,如果编译器设置为8字节对齐,则这里的4是8)
2、结构体中每个元素本身都必须对其存放,而每个元素本身都有自己的对齐规则。
3、编译器考虑结构体存放时,以满足以上2点要求的最少内存需要的排布来算。

gcc支持但不推荐的对齐指令:#pragma pack() #pragma pack(n) (n=1/2/4/8)

我们需要#prgama pack(n)开头,以#pragma pack()结尾,定义一个区间,这个区间内的对齐参数就是n。

#pragma pack(1)
struct mystruct
{short d;
	int b;
	char a;
};
#pragma pack()
gcc推荐的对齐指令__attribute__((packed)) attribute((aligned(n)))

(1)__attribute__((packed))使用时直接放在要进行内存对齐的类型定义的后面,然后它起作用的范围只有加了这个东西的这一个类型。packed的作用就是取消对齐访问。

struct mystruct
{short d;
	int b;
	char a;
}__attribute__((packed));

(2)__attribute__((aligned(n)))使用时直接放在要进行内存对齐的类型定义的后面,然后它起作用的范围只有加了这个东西的这一个类型。它的作用是让整个结构体变量整体进行n字节对齐(注意是结构体变量整体n字节对齐,而不是结构体内各元素也要n字节对齐)

struct mystruct
{short d;
	int b;
	char a;
}__attribute__((aligned(16)));

参考阅读blog:
结构体字节对齐参考博客

GCC___attribute__关键字和字节对齐

3.offsetof宏

(1)offsetof宏的作用是:用宏来计算结构体中某个元素和结构体首地址的偏移量(其实质是通过编译器来帮我们计算)。
(2)offsetof宏的原理:我们虚拟一个type类型结构体变量,然后用type.member的方式来访问那个member元素,继而得到member相对于整个变量首地址的偏移量。

// TYPE是结构体类型,MEMBER是结构体中一个元素的元素名
// 这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,类型是int
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)

(TYPE *)0这是一个强制类型转换,把0地址强制类型转换成一个指针,这个指针指向一个TYPE类型的结构体变量。 (实际上这个结构体变量可能不存在,但是只要我不去解引用这个指针就不会出错)。
((TYPE *)0)->MEMBER (TYPE *)0是一个TYPE类型结构体变量的指针,通过指针指针来访问这个结构体变量的member元素

&((TYPE *)0)->MEMBER等效于&(((TYPE *)0)->MEMBER),意义就是得到member元素的地址。但是因为整个结构体变量的首地址是0,

4.container_of宏
// TYPE是结构体类型,MEMBER是结构体中一个元素的元素名
// 这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,类型是int
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)

// ptr是指向结构体元素member的指针,type是结构体类型,member是结构体中一个元素的元素名
// 这个宏返回的就是指向整个结构体变量的指针,类型是(type *)
#define container_of(ptr, type, member) ({	\
	const typeof(((type *)0)->member) * __mptr = (ptr);	\
	(type *)((char *)__mptr - offsetof(type, member)); })

(1)作用:知道一个结构体中某个元素的指针,反推这个结构体变量的指针。有了container_of宏,我们可以从一个元素的指针得到整个结构体变量的指针,继而得到结构体中其他元素的指针。
(2)typeof关键字的作用是:typepef(a)时由变量a得到a的类型,typeof就是由变量名得到变量数据类型的。
(3)这个宏的工作原理:先用typeof得到member元素的类型定义成一个指针,然后用这个指针减去该元素相对于整个结构体变量的偏移量(偏移量用offsetof宏得到的),减去之后得到的就是整个结构体变量的首地址了,再把这个地址强制类型转换为type *即可。

struct mystruct
{short d;
	int b;
	char a;
};

int main(void)
{struct mystruct s1;
	struct mystruct *ps = NULL;
	int *p = &(s1.a);
	
	printf("s1 = %p.\n", &s1);
	
	ps = container_of(p, struct mystruct, a);
	
	printf("ps = %p.\n", ps);
	
	return 0;
}

知道一个结构体中a元素的指针,反推这个结构体变量的指针。
结果为:
s1 = 0x7ffd8cd5d2fc. ps = 0x7ffd8cd5d2fc.

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


本文标题:C语言结构体内存对齐和offsetof宏、container-创新互联
分享路径:http://ybzwz.com/article/cedgjc.html