【问题标题】:MISRA incrementation in CC中的MISRA增量
【发布时间】:2015-07-26 04:33:35
【问题描述】:

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

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

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

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

【问题讨论】:

  • 因为(*buffPtr)++ 是错误的。你的意思是*buffPtr++ = 0xFF;
  • @mch 我想这是问题正文中的错字。查看代码中的注释。

标签: c pointers embedded misra


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 原始代码绝对是规则的“变通”,而不是因为它们而“改进”。 for 循环是正确的模式。
  • 是的,想想看,我猜memset 本身甚至不符合 MISRA。因此,即使在可行的情况下,这也不是 MISRA 的正确方法。
【解决方案2】:

(*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 循环示例将是最常见且易于理解的构造。

【讨论】:

  • 更正:MISRA-C:2012 允许 ++ 但不允许 +。它与 MISRA-C:2004 中引用的规则不同且更宽松。
【解决方案3】:

在 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 = &amp;buffPtr[1]; 是一种错误的规避规则 17.4 的尝试,因为该规则没有多大意义。相反,程序员决定混淆他们的程序,使其可读性降低,因此安全性降低。

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

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

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

【讨论】:

  • 我判断该规则的意图是要求所有指针对象标识分配的开始,这是其他语言(例如标准 Pascal)中的语义限制。具有这种语义限制的 C 方言将无法完成 C 中可以完成的所有操作,但这种方言的实现可以支持数组边界检查,这种方式在“全能”C 中是不可能的. 具有编译器支持的数组边界检查可能有助于某些需要故障安全的系统。
猜你喜欢
  • 2021-11-28
  • 1970-01-01
  • 2012-12-29
  • 2012-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-17
相关资源
最近更新 更多