持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
开幕雷击
先来看一段代码!
#include <bits/stdc++.h>
int main() {
std::string res;
for (int i = 0; i < 10; i++)
res = ({
if (i % 2)
res = "odd";
else
res = "even";
res;
}),
({
std::cout << res << " ";
res;
});
return 0;
}
上述代码输出什么呢?
它会输出如下东西
even odd even odd even odd even odd even odd
逗号表达式
逗号表达式应该都挺熟悉的
我们可以写出这样不是很好看的东西
int main() {
int a = (1, 2, 3, 4);
}
虽然有病,但是没毛病 QAQ
那么,逗号表达式嵌入逗号表达式也是理所应当的
你还可以用它来做一个 swap 函数
#include <bits/stdc++.h>
#define SWAP(x, y) \
{ \
typeof(x) t; \
(t = x, (x = y, 0), (y = t, 0), 0); \
}
int main() {
int x = 1, y = 2;
SWAP(x, y);
std::cout << x << " " << y << std::endl;
std::string a = "11", b = "22";
SWAP(a, b);
std::cout << a << " " << b << std::endl;
return 0;
}
嗯,看起来还不错,我们这个函数居然还可以交换 stl中的string的值,就是拷贝有点严重!
目前看起来一切都还在正常范围内
对于这个表达式 (t = x, (x = y, 0), (y = t, 0), 0); 在末尾放在0,可以放置可以不放置,但是我就想让末尾为0,这样,整个表达式的值一定为0。
逗号表达式嘛
复合语句
什么是复合语句呢?
就是用{}包起来的语句,我们叫他复合语句,因为可以执行多条语句序列嘛!
那么,从语句的角度来看待,这个玩意儿也应该是一个语句啊!
是语句就可以分为两类:
有值(表达式...),无值(if, for, while)
我们可以在局部写一个复合语句,当作一个语句看待,内部有他自己的作用域,这个是清楚的
那么,我们可以把他当作一个有值的语句,那么是不是可以嵌套到逗号表达式中呢?
怪起来了!
但是注意一点,我们要把这个表达式看作一个值,总得看起来不一样一点嘛
int main() {
({
std::cout << "hello"
"world"
<< std::endl;
0;
});
return 0;
}
那么,我们是不是可以和我们的逗号表达式配合起来用一用呢?
int main() {
int x = 1;
(({ x = 2; }), ({ x = 3; }), ({ x = 4; }), 0);
return 0;
}
这个确实是可行的
只要有值,就可以成为逗号表达式中的一项嘛!
我嵌套一个复合语句进去,有问题吗?
现在就大抵可以回答开头的内容了
应用
那我们来看看这个玩意有什么用途呢?
宏函数
#include "assert.h"
#include <bits/stdc++.h>
#define address_of(arg) \
({ \
assert((&(arg))); \
&(arg); \
})
struct E {
int a, b;
void f() { std::cout << " haha" << std::endl; }
};
void fun(E *q) { q->f(); }
int main() {
E a;
E *b = 0;
fun(b);
fun(address_of(a));
return 0;
}
比如要实现一个函数,判断是不是空,不是空就返回自己的指针
很多时候,一个你可以内联这样干,也可以直接用宏来实现,就没有出栈和入栈的消耗
并且,也可以实现相同的功能
std::cout << address_of(a)->a;
std::cout << address_of(a)->b;
当然,这个并不是我的臆想,而是一个有用的东西
比如Linux内核中的 container_of
#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) \
({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); \
})
就利用了宏函数的特性,使得可以返回任意值,返回什么值看你最后一行的类型
而类型则用typeof推导
我们就可以使用 成员中任意一个成员的指针找到整体结构的指针
(在c++中不一定适用)(看container_of的实现吧,typeof本来好像就是gcc的拓展,其他编译器同意与否不可预知)
当然,既然c++是兼容c的,那么应该没问题,实在不行把typeof换成decltype搞
struct E {
int a;
int b;
int c;
};
int main() {
E a{1, 2, 3};
std::cout << container_of(&a.b, E, b)->a;
return 0;
}