相关信息: 如果出现运行时错误会怎样?
这个问题类似于上面的问题,但是这是另一种情况:
int pin = 999;
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
在这种情况下会发生什么?编译器可能会捕获它,但是如果您使用随机数,IDE会捕获它吗?
相关信息: 如果出现运行时错误会怎样?
这个问题类似于上面的问题,但是这是另一种情况:
int pin = 999;
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
在这种情况下会发生什么?编译器可能会捕获它,但是如果您使用随机数,IDE会捕获它吗?
Answers:
编译器将不会检测到任何错误,并且代码将编译并执行。因此,要了解发生了什么,我们需要探索幕后魔术。有关摘要,请跳至结尾。
代码的第二行是魔术发生的地方,那就是我们需要关注的地方。
pinMode(pin, OUTPUT);
pinMode
与该讨论相关的部分是:
void pinMode(uint8_t pin, uint8_t mode)
{
uint8_t bit = digitalPinToBitMask(pin); //The first instance where pin is used
uint8_t port = digitalPinToPort(pin);
if (port == NOT_A_PIN) return;
//Do something
}
(完整的实现可以在connection_digital.c中找到)
因此,这里digitalPinToBitMask
似乎正在使用它pin
来计算中间位。进一步研究的digitalPinToBitMask
是一个宏,Arduino.h
其中定义的宏是以下这种单行:
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
这种看起来很奇怪的衬里做的很简单。它索引数组中的第 P 个元素digital_pin_to_bit_mask_PGM
并返回它。该阵列digital_pin_to_bit_mask_PGM
在pins_arduino.h
所使用的特定板中定义,或在引脚图中定义。
const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {
_BV(0), /* 0, port D */
_BV(1),
_BV(2),
_BV(3),
_BV(4),
_BV(5),
_BV(6),
_BV(7),
...
};
这个数组总共有20个元素,所以我们很不走运。999将为该阵列之外的闪存中的存储器位置建立索引,从而导致不可预测的行为。还是会?
对于运行时无政府状态,我们还有另一道防线。其功能的下一行pinMode
:
uint8_t port = digitalPinToPort(pin);
digitalPinToPort
带我们走了一条类似的道路。与一起定义为宏digitalPinToBitMask
。其定义是:
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
现在,我们索引第P 个元素,digital_pin_to_port_PGM
该元素是在引脚映射中定义的数组:
const uint8_t PROGMEM digital_pin_to_port_PGM[] = {
PD, /* 0 */
PD,
....
PC,
PC,
};
该数组包含20个元素,因此999再次超出范围。同样,此命令从闪存中读取并返回一个我们无法确定其值的值。从此以后,这将再次导致不可预测的行为。
最后一道防线。那就是if
检查pinMode
返回值digitalPinToPort
:
if (port == NOT_A_PIN) return;
NOT_A_PIN
在中定义为0 Arduino.h
。因此,如果从返回的字节digitalPinToPort
恰好为零,pinMode
则将无提示地失败并返回。
无论如何,pinMode
不能使我们摆脱无政府状态。999注定会导致厄运。
TL; DR,代码将执行,并且其结果将不可预测。最有可能的是,没有引脚将设置为OUTPUT
,并且digitalWrite
将失败。如果您碰巧碰巧运气不好,则可以将随机图钉设置为OUTPUT
,并将其digitalWrite
设置为HIGH
。
uint8_t
因此它将首先通过代码调用转换为231 pinMode
。最终结果是相同的:pinMode
并且digitalWrite
将具有不可预测的行为,并且如果您使用错误的pin参数调用它们,可能会破坏内存的随机部分。
在标准库中,有用于将引脚转换为端口的宏,这些宏在汇编中使用。这是Arduino 1.0.5中的Uno:
#define digitalPinToPCICR(p) (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)0))
#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1))
#define digitalPinToPCMSK(p) (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)0))))
#define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 13) ? ((p) - 8) : ((p) - 14)))
还有更多,但我不会在这里显示。
我相信您的程序会从999中减去14,这对于brogram来说仍然太大。然后,它将尝试指向digital_pn_to_bit_mask_PGM
数组的第985个元素,该元素仅包含20个元素。这很可能最终通过指向progmem中的随机点来搞砸Arduino。