结构体难点总结

来源:互联网 发布:淘宝装修代码怎么使用 编辑:程序博客网 时间:2024/05/20 01:33

本文中不涉及结构体一些基础知识的说明


1、结构体定义时需要先定义结构体类型,然后再用类型来定义变量。

也可以在定义结构体类型的同时定义结构体变量。


举例:
// 定义类型的同时定义变量。
struct student
{
char name[20];
int age;
}s1;




// 将类型struct student重命名为s1,s1是一个类型名,不是变量
typedef struct student
{
char name[20];
int age;
}s1;




2、结构体的访问方式和数组一样,本质都是通过指针进行访问,无论是(.还是->)
注意两者的区别:
struct myStruct
{
int a; // 4 
double b; // 8
char c;
};
struct s s1;
.......
double *p = (double *)((int)&s1 + 4); //这种才是正确的
double *p = (double *)(&s1 + 4); 


3、结构体中元素对齐访问主要原因:
1.为了配合硬件,如果对齐排布和访问会提高效率,否则会大大降低效率。
2.内存是一个物理器件,本身有一定的局限性:如果内存每次访问时按照4字节对齐访问,那么效率是最高的;如果不对齐访问效率要低很多。
3.还有很多别的因素和原因,导致我们需要对齐访问。譬如Cache的一些缓存特性,
还有其他硬件(譬如MMU、LCD显示器)的一些内存依赖特性,所以会要求内存对齐访问。
4.对比对齐访问和不对齐访问:对齐访问牺牲了内存空间,换取了速度性能;而非对齐访问牺牲了访问速度性能,换取了内存空间的完全利用。
5.32位编译器,一般编译器默认对齐方式是4字节对齐。单个元素是4字节对齐,结构体整体也要保证4字节对齐。

举例:
struct mystruct1
{
   int a;
   char b;
   short c;
};

typedef struct 
{
   int a; //4
   struct mystruct1 s1;//8
   double b; //8
   int c; //4
}myStruct2;


typedef struct 
{
char sex; //4
int length; //4
char name[10];//12
}myStruct3;

printf("sizeof(struct mystruct2) = %d.\n", sizeof(struct mystruct2));//值为24而不是32
printf("sizeof(struct mystruct3) = %d.\n", sizeof(struct mystruct3));//值为20


4、对齐方式:
1.gcc支持但不推荐的对齐指令:#pragma pack()   #pragma pack(n) (n=1/2/4/8)
我们需要#prgama pack(n)开头,以#pragma pack()结尾,定义一个区间,这个区间内的对齐参数就是n。
#prgma pack的方式在很多C环境下都是支持的,但是gcc虽然也可以不过不建议使用。
这种对齐方式保证每个元素按多少个字节对齐,具体可安下面的代码进行测试:
#pragma pack(128)//改变n的值进行测试
struct mystruct1
{
   int a;
   char b;
   short c;
};
#pragma pack()
printf("sizeof(struct mystruct1) = %d.\n", sizeof(struct mystruct1));

2.gcc推荐的对齐指令__attribute__((packed))  __attribute__((aligned(n)))//// 定义变量时加这个没作用
(1)__attribute__((packed))使用时直接放在要进行内存对齐的类型定义的后面,然后它起作用的范围只有加了这个东西的这一个类型。
packed的作用就是取消对齐访问。相当于#pragma pack(1)
(2)__attribute__((aligned(n)))使用时直接放在要进行内存对齐的类型定义的后面,然后它起作用的范围只有加了这个东西的这一个类型。
它的作用是让整个结构体变量整体进行n字节对齐(注意是结构体变量整体n字节对齐,而不是结构体内各元素也要n字节对齐)
测试(注意书写格式不能变):
typedef struct 
{
   char a;
   int b;
   short c;
} __attribute__((packed)) mystruct11;
printf("sizeof(struct mystruct11) = %d.\n", sizeof(struct mystruct11));

typedef struct
{
   int a;
   char b;
   short c;
}__attribute__((aligned(1024))) mystruct21;
printf("sizeof(struct mystruct21) = %d.\n", sizeof(struct mystruct21));

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


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


((TYPE *)0)->MEMBER (TYPE *)0是一个TYPE类型结构体变量的指针,通过指针指针来访问这个结构体变量的member元素


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


举例:
struct mystruct
{
char a; // 0
int b; // 4
short c; // 8
};


int offsetofa = offsetof(struct mystruct, a);
printf("offsetofa = %d.\n", offsetofa);


int offsetofb = offsetof(struct mystruct, b);
printf("offsetofb = %d.\n", offsetofb);


int offsetofc = offsetof(struct mystruct, c);
printf("offsetofc = %d.\n", offsetofc);




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




#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr);\
(type *)((char *)__mptr - offsetof(type, member)); })

功能说明:
ptr是指向结构体元素member的指针,type是结构体类型,member是结构体中一个元素的元素名
这个宏返回的就是指向整个结构体变量的指针,类型是(type *)


具体分析:
1.const typeof(((type *)0)->member)得到member的数据类型
假设member类型为int 则const typeof(((type *)0)->member) * __mptr = (ptr);可以转化为
int * __mptr = (ptr);


2.offsetof(type, member)算出的是member相对于结构体地址(首元素的地址)的偏移量,
现在知道偏移量和member的地址,则结构体首元素的地址是__mptr-offsetof(type, member),
即(type *)((char *)__mptr - offsetof(type, member));
举例:


struct mystruct
{
char a; // 0
int b; // 4
short c; // 8
};


struct mystruct s1;
struct mystruct *pS = NULL;


short *p = &(s1.c); // p就是指向结构体中某个member的指针


printf("s1的指针等于:%p.\n", &s1);


// 问题是要通过p来计算得到s1的指针,这个在linux内核中经常会使用到
pS = container_of(p, struct mystruct, c);
printf("pS等于:%p.\n", pS);
原创粉丝点击