有没有避免严格别名警告的方法?

3
我正在使用一个库(ENet),它使用回调函数。在这些回调函数中,它会传递一个结构体,该结构体包含一个 void* 类型的用户数据,供您自己使用。我想使用该变量,但不想将其作为指针使用。我不想为了指向它而分配内存,我更想直接使用 void* 的空间来存储 size_t 类型的值。

但是,正如预期的那样,当我将 void* 变量转换为 size_t 变量时,会收到严格别名警告。并且回调的结构体没有提供 union 以访问除 void* 以外的其他类型。

我知道可以完全禁用该警告,但我更愿意仅在此特定情况下将其静音。是否有一种方法可以编写这种类型的转换,使编译器知道这是有意的,以避免警告?

编辑:

这里是我要做的事情的示例。由于我需要能够编辑用户值,因此我将其转换为 size_t 类型,同时尝试获取对它的引用:

size_t& offset = reinterpret_cast<size_t&>(packet->userData);

这个代码能够工作,但会产生警告。


你在用哪种编程语言工作? - Andrew Barber
C++,使用C库。 - Nairou
你理解什么是严格别名规则吗?以及为什么违反它是一个坏主意?禁用警告可能会让你的代码变得疯狂:编译器可能会假设对 size_t& 的任何更改都不会导致 void*& 改变,并将 void*& 值缓存到寄存器中,忽略你的更改... - Yakk - Adam Nevraumont
这是C++代码,请移除'C'标签。 - user3629249
这似乎很危险。如果 sizeof(size_t) != sizeof(void*) 会怎样? - zmb
我的理解是,size_t保证是指针的大小。 - Nairou
1个回答

4

但是,正如预期的那样,当我将void*变量反引用为size_t变量时,会出现严格别名警告。

如果您只想使用void *来传输一个普通整数,则不需要对其进行反引用操作,而是需要将其转换为适当的整数类型(最好选择intptr_t,因为标准保证它可以通过void *进行往返传输)。

intptr_t magic=42;
register_callback(&myfunc, (void *)magic);

// ...

void myfunc(void *context)
{
    intptr_t magic=(intptr_t)context;
    // ...
}

如果您喜欢使用C++风格的转换,那么所有的都将是reinterpret_cast。此外,您可能在代码中做一些奇怪的事情,因为void*(像char*和unsigned char*一样)不受严格别名规则的约束(它们可以与任何其他指针别名)。更新。

Here is an example of what I'm trying to do. Since I need to be able to edit the user value, I'm casting it to size_t while also trying to grab a reference to it:

size_t& offset = reinterpret_cast<size_t&>(packet->userData);

This works, but gives the warning.

不行,即使假设和具有相同的大小和对齐要求(这并不保证),也不能以可移植的方式实现;将与别名化是不允许的(此外,这特别毒辣,因为它隐藏在引用中)。
如果你真的需要这样做,你必须与你的编译器妥协;例如,在gcc中,你可以使用-fno-strict-aliasing编译只有你拥有这个东西的文件,这样,代替简单地禁用警告(=将潜在问题隐藏在地毯下),它会禁用编译器对严格别名的假设,从而生成正确工作的代码,即使指向不相关类型的指针/引用指向相同的东西。

谢谢。我打错了,我正在进行一个类型转换,而不是解引用。如果有帮助的话,我会更新帖子以显示我实际想要做什么。 - Nairou
谢谢澄清!知道了这一点,我编辑了我的代码,将获取和更新变量分成了两个步骤,而不是使用引用。现在一切都好了! - Nairou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接