修复取消引用类型惩罚指针将破坏严格别名

时间:2012-01-11 18:28:29

标签: c strict-aliasing type-punning

我正在尝试使用GCC编译特定程序时修复两个警告。警告是:

警告:解除引用类型惩罚指针将中断 严格别名规则[-Wstrict-aliasing]

和两个罪魁祸首是:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

incoming_buf outgoing_buf 的定义如下:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

这似乎与我一直在研究的那个警告的其他例子略有不同。我宁愿修复问题而不是禁用严格别名检查。

有许多使用工会的建议 - 对于这种情况,什么可能是合适的工会?

8 个答案:

答案 0 :(得分:39)

首先,让我们来看看为什么会收到别名违规警告。

别名规则只是说您只能通过自己的类型,签名/无符号变体类型或字符类型(charsigned char来访问对象,unsigned char)。

C表示违反别名规则会调用未定义的行为(所以不要!)。

在你的计划的这一行:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

虽然incoming_buf数组的元素属于char类型,但您正在以unsigned int的形式访问它们。实际上,表达式*((unsigned int*)dcc->incoming_buf)中的解除引用运算符的结果是unsigned int类型。

这违反了别名规则,因为您只有权访问incoming_buf数组的元素(请参阅上面的规则摘要!)charsigned char或{{ 1}}。

请注意,您在第二个罪魁祸首中存在完全相同的别名问题:

unsigned char

您通过*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); 访问char的{​​{1}}元素,因此存在别名冲突。

建议的解决方案

要解决您的问题,您可以尝试直接在要访问的类型中定义数组的元素:

outgoing_buf

(顺便说一下unsigned int的宽度是实现定义的,所以如果你的程序假定unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)]; unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)]; 是32位的话,你应该考虑使用unsigned int

这样,您可以通过类型uint32_t访问元素,在不违反别名规则的情况下将unsigned int个对象存储在数组中,如下所示:

unsigned int

char

修改

我完全重写了我的答案,特别是我解释了为什么程序从编译器获得别名警告。

答案 1 :(得分:20)

要解决此问题,请不要使用双关语!读取类型T的唯一“正确”方法是分配类型T并在需要时填充其表示形式:

uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);

简而言之:如果你想要一个整数,你需要做一个整数。没有办法以语言宽容的方式欺骗​​它。

允许的唯一指针转换(通常用于I / O)是将 T类型的现有变量的地址视为{{1}或者更确切地说,作为指向大小为char*的字符数组的第一个元素的指针。

答案 2 :(得分:4)

union
{
    const unsigned int * int_val_p;
    const char* buf;
} xyz;

xyz.buf = dcc->incoming_buf;
unsigned int received_size = ntohl(*(xyz.int_val_p));

简化说明 1. c ++标准声明你应该自己尝试对齐数据,g ++需要额外的努力来产生关于这个主题的警告。 2.只有在完全理解架构/系统和代码内部的数据对齐时才应该尝试它(例如,上面的代码在Intel 32/64上是肯定的;对齐1; Win / Linux / Bsd / Mac) 3.使用上述代码的唯一实际原因是避免编译器警告,WHEN和IF你知道你在做什么

答案 3 :(得分:0)

如果我可以,恕我直言,对于这种情况,问题是ntohl和htonl及相关功能API的设计。它们不应该用数字返回写成数字参数。 (是的,我理解宏优化点) 他们应该被设计为' n' side是指向缓冲区的指针。完成此操作后,整个问题就会消失,并且无论主机是哪个端点,例程都是准确的。 例如(没有尝试优化):

inline void safe_htonl(unsigned char *netside, unsigned long value) {
    netside[3] = value & 0xFF;
    netside[2] = (value >> 8) & 0xFF;
    netside[1] = (value >> 16) & 0xFF;
    netside[0] = (value >> 24) & 0xFF;
};

答案 4 :(得分:0)

如果您有不允许更改源对象类型的原因(例如在我的情况下),并且您完全有信心代码是正确的,并且可以使用该char数组进行操作,为避免警告,您可以执行以下操作:

unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);

答案 5 :(得分:0)

我最近将一个项目从GCC 6升级到了GCC 9,并开始看到此警告。该项目位于32位微控制器上,我创建了一个结构来访问32位机器寄存器的各个字节:

struct TCC_WEXCTRL_t
{
    byte    OTMX;
    byte    DTIEN;
    byte    DTLS;
    byte    DTHS;
};

然后编码:

((TCC_WEXCTRL_t *)&TCC0->WEXCTRL)->DTLS = PwmLoDeadTime;

会在新的编译器中产生警告。我发现我可以通过将结构体与原始类型结合在联合体中来消除警告:

union TCC_WEXCTRL_t
{
    TCC_WEXCTRL_Type std;
    struct  
    {
        byte    OTMX;
        byte    DTIEN;
        byte    DTLS;
        byte    DTHS;
    };    
};

其中TCC_WEXCTRL_Type是制造商的头文件中提供的WEXCTRL成员的类型。

我不确定这是否被视为完全符合要求的修复程序,或者GCC是否只是未能捕获到该修复程序。如果这没有用(或者被其他GCC升级所困扰),我将继续使用指针类型的并集,如实名对此线程所述。

答案 6 :(得分:0)

C转换不起作用,但是reinterpret_cast <>在类似情况下也帮助了我。

答案 7 :(得分:-2)

将指针转换为无符号,然后返回指针。

unsigned int received_size = ntohl(*((unsigned *)((unsigned)dcc-&gt; incoming_buf)));