linux C下的结构体大小和如何使用计算结构体成员相对于结构体头的偏移量

144 篇文章 6 订阅
本文讲述了在Linux环境下C编程中遇到的结构体大小问题,重点介绍了如何通过`offsetof`函数计算成员的偏移量,避免因对齐导致的访问错误。讨论了结构体对齐原理和实际应用实例,以及不同结构定义对大小的影响。
摘要由CSDN通过智能技术生成

在linux下进行C编程,有时候会绵连结构体的大小问题。结构体大小并不等于其内部所有成员大小之和,这个主要是由于结构体需要内部对齐造成的,比如struct a{ int a; long b}它的大小可能就是16,而不是12,因为long为8个字节,int为4个字节,这里面需要8字节对齐。而struct a{ int a; int b; long c;}同样是16个字节,这是因为前面2个int刚好8字节,和后面的long一样。所有如果想在程序中基于偏移量来获取结构体成员的值时,靠人工计算该成员相对于结构体首地址的偏移量是有问题的。还是借助linux内核中一个很经典的函数思想来实现,contains of,先将结构体指针赋值为0,然后打印出该成员的地址,将地址转换成int值,就得到了该成员相对于结构体首地址的偏移量。后面得到一个结构体指针后,加上该偏移量,就是该成员的真实地址。

//测试模块中所定义的结构体的大小,便于后期大小设置访问
//检测是否出现struct中字段出现位不齐问题

#include <stdio.h>
#include <stdlib.h>
struct addr_space {
	unsigned int in_episode_head;      //是否在幕头中
	unsigned long offset;              //addr在幕头,offset为幕文件地址;addr在元数据,offset为幕头aadr的偏移
	unsigned int iblock;               //地址空间,0-未分配空间,1-已分配空间
    unsigned long allocated_size;      //已分配的空间大小
	unsigned long used_size;           //已使用的空间大小
};

//幕头结构(存储在文件头部,固定长度)
struct episode_head {
	unsigned int magic1;               //魔数1
	unsigned int num;                  //数据种类的数量
    struct addr_space addr;            //地址空间
    unsigned int magic2;               //魔数2
};

void main(){
	int offset = 0;
	unsigned int block = 0;
	struct episode_head *head = 0;
	
	/*printf("addr head->addr =%p \n",&head->addr);
	offset = (int)((&head->addr));//获取addr在head中的偏移量
	printf("offset = %d\n",offset);

	struct addr_space *addr = 0;
	printf("addr->iblock = %p\n",&addr->iblock);
	offset += (int)((&addr->iblock));
	*/
	//上述两次获取,等同于offset = (int)(&head->addr.iblock);
	offset = (int)(&head->addr.iblock);

	head = (struct episode_head *)malloc(sizeof(struct episode_head));
	printf("after malloc, head = %p\n",head);
	head->magic1 = 1;
	head->num = 2;
	head->magic2 = 3;
	head->addr.in_episode_head = 4;
	head->addr.offset = 5;
	head->addr.iblock = 6;
	head->addr.allocated_size = 7;
	head->addr.used_size = 8;
	printf("iblock = %d\n",head->addr.iblock);
	int * a = &(head->addr.iblock);
	printf("a = %d\n",*a);
	block = *(int *)(&(head->addr.iblock));//,sizeof(unsigned int));
	printf("head +offset = %p\n",(char *)head+offset);
	printf("iblock = %d\n",*(int *)((char*)head+offset));

	printf("head +offset = %p\n",head+offset);
	printf("iblock = %d\n",*(int *)(head+offset));
	//memcpy(&block,head+offset,4);
	printf("block = %u\n",block);
    printf("地址空间所占大小:struct addr_space size=%d\n",sizeof(struct addr_space)); //20
	printf("幕头空间所占大小:struct episode_head size=%d\n",sizeof(struct episode_head)); //32
}

 从上述输出中,我们可以看到,我们得到iblock相对于head的偏移量之后,如果想获取iblock的值,那么不能直接使用head+offset,如果这么用,它的意思是把当前地址当成一个指向episode_head的数组的首地址,head+offset等于head[offset],也就等同于head+offset*sizeof(episode_head)这个值可不是我们想要的,所以,我们先把head当成char*,然后得到head+offset的值,再把这个值当成int*,即可得到想要的内容了。

此外,对于上述两个结构体的大小,如果更改结构体定义中的某些成员的上下位置,结构体的大小也会发生变化,这就是因为对齐造成的。我上面的截图是在windows下验证的,不存在这方面的问题。但是在linux下,如下图所示,发现结构体大小和windows上的完全不一样,同时,如果把addr_space中的iblock移动到offset之前,则sizeof(addr_space)=32.(linux c中的long为8字节,int为4字节)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值