从list_head到sizeof

简介:

在Linux内核中,链表是最常见的数据结构。

一般我们在用户层编程时,使用的链表结构如下:

struct list_node{

          DataType data;

          struct list_node *next;

};

采用这种结构,每个DataType都要定义自己的链表类型,如果用在Linux内核中,那么将充斥这各种各样的链表类型,极不方便。

Linux内核中用了很巧妙的结构。在数据里包含链表,而不是在链表里包含数据。

通用的链表类型

struct list_head{

          struct list_head * next , * pre;

};

struct DataType{

          DataTypeFiled data;

           struct list_head list;

};

 

 

list_head使用了typeof,而typeof是编译时处理的,与typeof差不多的函数是sizeof,也是编译时处理的,某些面试题里出现用函数实现sizeof,可以实现一个具有大部分sizeof功能的函数,但是typeof我还没想到好办法用函数实现。

未完待续。。。

 

sizeof()和typeof()的实现

我们说sizeof()和typeof()是编译时处理的,gcc加上-E选项可以显示预处理后的结果,看看这个例子

 
  1. niuxinli@linux-nxl:~/algrithoms/sizeof> cat test.c 
  2. #define NUM  1 
  3. int main() 
  4.     int a = sizeof(int); 
  5.     typeof(a) b = 1; 
  6.     return 0; 
  7. niuxinli@linux-nxl:~/algrithoms/sizeof> gcc -E test.c  
  8. # 1 "test.c" 
  9. # 1 "<built-in>" 
  10. # 1 "<command-line>" 
  11. # 1 "test.c" 
  12.  
  13. int main() 
  14.  int a = sizeof(int); 
  15.  typeof(a) b = 1; 
  16.  return 0; 

可以看到,预处理阶段并没有对sizeof和typeof进行宏替换,说明它们不是用宏实现的。

利用gcc的-S选项可以查看gcc汇编后的汇编文件

 
  1. niuxinli@linux-nxl:~/algrithoms/sizeof> gcc -S test.c  
  2. niuxinli@linux-nxl:~/algrithoms/sizeof> cat test.s 
  3.     .file   "test.c" 
  4.     .text 
  5. .globl main 
  6.     .type   main, @function 
  7. main: 
  8. .LFB0: 
  9.     .cfi_startproc 
  10.     pushq   %rbp 
  11.     .cfi_def_cfa_offset 16 
  12.     movq    %rsp, %rbp 
  13.     .cfi_offset 6, -16 
  14.     .cfi_def_cfa_register 6 
  15.     movl    $4, -4(%rbp) 
  16.     movl    $1, -8(%rbp) 
  17.     movl    $0, %eax 
  18.     leave 
  19.     .cfi_def_cfa 7, 8 
  20.     ret 
  21.     .cfi_endproc 
  22. .LFE0: 
  23.     .size   main, .-main 
  24.     .ident  "GCC: (SUSE Linux) 4.5.1 20101208 [gcc-4_5-branch revision 167585]" 
  25.     .section    .comment.SUSE.OPTs,"MS",@progbits,1 
  26.     .string "ospwg" 
  27.     .section    .note.GNU-stack,"",@progbits 

可以看到,上面加粗标红的指令就是两个赋值语句,没有调用函数,直接就把4和1赋值给a和b了,说明sizeof()和typeof()不是函数实现的。

编译实际上份好几个过程,预处理只是简单的宏替换,sizeof和typeof应该是在类型检查的时候处理的,在使用它们的时候,可以看作宏。比如下例

 
  1. int main() 
  2.     int a[sizeof(int)] = {0}; 
  3.     return 0; 

再看下面的例子

 
  1. niuxinli@linux-nxl:~/algrithoms/sizeof> cat test.c 
  2. int main() 
  3.     int i; 
  4.     int a[10]; 
  5.     int *b = (int*)malloc(sizeof(int)*10); 
  6.     for(i = 0; i < 10; i++) 
  7.     { 
  8.         a[i] = -1; 
  9.         b[i] = -1; 
  10.     } 
  11.     memset(a,0,sizeof(a)); 
  12.     memset(b,0,sizeof(b)); 
  13.     printf("sizeof(a)=%d,sizeof(b)=%d\n",sizeof(a),sizeof(b)); 
  14.     for(i = 0; i < 10; i++) 
  15.         printf("a[%d]=%d,b[%d]=%d\n",i,a[i],i,b[i]); 
  16.     return 0; 
  17. niuxinli@linux-nxl:~/algrithoms/sizeof> make test 
  18. make: “test”是最新的。 
  19. niuxinli@linux-nxl:~/algrithoms/sizeof> ./test  
  20. sizeof(a)=40,sizeof(b)=8 
  21. a[0]=0,b[0]=0 
  22. a[1]=0,b[1]=0 
  23. a[2]=0,b[2]=-1 
  24. a[3]=0,b[3]=-1 
  25. a[4]=0,b[4]=-1 
  26. a[5]=0,b[5]=-1 
  27. a[6]=0,b[6]=-1 
  28. a[7]=0,b[7]=-1 
  29. a[8]=0,b[8]=-1 
  30. a[9]=0,b[9]=-1 

a的类型是int [10],所以它的长度是40,b的类型是int *,64位机器上是8个字节。

我们再来看下怎么用函数实现类似sizeof()的功能

如果用C++,可以用模板

 
  1. niuxinli@linux-nxl:~/algrithoms/sizeof> cat sizeof.cpp 
  2. #include <iostream> 
  3. #include <stdio.h> 
  4. template <typename T> 
  5.  
  6. size_t my_sizeof(T a) 
  7.     return (size_t) ((char*)(&a+1) - (char*)(&a)); 
  8. int main() 
  9.     int a; 
  10.     double b; 
  11.     printf("size_of(a) = %d,sizeof(b) = %d\n",my_sizeof(a),my_sizeof(b)); 
  12.     return 0; 
  13. niuxinli@linux-nxl:~/algrithoms/sizeof> make sizeof 
  14. make: “sizeof”是最新的。 
  15. niuxinli@linux-nxl:~/algrithoms/sizeof> ./sizeof  
  16. size_of(a) = 4,sizeof(b) = 8 

如果纯用C语言,可以为每个类型定义一个函数,不过用宏也很方便

 
  1. niuxinli@linux-nxl:~/algrithoms/sizeof> cat sizeof.c 
  2. #include <stdio.h> 
  3. #define my_sizeof(a)    (size_t)((char*)(&a+1) - (char*)(&a)) 
  4. int main() 
  5.     int a; 
  6.     double b; 
  7.     printf("sizeof(a) = %d,sizeof(b) = %d\n",my_sizeof(a),my_sizeof(b)); 
  8.     return 0; 
  9. niuxinli@linux-nxl:~/algrithoms/sizeof> gcc sizeof.c 
  10. niuxinli@linux-nxl:~/algrithoms/sizeof> ./a.out  
  11. sizeof(a) = 4,sizeof(b) = 8 

自己写的sizeof有两个问题,一个是无法计算sizeof(int)这样的,可以通过为每个类型写个宏解决这个问题,如#define sizeof(int)  4

第二个问题是我们写的sizeof不能用作宏,这点没有解决方法。

其实虽说是用函数实现,但也需要在编译时处理,类型检查。



本文转自nxlhero 51CTO博客,原文链接:http://blog.51cto.com/nxlhero/732347,如需转载请自行联系原作者

相关文章
|
1月前
|
缓存 JavaScript 前端开发
【Node系列】Buffer详解
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。 但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。 Node.js中的Buffer是一个全局对象,属于固有(built-in)类型的全局变量,不需要使用require函数导入。它允许直接操作原始内存,主要用于处理二进制数据流。Buffer实例对象的结构和整数数组很像,但Buffer的大小是固定的且在V8堆外分配物理内存。
25 2
|
6月前
|
存储 Cloud Native Linux
malloc、free 和 new、delete 的区别
malloc、free 和 new、delete 的区别
|
5天前
|
XML 数据格式
节点列表长度(Node List Length)
`NodeList`对象自动更新,其`length`属性表示列表中节点数量。例如,加载&quot;books.xml&quot;后,`getElementsByTagName(&#39;title&#39;).length`返回`4`。此属性可用来遍历列表,如示例所示,遍历所有`&lt;title&gt;`元素并打印其文本内容:Everyday Italian, Harry Potter, XQuery Kick Start, Learning XML。
|
10月前
|
安全 C语言 C++
new delete和malloc free的区别
一个对象我们可以建立在栈上也可以建立在堆上,但是在C语言里与C++里他们的实现还不同,本文将详细介绍new delete和malloc free之间的区别。
|
10月前
|
算法 API 开发工具
T-Head DebugServer
T-Head DebugServer 是一种用于调试和测试 TEE 应用程序的工具。它可以在 TEE 中运行并提供一个调试接口,允许开发人员通过该接口与 TEE 应用程序进行交互和调试。
384 3
|
JavaScript 网络协议 测试技术
Node 中 Buffer的理解
Buffer对象,是Node的核心模块,在面试中也是会频繁被问到一个考题,如果没有对其深入探究,可能就跟我一样只会用,一旦面试官扩展问些问题可能就不会了。反正,当时我也只能回答说:目前涉及到的业务对Buffer模块用的比较少,但是Buffer对象可以对于一些字符串传输无法满足业务功能和性能上得到有效的优化。
117 0
|
安全 编译器 C语言
malloc、free与new、delete的区别
malloc、free与new、delete的区别
129 0
|
C++
C/C++size(),sizeof(),length(),strlen() 对比分析详解
C/C++size(),sizeof(),length(),strlen() 对比分析详解
131 0
C/C++size(),sizeof(),length(),strlen() 对比分析详解
node函数buf.readDoubleBE详解
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.
728 0