1. 命名习惯
#define PI 3.1415926 int min_value, max_value; void send_data(void);
2. Case范围
GNU C支持case x...y这样的语法,区间[x,y]的数都会满足这个case的条件,例如:
swith(ch) { case '0'...'9': c -= '0'; break; case 'a'...'f': c -= 'a' -10; break; }
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
GNU C把包含在括号中的复合语句看作是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方。我们可以在语句表达式中使用原本只能在符合语句中使用的循环变量、局部变量等,例如:
#define min_t(type,x,y) \ ({ type _ _x = (x); type _ _y = (y); _ _x < _ _y ? _ _x: _ _y; }) int ia, ib, mini; float fa, fb, minf; mini = min_t(int, ia, ib); minf = min_t(float, fa, fb);
#define min(x,y) ((x) < (y) ? (x) : (y))
#define min(x,y) ({ \ const typeof(x) _x = (x); \ const typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x < _y ? _x : _y; })我们不需要像min_t(type,x,y)这个宏那样把type传入,因为通过typeof(x)、typeof(y)可以获得type。代码行(void)(&_x==&_y)的作用是检查_x和_y的类型是否一致。
标准C只支持可变参数的函数,意味着函数的参数是不固定的,例如printf()函数的原型为:
int printf( const char *format [, argument]... );而在GNU C中,红叶可以接受可变数目的参数,例如:
#define pr_debug(fmt,arg...) \ printk(fmt,##arg)这里arg表示其余的参数可以是零个或多个,这些参数以及参数之间的逗号构成arg的值,在宏扩展时替换arg,例如下列代码:
pr_debug("%s:%d",filename,line)会被扩展成为:
printk("%s:%d", filename, line)
pr_debug("success!\n")
printk("success!\n")
printk("success!\n",)
GNU C预定义了两个标识符保存当前函数的名字,__FUNCTION__保存函数在源码中的名字,__PRETTY_FUNCTION__保存带语言特色的名字。在C函数中,这两个名字是相同的。
void example() { printf("This is function:%s", _ _FUNCTION_ _); }
7. do{} while(0)
在Linux内核中,经常会看到 do{} while(0)这样的语句,许多人开始都会疑惑,认为do{} while(0)毫无意义,因为它只会执行一次,加不加do{}while(0)效果完全一样的,其实do{}while(0)主要用于宏定义中。
这里用一个简单的宏来演示:
#define SAFE_FREE(p) do{ free(p); p = NULL;} while(0)
#define SAFE_FREE(p) free(p); p = NULL;
if(NULL != p) SAFE_DELETE(p) else ...//do something
if(NULL != p) free(p); p = NULL; else ...//do something
(1)if分支后有两个语句,导致else分支没有对应的if,编译失败;
(2)假设没有else分支,则SAFE_FREE中的第二个语句无论if测试是否通过都会执行。
将SAFE_FREE定义加上{}就可以解决上述问题了,即:
#define SAFE_FREE(p) { free(p); p = NULL;}
if(NULL != p) SAFE_DELETE(p) else ...//do something
if(NULL != p) { free(p); p = NULL; } else ...//do something
if(NULL != p) SAFE_DELETE(p); else ...//do something
将被扩展为:
<span style="font-size: 9pt;">if(NULL != p) <span style="font-size: 9pt;">{ free(p); p = NULL; }; <span style="font-size: 9pt;">else <span style="font-size: 9pt;">...//do something</span></span></span></span>这样,else分支就又没有对应的if了,编译将无法通过。假设用了do{} while(0),情况就不一样了,同样的代码会被展开为:
if(NULL != p) do{ free(p); p = NULL;} while(0); else ...//do something不会再出现编译问题。 do{}while(0)的使用完全就是为了保证宏定义的使用者能无编译错误的使用宏,它不对其使用者做任何假设。