C 表达式 ((void(*)(void))0)(); 的含义是什么?

22

((void(*)(void))0)();

我们将整数0强制转换为了一个棘手的类型(void(*))(void),然后执行它。来源声称这应该是有效的,但实际上它做了什么呢?

我想这一定是像#define TRUE FALSE那样的C语言笑话吧。


4
哪个来源声称这应该有效?你有链接吗?在上下文中可能更容易理解。 - anatolyg
1
这是将其转换为函数指针。在我看来像是一个等待发生段错误的情况。 - jim mcnamara
5
非标准的 abort(),也许? ;) - Guido
1
Jekyll的回答启发我问你,你的问题是否在嵌入式系统的背景下出现? - alk
1
这是在开玩笑的情况下提到的:if(cmd == RESET) // 如果命令是RESET ((void (*)(void))address)(); // 来自黑魔法的某些东西 - user2984878
显示剩余2条评论
5个回答

34
这是一个不需要参数并且不返回任何值的函数:
void f(void)

这是一个指向不接受参数且不返回值的函数的指针:
void (*p)(void)

这是那个指针的类型

void (*)(void) /* just remove the p! */

这是括号中的类型:

(void (*)(void))

这是将值转换为括号中指定的类型的语法(括号中是类型,后面跟着一个值):

(void (*)(void))0

还跟上我了吗?到目前为止,我们将整数值0转换为一个指向不带参数且返回值为空的函数指针。

这个转换是一个具有函数指针类型的表达式。当你拥有其中之一时,可以像这样调用它:

(your expression here)(arguments to the function)

第一组括号仅用于优先级,有时可能不需要(但这次需要)。最终结果:

((void (*)(void))0)(/* no args */);

将值0赋给一个指向不接受参数且返回值为空的函数指针,然后调用该函数,不提供任何参数。

2
你也可以添加:0 是函数(不带参数且返回 void)所在的地址。 - AlphaGoku
请参考以下链接了解有关函数指针的更多信息:https://dev59.com/nnRA5IYBdhLWcg3wwwvD - Cutton Eye

17

将地址强制转换为函数指针并调用它的语法如下:

((void (*)(void))address)();

但可能更清晰的做法是这样:

void (*fptr)(void) = (void (*)(void))address;
fptr();
据说((void(*)(void))0)();指令通常用于跳转到固件中的地址0。虽然它实际上是调用了地址0而不是跳转,但在实际应用中并没有区别(设备将会执行热重启)。

请问 "fw" 是什么意思? - alk
3
固件中,引导地址通常为0(或arm或i386的0xFFFF0000),此跳转(调用)将导致处理器热重启。如果您想获取更多信息,请查找跳转到复位向量 - Jekyll
@ShafikYaghmour 我会找到它的,在此期间你可以在http://learn.adafruit.com/system/assets/assets/000/010/293/original/ATmegaBOOT_168.c看到Arduino的一个小变体。请注意,这种重置不太适用于大型控制器(如ARM11或更新版本),其中通常会触发内部看门狗以实现更安全的硬件重置。 - Jekyll

10

这将NULL视为函数指针并执行它,在大多数系统上应该会引发sigbus或类似的错误。

void(*)(void)   <- type, function pointer taking no arguments and returning no value
(void(*)(void)) <- cast to above type
((...)0)        <- cast NULL/0 to said type
((...)0)()      <- execute the cast value as a function

3
AVR通常会将用户应用程序加载到地址0。引导加载程序则会加载到更高的地址,并可能跳转到(调用)地址0处的函数。因此,在这种环境下,对地址0进行强制转换和调用是一个有用的结构。

3
在一些嵌入式系统中(例如 AVR 微控制器),这可能是实现跳转(实际上是调用)重置向量的方法。
如果在这之前禁用了中断,您可以通过这种方式重新启动软件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接