42

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

警告:取消引用类型双关指针将破坏严格别名规则 [-Wstrict-aliasing]

两个罪魁祸首是:

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

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

传入_buf传出_buf定义如下:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

这似乎与我一直在研究的其他警告示例略有不同。我宁愿解决问题,也不愿禁用严格别名检查。

有很多使用联合的建议——对于这种情况,什么是合适的联合?

4

8 回答 8

62

首先,让我们检查一下为什么会收到混叠违规警告。

别名规则只是说您只能通过对象自己的类型、其有符号/无符号变体类型或通过字符类型 ( char, signed 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 charunsigned char

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

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

您访问through的char元素,因此这是一个别名冲突。outgoing_bufunsigned 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_tunsigned int

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

*((char *) outgoing_buf) =  expr_of_type_char;

或者

char_lvalue = *((char *) incoming_buf);

编辑:

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

于 2012-01-11T19:15:02.147 回答
29

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

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

简而言之:如果你想要一个整数,你需要做一个整数。没有办法以语言宽恕的方式作弊。

您被允许的唯一指针转换(通常用于 I/O)是将现有类型变量的地址T视为 a char*,或者更确切地说,将其视为指向大小为 chars 数组的第一个元素的指针sizeof(T)

于 2012-01-11T19:14:35.570 回答
6
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.使用上面代码的唯一实际原因是避免编译器警告,当你知道你在做什么时

于 2016-09-06T17:30:33.157 回答
2

如果您有理由不允许您更改源对象的类型(就像在我的情况下一样),并且您绝对确信代码是正确的并且它执行了该 char 数组的预期操作,以避免警告您可以执行以下操作:

unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
于 2019-02-27T11:53:03.120 回答
0

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

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;
};
于 2018-03-16T18:10:15.483 回答
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 升级),我将继续使用指针类型的联合,如实名在此线程中所述。

于 2020-10-13T01:15:56.767 回答
0

C cast 不起作用,但 reinterpret_cast<> 在类似的情况下帮助了我。

于 2020-12-17T00:25:13.390 回答
-2

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

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

于 2015-07-10T13:20:59.720 回答