在调试一些嵌入式代码时,我遇到了类似这样的事情:
buffPtr = &a[5];
buffEndPtr = &a[10];
while (buffPtr != buffEndPtr)
{
*buffPtr = 0xFF;
buffPtr = &buffPtr[1]; /* MISRA improvement for: buffPtr++ */
}
为什么这个结构会比(* buffPtr)++改进?
答案 0 :(得分:12)
有一条MISRA规则规定允许的唯一指针是索引操作。
您展示的模式是执行不良的解决方案。这是丑陋/怪异/不常见,可能是基于对该规则的目的的误解。它也可能违反另一条规则。
编写此代码的更好方法是:
if(cartime<=first){
second=first;
secode=firstcode;
first=cartime;
firstcode=carcode;
}
更新2015-05-20 - 由于这是接受的答案,这是违反的实际规则,由embedded.kyle提供:
MISRA-C:2004,规则17.4(必填)或MISRA-C:2012,规则18.4(必填) 数组索引应该是唯一允许的指针算法形式。
答案 1 :(得分:10)
(*buffPtr)++
违反的规则是:
MISRA-C:2004,规则17.4(必填)或MISRA-C:2012,规则18.4(必填)
数组索引应该是唯一允许的指针算法形式。
他们在这条规则背后的推理:
使用数组下标语法
ptr[expr]
进行数组索引是 指针算术的首选形式,因为它通常更清晰 因此比指针操作更不容易出错。任何明确的 计算的指针值有可能无意中访问或 内存地址无效。阵列也可以实现这种行为 索引,但下标语法可以简化人工审查的任务。C中的指针运算可能会让新手混淆表达式
ptr+1
可能被错误地解释为1
添加到{。}}ptr
中的地址。实际上新的内存地址取决于 指针目标的大小(以字节为单位)。这种误解可能导致 如果sizeof
未正确应用,则会出现意外行为。
MISRA的许多规则都有类似的理由。基本上他们的思维过程是,如果你尽可能简单明了地编写代码,代码将更易读和可维护,从而导致本身更安全的代码。更安全的代码是MISRA标准背后的目的。
正如Brian指出的那样,有一些方法可以编写符合MISRA标准的代码,但仍然违反规则背后的意图。在我看来,Brian的for
循环示例将是最常见且易于理解的构造。
答案 2 :(得分: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而不理解规则背后的合理理由,或者在这种情况下缺乏这样的规则。