测试代码:
运行与输出:
说明:
定义的宏取两数中较小那个。刚开始 *p 的值为 8 。在执行 (*p++) <= (10) 这个判断后,指针+1,指向了 9,所以取得了最小值 9。然而,在取值阶段,也就是 (*p++):(10),指针 p 再加 1,指向了 11。这里的副作用是,本意是想判断数组第一个元素是否为较小者,然后取得其值,但是经过指针的移动后,最后取得的值是数组的第 2 个元素 9 。
引用#include <stdio.h>
#define MIN(x,y) ((x) <= (y) ? (x):(y))
int main ()
{
int i = 10;
int k = 8;
int a [ 5 ] = { 8 , 9 , 11 , 10 , 13 };
int *p = a;
int j;
j = MIN( *p ++ , i);
printf( "%d /n " , j);
printf( "%d /n " , *p);
return ( 0);
}
运行与输出:
引用beyes@linux-beyes:~/C/micro> ./micro.exe
9
11
说明:
定义的宏取两数中较小那个。刚开始 *p 的值为 8 。在执行 (*p++) <= (10) 这个判断后,指针+1,指向了 9,所以取得了最小值 9。然而,在取值阶段,也就是 (*p++):(10),指针 p 再加 1,指向了 11。这里的副作用是,本意是想判断数组第一个元素是否为较小者,然后取得其值,但是经过指针的移动后,最后取得的值是数组的第 2 个元素 9 。
GNU CC 是一个功能非常强大的跨平台 C 编译器,它对 C 语言提供了很多扩展,这些扩展对优化、目标代码布局、更安全的检查等方面提供了很强的支持。这里对支持支持 GNU 扩展的 C 语言成为 GNU C。 在 Linux 内核中使用了大量的 GNU C 扩展,以致 GNU C 成为了内核唯一的编译器。
1、语句表达式
GNU C 把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本只能在复合语句中使用。例如有如下宏定义:
引用#define min_t(type, x, y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
而标准的宏定义为:
引用#define min(x,y) ((x) < (y) ? (x) : (y))
这个定义计算 x 和 y 分别两次,当参数有副作用时,将产生不正确的结果.
修改上面链接代码如下:
引用#include <stdio.h>
#define MIN(type, x, y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y;})
int main ()
{
int i = 10;
int a [ 5 ] = { 8 , 9 , 11 , 10 , 13 };
int *p = a;
int j;
j = MIN( int , *p ++ , i);
printf( "%d /n " , j);
printf( "%d /n " , *p);
return ( 0);
}
运行及输出:
引用beyes@linux-beyes:~/C/micro> ./micro2.exe
8
9
说明:
这时候,*P 不再做 2 次的 + 1 运算。这是因为宏定义的语句表达式中有赋值语句,此后作用的是 __x (为 8)而不是 x (为 *p++)。
上面的宏定义,需要提供参数类型,如 int 。但是如果用 typeof 就可以定义更加通用的宏。
测试代码:
引用#include <stdio.h>
#define MIN(x, y) ({ const typeof(x) _x = (x); const typeof(y) _y = (y); _x < _y ? _x:_y;})
int main ()
{
int i = 10;
float a [ 5 ] = { 8.1 , 9.1 , 11.2 , 10.3 , 13.4 };
float *p = a;
float j;
j = MIN( *p ++ , i);
printf( "%f /n " , j);
printf( "%f /n " , *p);
return ( 0);
}
运行及输出:
引用beyes@linux-beyes:~/C/micro> ./micro3.exe
8.100000
9.100000
说明:
应用上面程序中的宏,不再需要额外提供一个参数的类型。和上面的例子一样,不会产生副作用,最终用来比较的值是所希望的数组中的第一个元素 8.1。注意,在宏执行完后,指针还是要移动到数组的下一个元素。