((void(*)(void))0)()是退出函数?

时间:2017-12-19 05:20:41

标签: c avr bootloader atmel

我试图在Atmel AVR微控制器上编写自己的启动加载程序。我已经从github引用了一个代码库。我要感谢ZEVERO

的代码库

在初级阶段我理解代码库。但是在第224行,我发现了一条线 Reference to the code

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

我理解if条件部分,但是当我试图理解真实的陈述部分时,即

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

代码编写者对此进行了解释 //退出自定义程序

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

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

4 个答案:

答案 0 :(得分:11)

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

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

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

在硬件级别,所有几乎都是依赖于实现的,并且几乎 nothing 都是可移植的。您不能指望针对特定硬件平台的C代码能够以任何方式代表普遍接受的C模式和实践。

答案 1 :(得分:6)

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

调用NULL函数指针是未定义的行为,因此无法保证此技巧可以与其他编译器,更高版本的GCC或甚至不同的优化设置一起使用。

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

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:-1)

它将简单地调用地址0,就好像它是一个返回void并且不带参数的函数。或者......简单地说就是空指针的位模式的地址。或者甚至不那么简单,行为是未定义的,因此它可能会做任何意外的事情。