一、奇妙的“结构体”
1#include <stdio.h>
2//结构体定义
3typedef struct __tag_Test
4{
5 int param1;
6 int param2;
7}stTest;
8//结构体初始化方式1
9stTest sTest1 = {
10 .param1 = 1,
11 .param2 = 2
12};
13//结构体初始化方式2
14stTest sTest2 = {
15 1,2
16};
17/*********************************************
18 * Fuction: main
19 * Author : (公众号:最后一个bug)
20 ********************************************/
21int main(void) {
22
23 printf("sTest1.param1 = %d\n",sTest1.param1);
24 printf("sTest1.param2 = %d\n",sTest1.param2);
25 printf("sTest2.param1 = %d\n",sTest2.param1);
26 printf("sTest2.param2 = %d\n",sTest2.param2);
27 return 0;
28}
解析一下:上面是一个非常简单的代码,其中第一种结构体的初始化是linux源码中非常常见的一种方式,这种初始化的方式编译器必须要遵循ISO C99标准,否则只能使用第二种比较常规的方式。
1#include <stdio.h>
2
3 struct __tag_Man
4{
5 int Age;
6 int Height;
7};
8
9typedef struct __tag_Man stStudent;
10typedef struct __tag_Man stWaiter;
11
12stStudent sStudent = {
13 .Age = 12,
14 .Height = 20,
15};
16
17stWaiter sWaiter = {
18 .Age = 12,
19 .Height = 20,
20};
21
22int main(void) {
23
24 printf("sStudent.Age = %d\n",sStudent.Age);
25 printf("sStudent.Height = %d\n",sStudent.Height);
26 printf("sWaiter.Age = %d\n",sWaiter.Age);
27 printf("sWaiter.Height = %d\n",sWaiter.Height);
28 printf("公众号:最后一个bug");
29 return 0;
30}
3、0地址与结构体的妙用
这一块的内容算是这篇文章的重点内容,各位小伙伴们要做好笔记了,作者一直非常强调一点的是多读读大佬们的代码,并且善于总结一些常用的小技巧供大家平时使用。这不,我们今天就拿Linux的kernal中的两个宏定义来分享几个结构体小技巧:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
解析一下:第一个宏定义的功能是获得一个结构体成员距离结构体首地址的偏移量,参数TYPE : 结构体类型;参数MEMBER : 结构体成员,其实这个算是比较简单的,把0地址强制类型转化为结构体类型指针,然后通过结构体指向成员即可获得结构体成员变量,然后通过&进行取地址便获得了结构体成员地址,成员的偏移 = (结构体成员地址 - 结构体首地址);然而结构体首地址为0,这样成员的偏移 = 结构体成员地址,应该足够清楚了吧。
第二个宏定义的功能是通过结构体成员变量获得对应的结构体首地址(也就是结构体地址),参数ptr :结构体成员变量地址;参数type : 结构体类型;参数member :结构体成员,这个宏定义可能对于一些小伙伴而言在写法上有一点点难度,不过其主要分为两个部分:
第一部分通过typeof获得成员的类型并定义了一个const指针,定义为const的目的是不让用户对0地址的内容进行写操作,对于大部分芯片对不合法区域进行读写会引起异常;第二部分通过使用offsetof宏定义获得结构体成员相对结构体首地址的偏移,这样一相减便获得了当前结构体成员所属结构体的地址,原理公式:(结构体地址 = 结构体成员地址 - 结构体成员的偏移)。
这里大体说一下注意事项:在第二点我们谈到了typeof关键字,该关键字是GUN C标准中扩展的关键字,所以在使用该宏定义的时候需要注意一下;不然,采用其他标准进行编译可能会报错。
4、最后帮助大家理解的小程序
作者要说的全在代码里面了:
#include <stdio.h>
/**********************************
* Fuction : from Linux Kernal
**********************************/
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/**********************************
* Fuction : 结构体定义区
* Author : (公众号:最后一个bug)
**********************************/
typedef struct _tag_Test{
int Member1;
int Member2;
int Member3;
}STest;
int main(void) {
int iTest = 0;
STest stTest;
printf("offsetof(STest , Member3) : %d\n",offsetof(STest , Member3));
printf("&stTest : 0X%X\n",&stTest);
printf("&(stTest.Member3) : 0X%X\n",&(stTest.Member3));
printf("container of Member3 : 0X%X\n",container_of(&(stTest.Member3),STest,Member3));
//这里主要是进一步让大家理解第二个宏
//且右侧必须加()
iTest = ({int Val = 5; Val;});
printf("iTest = %d\n",iTest);
printf("欢迎关注公众号:最后一个bug\n");
return 1;
}
程序运行的结果如下:
offsetof(STest , Member3) : 8
&stTest : 0X452469E0
&(stTest.Member3) : 0X452469E8
container of Member3 : 0X452469E0
iTest = 5
欢迎关注公众号:最后一个bug
END
→点关注,不迷路←
文章引用微信公众号"嵌入式微处理器",如有侵权,请联系管理员删除!