((void(*)(void))0)() 是 Exit 函数吗?

((void(*)(void))0)() is Exit function?

我试图在 Atmel AVR 微控制器上编写自己的引导加载程序。我引用了 github 中的代码库之一。我要感谢 ZEVERO

的代码库

在初级阶段,我了解代码库。但是在第 224 行我找到了一行 Reference to the code

**if (pgm_read_word(0) != 0xFFFF) ((void(*)(void))0)();   //EXIT BOOTLOADER**

我理解 if 条件部分,但是当我试图理解 true 语句部分时,即

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

code writer对此给出的解释是//EXIT BOOTLOADER

我的第一个问题是这个复杂的声明是什么意思 **((void(*)(void))0)();**

第二个问题是,是否退出微控制器中代码的执行。

它将简单地调用地址 0,就好像它是一个返回 void 且不带参数的函数一样。或者...更简单地说,地址是空指针的位模式。或者更简单地说,行为是未定义的,所以它可能会做任何意想不到的事情。

正如@iBug 指出的那样,((void(*)(void))0)(); 在 NULL 函数指针上调用函数。

实际上,这会将程序控制转移到内存地址 0。现在,在工作站上,这将是巨大的 UB,很可能会导致段错误。

但是,由于有问题的代码是针对硬件引导加载程序的,它不是 UB,它(显然)只是退出引导加载程序。

在硬件层面,几乎一切 都依赖于实现,几乎没有什么 是可移植的。您不能指望针对特定硬件平台的 C 代码以任何方式代表普遍接受的 C 模式和实践。

((void(*)(void))0)(); 尝试调用 NULL 函数指针。 AVR 微控制器的用户程序(不是引导加载程序)通常从地址 0 开始执行。AVR-GCC 的 ABI 使用 NULL 函数指针的全 0 位表示,因此此调用将(除其他事项外)将执行转移到用户程序。本质上,它作为 __asm__ __volatile__("jmp 0"); 的较慢版本工作,并假定用户程序的启动代码无论如何都会重新初始化堆栈指针。

通过 NULL 函数指针调用是未定义的行为,因此不能保证此技巧适用于其他编译器、更高版本的 GCC,甚至不同的优化设置。

调用前的if (pgm_read_word(0) != 0xFFFF)检查可能是为了确定是否存在用户程序:已擦除但未写入的程序内存字将读取为0xFFFF,而大多数程序以[=13开头=] 指令跳过中断向量的其余部分 table,并且 JMP 指令的第一个字永远不会是 0xFFFF。

如前所述,调用此函数只会导致跳转到地址 0。

由于此地址的代码通常不是由您自己的程序定义的,而是由特定环境定义的,因此行为完全取决于该环境。

您的问题被标记为 AVM/Atmel:在 AVR 上,跳转到地址 0 只会导致重启(与硬件复位几乎相同的行为,但要注意,MCU 将保持中断 enabled/disabled 状态而不是 "real" 重置)。 "cleaner" 程序可能希望使用看门狗定时器进行 "real" 重置(wdt_reset() 等)。