尝试进行从 char 到 int 指针的类型转换时违反 MISRA C-2012 规则 11.3

MISRA C-2012 Rule 11.3 violation while trying to do a typecast from char to int pointer

我正在尝试从我的代码中删除规则 11.3。

示例代码:

static int32_t 
do_test(const char *cp)
{
    const char *c = cp;
    const int32_t *x;
    x = (const int32_t *)cp;

    return *x;
}

我希望*c 和*x 的值相同。即使代码正在编译并给出正确的值,"x = (int32_t *)cp;" 导致违反 11.3 和 11.8

违反规则 11.3:指针类型的对象不得转换为指向不同对象类型的指针。

我试过使用 void 指针,但结果与我预期的不一样,而且还导致了额外的违规。

有没有办法消除这些违规行为?

从 MISRA C 2012 文档中,他们提到此规则有一个例外,因为允许将指向对象类型的指针转​​换为指向对象类型 char、signed char 或 unsigned char 之一的指针。标准保证指向这些类型的指针可用于访问对象的各个字节。

由于字符类型而忽略 Dir 4.6。

如果您觉得需要避免显式强制转换,您可以随时执行 memcpy:

#include <string.h>
#include <stdint.h>

static int32_t 
do_test(const char *cp)
{
    int32_t r;
    memcpy(&r,cp,sizeof(r));
    return r;
}

使用内置 mempcy 的优化编译器,这应该和 return *(int32_t*)cp; 一样高效(您的代码,写得更简洁)。

请记住,无论哪种情况,只有当您传入的 cp 值指向有效的 int32_t 对象时,才会定义代码。

如果 memcpy 由于隐式 char*void* 强制转换而无法正常运行,您可以将其替换为定制的简单实现的 void charwise_memcpy(char *Dest, char const *Src, size_t Sz); 或等效的 for循环。

void charwise_memcpy(char *Dest, char const *Src, size_t Sz)
{
    for(size_t i=0; i<Sz; i++)
        Dest[i]=Src[i];
}

原始代码可能会导致未定义的行为:

const char *cp;
// ...
x = (const int32_t *)cp;

如果 int32_t 在平台上有对齐要求,而 cp 未针对该要求正确对齐,则行为未定义。

总的来说,我不是 MISRA 的粉丝,但这个特殊的例子似乎很有道理。即使您碰巧在没有对齐要求的平台上(即使在嵌入式开发中也不太可能),此代码也是不可移植的,并且如果您移动到确实有对齐要求的 CPU 可能会开始崩溃。

一个好的解决方案是使用 memcpy 而不是定义明确的:

你很幸运你使用的是 MISRA-C,因为这段代码充满了错误。你无法通过强制转换让错误消失。

  • 错误 1。字符指针不一定对齐,在这种情况下,您的代码会根据 C 标准 6.3.2.3/7 调用未定义的行为:

    A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

  • 错误 2。代码包含明显的严格别名违规。根据 C 标准 6.5/7,这始终是未定义的行为。

    您的假设 "The Standard guarantees that pointers to these types can be used to access the individual bytes of an object." 是正确的:作为一个特殊的异常,C 允许您将指向 x 的指针转换为指向 char 的指针,然后通过 char 指针访问数据。 反之则不然

    您的代码不是访问单个字节;您正相反,从字符数组到 32 位类型。这是不允许的。见 What is the strict aliasing rule?.


正确的代码,C语言和MISRA-C都应该没问题:

static int32_t do_test(const char *cp)
{
  return (int32_t) ((uint32_t)cp[0] << 24u) |
                   ((uint32_t)cp[1] << 16u) |
                   ((uint32_t)cp[2] <<  8u) |
                   ((uint32_t)cp[3]);
}

此 shift 版本始终是首选,因为它与字节序无关,因此可移植。强制转换为 uint32_t 是防止在 8/16 位系统上进行隐式提升所必需的,此外,您永远不应该对有符号类型进行位移。