使用无效的密码时会发生什么?


9

相关信息: 如果出现运行时错误会怎样?

这个问题类似于上面的问题,但是这是另一种情况:

int pin = 999;
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);

在这种情况下会发生什么?编译器可能会捕获它,但是如果您使用随机数,IDE会捕获它吗?

Answers:


9

编译器将不会检测到任何错误,并且代码将编译并执行。因此,要了解发生了什么,我们需要探索幕后魔术。有关摘要,请跳至结尾。


代码的第二行是魔术发生的地方,那就是我们需要关注的地方。

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_PGMpins_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


有趣的是,没有边界检查。无论如何,digitalWrite是如此的缓慢和庞大,放入编译时或运行时检查都不会很尴尬。
Cyber​​gibbons

如果所有arduino引脚都在连续范围内,那么它们是否不能用port> = BOARD_MAX_PIN check代替port == not引脚检查,其中在某些头文件中基于检测板的ifdef定义了板最大引脚?
EternityForest 2014年

您忘记了999不能用a表示,uint8_t因此它将首先通过代码调用转换为231 pinMode。最终结果是相同的:pinMode并且digitalWrite将具有不可预测的行为,并且如果您使用错误的pin参数调用它们,可能会破坏内存的随机部分。
David Grayson

3

在标准库中,有用于将引脚转换为端口的宏,这些宏在汇编中使用。这是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。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.