一段古怪的代码

很久很久以前,我在互联网上的某个角落看到了这样一段代码。

1
2
3
4
5
6
7
int main()
{
int *(*(*pfunc)(void *(*)(int *, float), int))(int, double *) =
[](void *(*)(int *, float), int) -> int *(*)(int, double *)
{ return [](int, double *) -> int * { return nullptr; }; };
return 0;
}

该段代码可以在笔者的Clang++ 16上通过编译并正确运行。

(我看不懂,但我大受震撼.jpg)

为了复习我学过的指针、lambda函数知识,我决定试着解释这段代码。


What’s happening?

前面的部分实在太复杂啦,也许我们可以从等号后面读起。

1
2
3
4
5
// 省略...
= [](void *(*)(int *, float), int)
-> int *(*)(int, double *) {
return [](int, double *) -> int *
{ return nullptr; }; };

这显然是一个lambda表达式,他的返回类型是 int *(*)(int, double *) —— 一个参数类型为int, double *,返回int*的函数指针。

Lambda表达式是什么?

一种用来定义匿名函数的语法。

1
2
3
4
[/*捕获外部的变量*/]
(/*函数的参数*/)
->/*返回值类型。不填的话,编译器会帮你自动推导*/
{/*函数体部分*/};

函数指针是什么?

1
2
3
4
5
6
7
8
9
10
11
// 声明函数指针pFunc, 该函数接受两个int类型参数。
int (*pFunc)(int, int);
// 你可以像平常声明函数那样给参数起个名字。但没必要。

// 假设我们之前定义过了add函数(并且它刚好也接受两个int型参数)。
int pFunc = add;
// 函数名本身就是指向函数的指针

//调用!
(*pFunc)(2, 3);
// 加括号以避免符号运算顺序来捣乱

再看看这个表达式吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 省略...
= [](void *(*)(int *, float), int)
// 参数
// 第一个参数是一个函数指针,接受参数(int *, float),返回void *
// 第二个参数是一个int型变量
-> int *(*)(int, double *)
// 返回值类型
// 一个函数指针,接受参数(int, double *),返回int *
{ return [](int, double *) -> int * { return nullptr; }; };
// 函数体
// omg 怎么是lambda套娃
// 接受(int, double *),返回 (int*)nullptr
// Tips: nullptr 的类型是 std::nullptr_t
// 它是一个特殊的类型,可以隐式地转换为任何指针类型,但不能转换为其他类型。

那么前面呢?

既然后面是一个lambda表达式,前面大概就是在声明函数指针吧。

1
2
3
4
5
6
7
8
int *( 

*(*pfunc) ( void *(*)(int *, float), int)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
)
(int, double *)


是函数指针套娃,真难顶。

  • 外层:一个函数指针,接受 (int, double *),返回 int*
  • 内层:一个指向函数指针*pfunc的指针。

结论

这段代码声明了pfunc,它指向一个接受(void *(*)(int *, float), int) 的函数,即后面那一大个lambda表达式。

pfunc返回一个int *(*)(int, double *)型函数指针,即套在里面的小lambda。很显然,他的返回值是被转换成int*类型的nullptr

人类怎么能写出这样的代码呢?


一段古怪的代码
https://a48zhang.github.io/2023/04/18/一段古怪的代码/
作者
a48zhang
发布于
2023年4月18日
许可协议