在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字节)。