调试时遇到一段代码,精简后如下:
double val = 1 / (0 * 1.0);
uint32_t result = (uint32_t) val;
在Keil环境下(armcc v5.06)下,变量val = 0,result = 0,在Gcc环境下(arm-none-eabi-gcc v6.3.1)下,val = Inf(一个很大的数),result = 0xFFFFFFFF。
除法运算,除数不能为0。如果除数为0,该行为未定义(Undefined):
C99 6.5.5p5 – The result of the / operator is the quotient from the division of the first operand by the second; the result of the % operator is the remainder. In both operations, if the value of the second operand is zero, the behavior is undefined. [link]
实践中,如果被除数是0,编译器会给出警告(Warning),但是上面代码里( 0 * 1.0 )的操作骗过了编译器的检查。
于是,Keil和Gcc两个不同平台的编译器给出了不同的结果。
猜测虽然C语言的规范没有定义,但具体到编译器身上,还是要给出一个数值结果——Keil给0,Gcc给Inf。
这种基础运算经常出现在库函数里不起眼的角落,并且会被重重嵌套。这个例子背后真实的代码是Thingy52里面sx1509的驱动代码[link]:
uint32_t fade_in_time_low_mult_reg =
(uint32_t) round(
(real_val->fade_in_time_ms /
(REG_RISEFALL_TIME_LOW_MULTIPLIER *
(reg_val->on_intensity - (4 * reg_val->off_intensity)) *
(255 / m_clkx_tics_pr_sec))) / 1000);
这一行代码中涉及到了多种数据类型:uint8_t,整形宏,float,double, uint32_t,此外还有常量。不同类型数据混合运算,会发生隐式类型转换,代码中也同时使用了显式类型转换。这些东西混在一起写成一行,很容易产生BUG。
(完)