在以下示例中:
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 ++进行编程而不是使用任何演员阵容?标准没有说HOW指针转换然后必须完成。
在我的生产代码中,我的问题在于使用预定义数据结构中的文件中的 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应该怎么做?
那么有任何解决方案吗?
编辑:彼得和Q.Q.答案都是正确的,似乎MISRA真的想要做任何事情而没有任何演员阵容,如果项目处于最后阶段很难做到。有两种选择:1 - 逐一记录MISRA偏差并解释为什么演员阵容好,解释如何测试(Q.Q.建议)
2 - 使用char类型的字节数组为file.read(),然后在安全地读取文件内容后将字节数组转换为标题内容,这必须逐个为每个成员完成,因为如果你转换char * to int32_t这再次违反规则5-2-7。有时候工作太多了。
答案 0 :(得分:1)
将不相关的指针转换为char*
不是一个好习惯。
但是,如果您经常使用大型遗留代码库,则可以通过添加特殊注释来禁止规则。
答案 1 :(得分: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 *
传递给std::istream::read()
作为第一个参数,将int32_t
作为第二个参数传递。两者都是不正确的。实际参数的类型为char *
和std::streamsize
(可能是,但不一定是int32_t
)。
答案 2 :(得分:0)
fread
是一个非常好的文件输入C ++函数,它使用void*
,MISRA允许。
它也擅长读取二进制数据,不像fstream
通过本地化字符转换逻辑处理所有数据(这是iostream上的&#34; facet&#34;可配置,但标准没有定义实现无操作转换的任何可移植方式。
C ++风格的fopen
/ fclose
在C ++程序中是不幸的,因为您可能忘记清理文件。幸运的是,我们有std::unique_ptr
可以将RAII功能添加到任意指针类型。使用std::unique_ptr<FILE*, decltype(&fclose)>
并在C ++中具有快速异常安全的二进制文件I / O.
注意:一个常见的误解是std::ios::binary
提供了二进制文件I / O.它不是。它所影响的只是换行符转换和(在某些系统上)文件结束标记处理,但对facet驱动的字符转换没有影响。