先上代码,对于基于f-statck协议栈改进项目时候,看到一段代码,内核里面的很多宏也经常使用这种复合型语句,看了相关的资料和博客,写下来自己的理解:
static int (*real_clock_gettime) (clockid_t , struct timespec *);
#define SYSCALL(func) \
({ \
if (unlikely(!real_##func)) { \
real_##func = dlsym(RTLD_NEXT, #func); \
} \
real_##func; \
})
相关调用:
int clock_gettime_real(clockid_t clk_id,struct timespec *tp)
{
return SYSCALL(clock_gettime)(clk_id, tp);
}
第一次看大概明白real_clock_gettime函数未定义(大概率)时,从动态链接库操作句柄与符号,获得函数的地址,并赋给函数指针real_clock_gettime,而在函数clock_gettime_real被调用时候,先进行SYSCALLL的宏替换并调用函数real_clock_gettime(clk_id,tp)返回值等于该函数返回值。但是第一次看还是觉得理解就好并且只是认为默认都这么用,没有关心这背后的道道,怎么最终变成return real_clock_gettime(clk_id,tp);
这边涉及到复合语句的使用,结合,看了一些博客,又重新熟悉了一遍复合语句使用。
复合语句:根据百度词条描述的是,把多个语句用括号{}括起来组成的一个语句称复合语句。
复合语句用的最多的一个地方就是在一些语句中,如选择和循环语句,if,for,while中,本来只能后面跟随一条语句,利用复合语句可以达到多条语句变成一条语句的效果。当然在,用{}也是一个代码块,有代码块作用域,一些变量声明在代码块声明时,也只能在这块区域内才能被访问。
而当复合语句被小括号包裹起来时,能被充当表达式的作用(个人理解),借用别人博客的例子说明:
https://blog.csdn.net/npy_lp/article/details/7015066
#include int main(void)
{
int a = ({ int b = 8;
int c = 99;
b + c;
b + c - 10;
});
printf("a = %d\n", a);
return 0;
}
这里的执行结果a = 97,因此({ })的作用是一个表达式,表达式的值是最后复合语句的最后一个语句的值,类型是最后一个语句的类型。这边时候已经大概明白({})的作用了,如图:
像(Statements and Declarations in Expressions)这类的复合语句,我更愿意理解为是一个表达式,即小括号加上复合型语句变成一个表达式,整体({})执行的过程为先执行复合语句里面的每个表达式直到最后一个,然后返回一个表达式,而表达式值为小括号内最后一个表达式的值,数据类型也为最后一个表达式的数据类型。
当时为了证明这一个过程,写了一些测试代码
#include int main(void)
{
char *p;
char a, b, c;
char new[100] = "expression";
b = ({a = 3; new;})[0];
c = (({a = 4; new;}), a, *(({a = 5; new;}) + 1));
printf ("b:%c\n", b);
printf ("c:%c\n", c);
printf ("sizeof(p):%d\n", sizeof(p));
printf ("sizeof(new):%d\n", sizeof(new));
printf ("sizeof(new[0]):%d\n", sizeof(({a = 3; new;})[0]));
printf ("sizeof(expression):%d\n", sizeof(({a = 3; new;})));
}
b:e
c:x
sizeof(p):8
sizeof(new):100
sizeof(new[0]):1
sizeof(expression):8
根据这一理解,b的等号右边是先算复合型语句,({a=3;new;}),值为new的值,由于new是数据名,也是指针常量,所以new的值是一个地址,数据类型为,(char *)类型,这时候下标表达式其实跟间接访问一致,最终值为new[0]的值为字符"e“。
C的等号右边是逗号表达式,逗号表达的值为最后一个表达式的值,所以最后表达式的值为new[1]
最下面的3个sizeof的打印值
第一个相当于类型(char *)的占得字节大小
第二个是new这个数组的长度(顺便说一句,数组名通常情况下是指针常量,只有在两种情况下不用指针常量表达,一个就是sizeof操作符,一个是单目操作符&,取一个数组名的地址是一个指向数组的指针)
第三个就是指new[0]占得字节大小
第四个就是这个表达式占得字节大小