13

在调试一些嵌入式代码时,我遇到了这样的事情:

buffPtr = &a[5];
buffEndPtr = &a[10];

while (buffPtr != buffEndPtr) 
{ 
    *buffPtr = 0xFF; 
    buffPtr  = &buffPtr[1];         /*  MISRA improvement for: buffPtr++ */ 
}

为什么这个构造会比 (*buffPtr)++ 有所改进?

4

3 回答 3

12

有一条 MISRA 规则规定唯一允许的指针数学是索引操作。

您展示的模式是一个执行不力的解决方法。这是丑陋/奇怪/不常见的,可能是基于对该规则目的的误解。它也可能违反另一条规则。

编写此代码的更好方法是:

for(i=5; i < 10; i++)
{
    a[i] = 0xff;
}

2015-05-20 更新 - 由于这是公认的答案,因此违反了实际规则,由 embedded.kyle 提供:

MISRA-C:2004,规则 17.4(必需)或 MISRA-C:2012,规则 18.4(必需)数组索引应是唯一允许的指针算术形式。

于 2015-05-14T14:44:52.717 回答
10

违反的规则(*buffPtr)++是:

MISRA-C:2004,规则 17.4(必需)或 MISRA-C:2012,规则 18.4(必需)

数组索引应该是唯一允许的指针算术形式。

他们在这条规则背后的推理:

使用数组下标语法的数组索引ptr[expr]是指针运算的首选形式,因为它通常比指针操作更清晰,因此更不容易出错。任何显式计算的指针值都有可能访问意外或无效的内存地址。数组索引也可能出现这种行为,但下标语法可以简化手动检查的任务。

C 中的指针运算可能会让新手感到困惑 表达式 ptr+1可能会被错误地解释为1ptr. 事实上,新的内存地址取决于指针目标的字节大小。sizeof如果应用不正确,这种误解可能会导致意外行为。

MISRA 的许多规则都有类似的理由。基本上,他们的思考过程是,如果您尽可能简单和明确地编写代码,代码将更具可读性和可维护性,因此会产生本质上更安全的代码。更安全的代码是 MISRA 标准背后的目的。

正如 Brian 指出的那样,有一些方法可以编写符合 MISRA 但仍然违反规则背后的意图的代码。在我看来, Brian 的for循环示例将是最常见且易于理解的构造。

于 2015-05-14T15:20:41.300 回答
5

在 MISRA-C:2004 规则 17.4 中有一条禁止所有形式的指针算术的咨询规则。意图是好的,该规则的目的是试图禁止潜在危险的代码,例如:

stuff* p; 
p = p + 5; // 5 stuff, not 5 bytes, bug or intentional?

和难以阅读的代码,例如

*(p + 5) = something;  // harder to read but equivalent to p[5]

通常,目的是推荐在循环指向数据时使用整数迭代器而不是指针算法。

但是,该规则还禁止了各种可能不危险的基本指针操作,例如ptr++. 一般来说,规则太严格了。

在 MISRA-C:2012 中,这条规则 (18.4) 被放宽,只禁止+ - += -=运营商。


就您而言,这buffPtr = &buffPtr[1];是规避规则 17.4 的错误尝试,因为该规则没有多大意义。相反,程序员决定混淆他们的程序,使其可读性降低,因此安全性降低。

处理此问题的正确方法是使用 ++ 运算符并忽略规则 17.4。这是一条建议性规则,因此不需要做任何偏差(除非本地 MISRA-C 实施出于某种原因另有说明)。如果您确实需要偏离,您可以简单地说该规则对 ++ 运算符没有任何意义,然后参考 MISRA-C:2012 18.4。

(当然,将整个循环重写为另一个答案所示的for循环是最好的解决方案)

不使用常识进行编程总是非常危险的,就像盲目地遵循 MISRA 而不了解规则背后的合理原理,或者在这种情况下缺乏此类原理。

于 2015-05-18T09:25:50.447 回答