[MISRA C 2004](三)防范表达式的失控
1.1 表达式的求值顺序
1.1.1 表达式的值必须在任何求值顺序下保持一致
考虑下面两段程序。
程序一:执行以下程序,从串口依次输入2 和4 ,变量result 将等于多少?
/* 注:uart_GetChar是从串口接收一个ASCII字符的函数 */ uint8_t result; result = uart_GetChar() – uart_GetChar(); |
程序二:执行以下程序,变量result将等于多少?
uint8_t result; uint8_t temp = 2; result = temp+++--temp; |
由于C语言标准规定的只是运算符的结合顺序,而对于二元运算符的求值顺序未作定义,“对于二元操作符,要先对两个操作数进行求值之后(但未指定求值顺序)再进行运算”。因此上述两段程序的最终结果取决于编译器特性。
如果使用的编译器总是从左向右解析表达式,则结果是:
程序一:result = ‘2’ – ‘4’ = 0x32 – 0x34 = 0xFE;
程序二:result = 2 + 2 = 4;
如果使用的编译器总是从右向左解析表达式,则结果是:
程序一:result = ‘4’ – ‘2’ = 0x34 – 0x32 = 0x02;
程序二:result = 1 + 1 = 2;
1.1.2 应该减少表达式对运算符优先级的依赖性
考虑下面一段程序:
if (STATRegister & BUSYMask = = 0) { do_something ; } |
该程序的本意是读状态寄存器( STATRegister ) 的值,如果其BUSY位是0 (即被掩码BUSYMask 选中) ,则做某些操作。但是,由于判等运算符“= = ”的优先级高于位与操作符“&”,实际的判断表达式变成了“( STATReg2ister & (BUSYMask = = 0) ) ! = 0”。此时应该加入括号“() ”以保证判断表达式的正确性:
if ( (STA TRegister & BUSYMask ) = = 0) { do_something ; } |
1.2 表达式的副作用
1.2.1 不允许将sizeof运算符作用于有副作用的表达式上
考虑下面一段程序:
uint32_t i; uint16_t j; j = sizeof(i = 1234); |
本意是先将1234 赋给i ,再把i 所占用的空间大小传给j 。可是由于sizeof 运算符只针对数据类型进行操作, 所以“j = sizeof (i = 1234) ”实际上被替换成“j = sizeof(int32_t) ”。故表达式“i = 1234”的操作不会进行,这就带来了可能的隐患。正确的做法是将最后一句替换成:
i = 1234 ;
j = sizeof (i) ;
1.2.2 逻辑运算(&&和||)的右操作数不允许包含副作用
在C 语言的表达式中,部分代码可能不被求值。如果这些代码具有副作用,就会产生一些隐患。典型的例子出现在逻辑运算中:
if (istrue | | do_something_with_side_effects () )
{
do_something ;
}
如果istrue 非0 ,编译器认为表达式的值已经确定为真,从而不再进行后面的求值,于是有副作用的操作被忽略,影响了后继操作。