修复取消引用类型双关指针将破坏严格别名的问题

发布于 2024-12-26 10:11:14 字数 665 浏览 5 评论 0原文

我正在尝试修复使用 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];

这似乎与其他规则略有不同我一直在研究该警告的例子。我更愿意解决问题而不是禁用严格别名检查。

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

I'm trying to fix two warnings when compiling a specific program using GCC. The warnings are:

warning: dereferencing type-punned pointer will break
strict-aliasing rules [-Wstrict-aliasing]

and the two culprits are:

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

and

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

incoming_buf and outgoing_buf are defined as follows:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

This seems subtly different than the other examples of that warning I've been examining. I would prefer to fix the problem rather than disable strict-aliasing checks.

There have been many suggestions to use a union - what might be a suitable union for this case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

拿命拼未来 2025-01-02 10:11:14

首先,让我们检查一下为什么会收到别名冲突警告。

别名规则简单地说,您只能通过其自己的类型、其有符号/无符号变体类型或通过字符类型(charsigned char无符号字符)。

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

在程序的这一行中:

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

虽然 incoming_buf 数组的元素是 char 类型,但您将它们作为 unsigned int 访问。事实上,表达式 *((unsigned int*)dcc->incoming_buf) 中解引用运算符的结果是 unsigned int 类型。

这违反了别名规则,因为您只有权通过(请参阅上面的规则摘要!)charsigned char 访问 incoming_buf 数组的元素无符号字符

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

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

您通过 unsigned int 访问 outgoing_bufchar 元素,因此这是别名冲突。

建议的解决方案

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

unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];

顺便说一下unsigned int的宽度是实现定义的,因此如果您的程序假定 unsigned int 是 32 位,则应考虑使用 uint32_t

这样,您就可以通过 char 类型访问元素,在数组中存储 unsigned int 对象,而不会违反别名规则,如下所示:

*((char *) outgoing_buf) =  expr_of_type_char;

char_lvalue = *((char *) incoming_buf);

编辑:

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

First off, let's examine why you get the aliasing violation warnings.

Aliasing rules simply say that you can only access an object through its own type, its signed / unsigned variant type, or through a character type (char, signed char, unsigned char).

C says violating aliasing rules invokes undefined behavior (so don't!).

In this line of your program:

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

although the elements of the incoming_buf array are of type char, you are accessing them as unsigned int. Indeed the result of the dereference operator in the expression *((unsigned int*)dcc->incoming_buf) is of unsigned int type.

This is a violation of the aliasing rules, because you only have the right to access elements of incoming_buf array through (see rules summary above!) char, signed char or unsigned char.

Notice you have exactly the same aliasing issue in your second culprit:

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

You access the char elements of outgoing_buf through unsigned int, so it's an aliasing violation.

Proposed solution

To fix your issue, you could try to have the elements of your arrays directly defined in the type you want to access:

unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];

(By the way the width of unsigned int is implementation defined, so you should consider using uint32_t if your program assumes unsigned int is 32-bit).

This way you could store unsigned int objects in your array without violating the aliasing rules by accessing the element through the type char, like this:

*((char *) outgoing_buf) =  expr_of_type_char;

or

char_lvalue = *((char *) incoming_buf);

EDIT:

I've entirely reworked my answer, in particular I explain why the program gets the aliasing warnings from the compiler.

仅冇旳回忆 2025-01-02 10:11:14

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

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

简而言之:如果您想要一个整数,则需要做一个整数。没有办法以语言纵容的方式来欺骗这一点。

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

To fix the problem, don't pun and alias! The only "correct" way to read a type T is to allocate a type T and populate its representation if needed:

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

In short: If you want an integer, you need to make an integer. There's no way to cheat around that in a language-condoned way.

The only pointer conversion which you are allowed (for purposes of I/O, generally) is to treat the address of an existing variable of type T as a char*, or rather, as the pointer to the first element of an array of chars of size sizeof(T).

(り薆情海 2025-01-02 10:11:14
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. 使用上述代码的唯一实际原因是避免编译器警告,当您知道自己在做什么时

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));

Simplified explanation
1. c++ standard states that you should attempt to align data yourself, g++ goes an extra mile to generate warnings on the subject.
2. you should only attempt it if you completely understand the data alignment on your architecture/system and inside your code (for example the code above is a sure thing on Intel 32/64 ; alignment 1; Win/Linux/Bsd/Mac)
3. the only practical reason to use the code above is to avoid compiler warnings , WHEN and IF you know what you are doing

辞取 2025-01-02 10:11:14

恕我直言,对于这种情况,问题在于 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;
};

If I may, IMHO, for this case, the problem is the design of the ntohl and htonl and related function APIs. They should not have been written as numeric argument with numeric return. (and yes, I understand the macro optimization point)
They should have been designed as the 'n' side being a pointer to a buffer. When this is done, the whole problem goes away and the routine is accurate whichever endian the host is.
For example (with no attempt to optimize):

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;
};
溺深海 2025-01-02 10:11:14

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

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

If you have reasons that do not allow you to change type of source object (like it was in my case), and you are absolutely confident that the code is correct and it does what intended to do with that char array, to avoid warnings you may do the following:

unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
短叹 2025-01-02 10:11:14

我最近将一个项目从 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 升级中陷入困境),我将继续使用指针类型的联合,如 Real Name 在此线程中所述。

I recently upgraded a project from GCC 6 to GCC 9, and started seeing this warning. The project is on a 32-bit microcontroller, and I had created a struct to access the individual bytes of a 32-bit machine register:

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

and then coded:

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

which produced the warning in the new compiler. I found I could eliminate the warning by combining my struct in a union with the original type:

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

where TCC_WEXCTRL_Type is the type of the WEXCTRL member as provided in the manufacturer's header files.

I'm not sure if this is considered a fully compliant fix, or if GCC is just failing to catch it. If this hadn't worked (or gets caught in another GCC upgrade), I would move on to using a union of the pointer types, as described on this thread by Real Name.

满栀 2025-01-02 10:11:14

如果您确定自己知道自己在做什么,请执行以下操作:

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

或只是:

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

If you are certain you know what you are doing, do this:

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

or just:

unsigned int received_size = ntohl (*((unsigned int*) ((void *) dcc->incoming_buf)));
救星 2025-01-02 10:11:14

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

无符号 int receive_size = ntohl (*((无符号 *)((无符号) dcc->incoming_buf)) );

Cast pointer to unsigned and then back to pointer.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文