((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()
等)。
我试图在 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()
等)。