网站建设手机,网络广告营销特性,网站建设要做些什么问题,阅读的网站建设需要多少钱typeof()
使用typeof可以获取一个变量或表达式的类型。 typeof的参数有两种形式#xff1a;表达式或类型。
int i;typeof(i) j 20; -- int j 20;typeof(int *) a; --int *a;
int f(); --typeof(f()) k;--? int k我们可以看出通过typeof获取一个变量的…typeof()
使用typeof可以获取一个变量或表达式的类型。 typeof的参数有两种形式表达式或类型。
int i;typeof(i) j 20; -- int j 20;typeof(int *) a; --int *a;
int f(); --typeof(f()) k;--? int k我们可以看出通过typeof获取一个变量的类型int后可以使用该类型再定义一个变量。
高级用法
typeof (int *) y;--int *y y是一个指向int类型的指针typeof (int) *y; 执行int类型的指针变量ytypeof (*x) y; y是一个指向x类型的指针 (这下是不是对前面两个的小小区别有所感悟)typeof (int) y[4]; y这个数组元素的类型是int 换成x就是x的类型 int y[4] typeof (*x) y[4]; *x y[4]typeof (typeof (char *)[4]) y;//--char *y[4] 字符指针数组 这个里面的数组元素都是 指针变量 typeof (char *)[4] -- char *[4] type (char *)类型不就是括号里面的 然后 在加一层--typeof(int x[4]) y; int y[4]对于以上其实观察的时候就一层层的剥开即可
typeof对于数组的操作还是挺花哨的惊艳了。
container_of宏
Linux内核第一宏container_of
听起来这么牛的宏我们来一睹芳容一下
#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: 结构体成员ptr: 结构体成员member地址
这个宏的作用 通过结构体的某一成员的地址来获取这个结构体的首地址。
这下再看是不是大概还是好点了。
我们来看个实用例子。
struct student
{int age;int num;int math;
};int main(void)
{struct student stu;struct student *p;p container_of(stu.num, struct student, num);return 0;
}定义一个结构体类型student 然后定义一个结构体变量stu
知道了结构体成员变量stu.num的地址
那么我们就可以通过container_of宏来获取结构体变量stu的首地址。
container_of这个玩意有什么用呢
因为内核中有很多的结构体类型数据为了抽象会对结构体进行多次的封装往往一个结构体里面又包含了多个结构体。无限套娃。不同的层次不同的模块使用的是对应的不同封装程度的结构体。
这个是不是有点让你联想到面向对象思想没想到没事我知道你认真专注于当下。
这样的优点我就不多说了。面向对象的优点。
在内核中我们传递个函数的参数是某个结构体的成员在这个函数中还想用这个结构体的其他变量这不是就需要container_of出场了。
找到了这个结构体的首地址–
offsetof()函数主要用来计算member成员相对于结构体起始地址的偏移量。
现在我们来详细看看这个宏定义
这里需要你先知道结构体怎么存储的。
结构体作为一个复合类型数据它里面可以有多个成员。当我们定义一个结构体变量时编译器要给这个变量在内存中分配存储空间。根据每个成员的数据类型和字节对齐方式编译器会按照结构体中各个成员的顺序在内存中分配一片连续的空间来存储它们。
将数字0通过强制类型转换转换为一个指向结构体类型为student的常量指针然后分别打印这个常量指针指向的各成员地址。运行结果如下。
因为常量指针的值为0即可以看作结构体首地址为0所以结构体中每个成员变量的地址即该成员相对于结构体首地址的偏移。
知道了结构体的相对偏移地址用结构体成员的地址减去相对偏移就可以得到结构体的首地址。
从语法角度来看container_of宏的实现由一个语句表达式构成。语句表达式的值即最后一个表达式的值。
取结构体某个成员member的地址减去这个成员在结构体type中的偏移运算结果就是结构体type的首地址。
因为语句表达式的值等于最后一个表达式的值所以这个结果也是整个语句表达式的值container_of最后会返回这个地址值给宏的调用者。
结构体的成员数据类型可以是任意数据类型为了让这个宏兼容各种数据类型我们定义了一个临时指针变量__mptr该变量用来存储结构体成员MEMBER的地址即存储宏中的参数ptr的值。如何获取ptr指针类型呢。 #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: 结构体成员 ptr: 结构体成员member地址 指针变量__mptr存储结构体成员MEMBER的地址--ptr我们知道**宏的参数ptr代表的是一个结构体成员变量MEMBER的地址**所以ptr的类型是一个指向MEMBER数据类型的指针当我们使用临时指针变量__mptr来存储ptr的值时必须确保__mptr的指针类型和ptr一样是一个指向MEMBER类型的指针变量。
确保__mptr的指针类型和ptr一样
typeof(((type*)0)-member表达式使用typeof关键字用来获取结构体成员MEMBER的数据类型然后使用该类型通过typeof(((type*)0)-member*__mptr这条程序语句就可以定义一个指向该类型的指针变量了。
在语句表达式的最后*因为返回的是结构体的首地址所以整个地址还必须强制转换一下转换为TYPE即返回一个指向TYPE结构体类型的指针*所以你会在最后一个表达式中看到一个强制类型转换TYPE
这个文章也写的不错讲的蛮清楚的。 一个parent代表结构体的类型name代表结构体中的成员。
*((parent)0)**把数字0强制转换成parent 结构体指针类型。这样 ((parent )0) 这个整体就相当于一个指针指向了 0 这个地址不管 0 这个地址是否合法是否真的有这么一个结构体对象它都会把以 0 地址为首的一片连续内存当成一个结构体对象操作。
((parent)0)-name* *结构体指针((parent)0)取结构体对象中name成员。因为这只是对内存操作**并没有写内存虽然地址不合法也不会出现段错误。
*((parent )0)-name对name成员取地址。
*offset (uint32_t)((parent )0)-name偏移量。 因为这个parent类型结构体对象是从0地址开始的故而offset就是成员name的偏移量。
知道成员偏移量就很容易求结构体对象本身的地址。成员的地址减去偏移量就是是结构体对象的首地址 – 本来的变量地址-便宜地址就是开始地方 ((parent*)0)((uint32_t)node - (uint32_t)((parent*)0)-name)结构体对象的首地址。
然后就可以开始访问变量了。-
到这里你应该懂了这个原理
[学习资料](https://www.freesion.com/article/57261214164/) 《嵌入式C语言自我修养从芯片、编译器到操作系统》