5

在以下示例中:

bool bad_function()
{  
  char_t * ptr = 0;

  // MISRA doesn't complains here, it allows cast of char* to void* pointer
  void* p2 = ptr;

  // the following 2 MISRA violations are reported in each of the casts bellow (two per code line)
  // (1) Event misra_violation:     [Required] MISRA C++-2008 Rule 5-2-7 violation: An object with pointer type shall not be converted to an unrelated pointer type, either directly or indirectly
  // (1) Event misra_violation:     [Required] MISRA C++-2008 Rule 5-2-8 violation: An object with integer type or pointer to void type shall not be converted to an object with pointer type
  ptr = (char_t*) (p2); 
  ptr = static_cast<char_t*> (p2); 
  ptr = reinterpret_cast<char_t*> (p2); 

  return true;
}

报告了 MISRA 5-2-8 和 5-2-7 违规行为。

我怎样才能消除这种违规行为?

我需要有 C++ 静态分析经验的人来帮助我。几天以来,我一直在用这种愚蠢的规则打我的头。

根据 MISRA C++ 标准(MISRA-Cpp-2008.pdf: 规则 5-2-7(必需):具有指针类型的对象不得直接或间接转换为不相关的指针类型。

好的,但是我们有很多代码,例如需要将地址转换为char*然后与它一起使用std::ifstream,该read(char* buffer, int length)函数需要将地址类型转换为 ( char_t*)。那么根据 MISRA 人的说法,有人可以用 C++ 编程而不使用任何演员表吗?标准没有说必须如何进行指针转换。

在我的生产代码中,我的问题在于使用从预定义数据结构中的文件读取std:ifstream的文件读取操作:

if (file.read((char_t*)&info, (int32_t)sizeof(INFO)).gcount() != (int32_t)sizeof(INFO)
{
                LOG("ERROR: Couldn't read the file info header\n");
                res = GENERAL_FAILURE;
}

根据 MISRA 应该怎么做?

那么有什么解决方案吗?

编辑:彼得和 QQ 的答案都是正确的,似乎 MISRA 真的想在没有任何演员表的情况下做所有事情,如果项目处于最后阶段,这很难做到。有两种选择:

1 - 逐一记录 MISRA 偏差并解释为什么演员表是好的,解释这是如何测试的(QQ 建议)

2 - 使用 char 类型的字节数组作为 file.read(),然后在安全读取文件内容后将字节数组转换为标题内容,这必须为每个成员一一完成,因为如果您将 char* 转换为 int32_t 这再次违反规则 5-2-7。有时工作量太大。

4

3 回答 3

1

将不相关的指针转换char*为不是一个好习惯。但是,如果您有一个大型遗留代码库经常这样做,您可以通过添加特殊注释来抑制规则。

于 2015-12-09T15:30:23.520 回答
1

MISRA 规则的基本原因是将任何指针/地址转换为任何非空指针允许使用该地址,就好像它是与实际不同的对象一样。在这些情况下,编译器会抱怨隐式转换。使用类型转换(或 C++_cast运算符)基本上可以停止编译抱怨,并且 - 在太多情况下无法计算 - 取消引用该指针会产生未定义的行为。

换句话说,通过强制进行类型转换,您将引入潜在的未定义行为,并关闭编译器提醒您这种可能性的所有可能性。MISRA 认为这是一个坏主意....尽管许多从编码易用性方面考虑的程序员认为在某些情况下这是一个好主意。

您必须意识到 MISRA 检查的理念不像典型程序员那样关注编程的易用性,而是更关注防止未定义(或实现定义或未指定等)行为通过所有检查并导致代码“在野生”,可能会造成伤害。

问题是,在您的实际用例中,您依赖于file.read()正确填充(可能)名为info.

if (file.read((char_t*)&info, (int32_t)sizeof(INFO)).gcount() != (int32_t)sizeof(INFO)
{
            LOG("ERROR: Couldn't read the file info header\n");
            res = GENERAL_FAILURE;
}

您需要做的是更加努力地提供能够通过 MISRA 检查器的有效代码。就像是

std::streamsize size_to_read = whatever();
std::vector<char> buffer(size_to_read);  

if (file.read(&buffer[0], size_to_read) == size_to_read)
{
      //  use some rules to interpret contents of buffer (i.e. a protocol) and populate info
      // generally these rules will check that the data is in a valid form
      //   but not rely on doing any pointer type conversions
}
else
{
            LOG("ERROR: Couldn't read the file info header\n");
            res = GENERAL_FAILURE;
}

是的,我意识到这比简单地使用类型转换,并允许二进制保存和读取结构更多的工作。但他们是休息时间。除了通过 MISRA 检查器之外,如果操作正确,这种方法还有其他优点,例如文件格式完全独立于用于构建代码的编译器。您的代码取决于实现定义的数量(结构中成员的布局, 的结果sizeof),因此您的代码 - 如果使用编译器 A 构建 - 可能无法读取由编译器 B 构建的代码生成的文件。其中一个MISRA 要求的共同主题是减少或消除任何行为可能对实现定义的数量敏感的代码。

注意:您还传递char_t *了 tostd::istream::read()作为第一个参数和 anint32_t作为第二个参数。两者实际上都不正确。实际参数的类型是char *and std::streamsize(可能是,但不一定是int32_t)。

于 2015-12-14T13:11:05.257 回答
0

fread是一个非常好的用于文件输入的 C++ 函数,它使用void*MISRA 允许的 .

它还擅长读取二进制数据,不像fstream通过本地化字符转换逻辑处理所有数据(这是 iostream 上的“方面”,它是可配置的,但标准没有定义任何可移植的方式来实现无操作转换)。

fopen/的 C 风格fclose在 C++ 程序中是不幸的,因为您可能会忘记清理文件。幸运的是,我们有这个std::unique_ptr可以将 RAII 功能添加到任意指针类型。在 C++ 中使用std::unique_ptr<FILE*, decltype(&fclose)>具有快速异常安全的二进制文件 I/O。


注意:一个常见的误解是std::ios::binary提供二进制文件 I/O。它不是。它所影响的只是换行符转换和(在某些系统上)文件结束标记处理,但对方面驱动的字符转换没有影响。

于 2015-12-09T15:42:27.270 回答