Linux内核中出现的C语言细节汇总(不断更新)

1214阅读 0评论2012-04-22 android_bsp
分类:

1.在linux 2.6.38内核的driver/input/keyboard目录下的gpio_keys.c源码中的486行开始如下
  1. for (i = 0; i < pdata->nbuttons; i++) {
  2.         struct gpio_keys_button *button = &pdata->buttons[i];
  3.         struct gpio_button_data *bdata = &ddata->data[i];
  4.         unsigned int type = button->type ?: EV_KEY;

  5.         bdata->input = input;
  6.         bdata->button = button;

  7.         error = gpio_keys_setup_key(pdev, bdata, button);
  8.         if (error)
  9.             goto fail2;

  10.         if (button->wakeup)
  11.             wakeup = 1;

  12.         input_set_capability(input, type, button->code);
  13.     }
其中红色一行出现的用法解释:
首先,三目运算符的用法如下,如果j大于0,那么i = j,否则i = k。
  1. i = j ?: k;
 其次,"->"与"."运算符的区别。
"->" 用于表示指向对象的指针的成员;
"."  用于表示一般成员的成员。
<对象指针名> -> <成员名> 和 (*<对象指针名>) . <成员名> 等价。

2.static限定词

c语言中static的语义
1.static变量:
1).
局部
a.
静态局部变量在函数内定义,生存期为整个源程序,但作用域与自动变量相同,只能在定义该变量的函数内使用。退出该函数后, 尽管该变量还继续存在,但不能使用它。
b.
对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。
static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。
2).全局
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。但是他们的作用域,非静态全局变量的作用域是整个源程序(多个源文件可以共同使用); 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。
2.static函数(也叫内部函数)
只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。区别于一般的非静态函数(外部函数) 
使用静态函数的好处
静态函数会被自动分配在一个一直使用的存储区,直到退出应用程序实例,避免了调用函数时压栈出栈,速度快很多。
关键字“static”,译成中文就是静态的,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。 使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。
 c语言中static_用法总结.doc   

3.空指针和未初始化的指针
空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数; 而未初始化指针则可能指向任何地方。
按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的:

点击(此处)折叠或打开

  1. void * pvoid;
  2. pvoid++;     //ANSI:错误
  3. pvoid += 1;     //ANSI:错误
     ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。例如:

点击(此处)折叠或打开

  1. int *pint;
  2. pint++;      //ANSI:正确      pint++的结果是使其增大sizeof(int)
     但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定,它指定void *的算法操作与char *一致因此下列语句在GNU编译器中皆正确:

点击(此处)折叠或打开

  1. pvoid++;      //GNU:正确
  2. pvoid += 1;     //GNU:正确      pvoid++的执行结果是其增大了1
  3.  
  4. //内核代码示例(linux3.2.8/driver/video/s3c-fb.c 其probe函数里有如下代码):
  5. /* initialise colour key controls */
  6. for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
  7. void __iomem *regs = sfb->regs + sfb->variant.keycon;

  8. //GNU下空指针可以算数运算,并且算法操作与char *一致
  9. //因此,如下代码,可以加,并且加的是(win * 8)的值,而非((win * 8) * 4)
  10. regs += (win * 8);
  11. writel(0xffffff, regs + WKEYCON0);
  12. writel(0xffffff, regs + WKEYCON1);
  13. }
      在实际的程序设计中,为迎合ANSI标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码:

点击(此处)折叠或打开

  1. void * pvoid;
  2. (char *)pvoid++; //ANSI:正确;GNU:正确
  3. (char *)pvoid += 1; //ANSI:错误;GNU:正确
      GNU和ANSI还有一些区别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合ANSI标准。 


4. 常量指针与指针常量
a. 常量指针:“const int *p; ” 与 “int const  *p;” 等价。常量指针不能靠解引用改变它所指向的对象的值,以此保护它所指向的常量的常量值。但是,它本身的指针值可以改变。
b. 指针常量:“int *const p = &n;" 指针常量可以靠解引用改变它所指向的对象的值,但是它本身的值不能改变,且必须在声明时初始化。
  1. int const *p; //常量指针
  2. p = &n;       //指针常量不可这么用

  3. int *const p; //指针常量
  4. *p = n;       //常量指针不可这么用

5. '##'宏参数贴合

点击(此处)折叠或打开

  1. /*
  2.  * Set of macros to define architecture features. This is built into
  3.  * a table by the linker.
  4.  */
  5. #define MACHINE_START(_type,_name)            \
  6. static const struct machine_desc __mach_desc_##_type    \
  7.  __used                            \
  8.  __attribute__((__section__(".arch.info.init"))) = {    \
  9.     .nr         = MACH_TYPE_##_type,        \
  10.     .name = _name,

  11. #define MACHINE_END                \
  12. };
如上代码时宏定义MACHINE_START,其中的'##'告诉编译器把宏参数_type和MACH_TYPE贴合。

点击(此处)折叠或打开

  1. //mach-jason6410.c中
  2. MACHINE_START(JASON6410, "JASON6410")
  3.     /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
  4.     .atag_offset = 0x100,
  5.     .init_irq = s3c6410_init_irq,
  6.     .map_io = jason6410_map_io,
  7.     .init_machine = jason6410_machine_init,
  8.     .timer = &s3c24xx_timer,
  9. MACHINE_END
  10. //展开后
  11. static const struct machine_desc __machine_desc_JASON6410
  12. __used
  13. __atrribute__((__section__(".arch.info.init"))) = {
  14. .nr = MACH_TYPE_JASON6410,
  15. .name = "JASON6410",
  16. /* Maintainer: Darius Augulis <augulis.darius@gmail.com> */
  17. .atag_offset = 0x100,
  18. .init_irq = s3c6410_init_irq,
  19. .map_io = jason6410_map_io,
  20. .init_machine = jason6410_machine_init,
  21. .timer = &s3c24xx_timer,
  22. };

6. 指向结构体的指针
如下代码中,win + 1,指针偏移的是一个结构体大小的位置。在framebuffer_alloc中,分配了fb_info结构体,并为s3c_fb_win结构体和调色板缓冲区分配了内存。win指向s3c_fb_win结构体,加1后指向调色板缓冲区起始地址。然后强制转换为palette_buffer类型的指针,并赋值给win中的palette_buffer成员。

点击(此处)折叠或打开

  1.  摘自linux3.2.8/driver/video/s3c-fb.c的s3c_fb_probe_win中的代码

  2.     struct s3c_fb_win *win;
  3.     int palette_size;

  4.     fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
  5.                  palette_size * sizeof(u32), sfb->dev);
  6.   
  7.     //指向驱动私有数据中的s3c_fb_win结构体,也是上面函数额外分配的内存的起始地址
  8.     win = fbinfo->par;
  9.     win->palette_buffer = (u32 *)(win + 1);

7. 指向多维数组的指针定义与分配
a. 二维数组的定义

点击(此处)折叠或打开

  1. int a[m][n];
  2. int (*p)[n];
  3. p=a;
b. 三维数组的定义与分配

点击(此处)折叠或打开

  1. int a[l][m][n];

  2. int (*p)[m][n];

  3. p = (int (*)[m][n])malloc(l*m*n*sizeof(int));

8. 枚举类型大小

点击(此处)折叠或打开

  1. union u_tag {
  2.     char cVals[16];
  3.     int nVal;
  4.     double dVal;
  5. }u;

  6. sizeof(u);    // 等于枚举元素中最长成员值,这里为16。注意:不同于数组中的情况,如果是数组,则为28。




上一篇:16道嵌入式C语言面试题(经典)
下一篇:如何缩小与"大牛" 的差距