执行变量的原始副本的简单转换是否会破坏严格别名?

发布于 2024-10-18 02:21:36 字数 1275 浏览 5 评论 0原文

我一直在阅读严格别名最近相当多。 C/C++ 标准规定以下代码无效(未定义的行为是正确的),因为编译器可能将 a 的值缓存在某处,并且在以下情况下无法识别需要更新该值:我更新b

float *a;
...
int *b = reinterpret_cast<int*>(a);
*b = 1;

该标准还规定 char* 可以为任何内容别名,因此(如果我错了,请纠正我)只要对 char* 变量进行写访问,编译器就会重新加载所有缓存的值被制成。因此,下面的代码是正确的:

float *a;
...
char *b = reinterpret_cast<char*>(a);
*b = 1;

但是当根本不涉及指针的情况下呢?例如,我有以下代码,GCC 向我抛出有关严格别名的警告。

float a = 2.4;
int32_t b = reinterpret_cast<int&>(a);

我想要做的只是复制 a 的原始值,因此不应该应用严格的别名。这里是否存在可能的问题,或者只是 GCC 对此过于谨慎?

编辑

我知道有一个使用memcpy<的解决方案/a>,但它会导致代码的可读性大大降低,所以我不想使用该解决方案。

EDIT2

int32_t b = *reinterpret_cast(&a); 也不起作用。

已解决

这似乎是GCC 中的一个错误

I've been reading about strict aliasing quite a lot lately. The C/C++ standards say that the following code is invalid (undefined behavior to be correct), since the compiler might have the value of a cached somewhere and would not recognize that it needs to update the value when I update b;

float *a;
...
int *b = reinterpret_cast<int*>(a);
*b = 1;

The standard also says that char* can alias anything, so (correct me if I'm wrong) compiler would reload all cached values whenever a write access to a char* variable is made. Thus the following code would be correct:

float *a;
...
char *b = reinterpret_cast<char*>(a);
*b = 1;

But what about the cases when pointers are not involved at all? For example, I have the following code, and GCC throws warnings about strict aliasing at me.

float a = 2.4;
int32_t b = reinterpret_cast<int&>(a);

What I want to do is just to copy raw value of a, so strict aliasing shouldn't apply. Is there a possible problem here, or just GCC is overly cautious about that?

EDIT

I know there's a solution using memcpy, but it results in code that is much less readable, so I would like not to use that solution.

EDIT2

int32_t b = *reinterpret_cast<int*>(&a); also does not work.

SOLVED

This seems to be a bug in GCC.

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

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

发布评论

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

评论(3

陈甜 2024-10-25 02:21:36

如果你想复制一些内存,你可以告诉编译器这样做:

编辑:添加了一个函数以获得更可读的代码:

#include <iostream>
using std::cout; using std::endl;
#include <string.h>

template <class T, class U>
T memcpy(const U& source)
{
    T temp;
    memcpy(&temp, &source, sizeof(temp));
    return temp;
}

int main()
{
    float f = 4.2;
    cout << "f: " << f << endl;

    int i = memcpy<int>(f);
    cout << "i: " << i << endl;
}

[代码]
[更新的代码]

编辑:正如用户/GMan 在评论中正确指出的那样,功能齐全的实现可以检查 TU 是否为 POD 。但是,鉴于该函数的名称仍然是 memcpy,您可以依赖开发人员将其视为与原始 memcpy 具有相同的约束。这取决于您的组织。另外,使用目标的大小,而不是源的大小。 (谢谢,奥利。)

If you want to copy some memory, you could just tell the compiler to do that:

Edit: added a function for more readable code:

#include <iostream>
using std::cout; using std::endl;
#include <string.h>

template <class T, class U>
T memcpy(const U& source)
{
    T temp;
    memcpy(&temp, &source, sizeof(temp));
    return temp;
}

int main()
{
    float f = 4.2;
    cout << "f: " << f << endl;

    int i = memcpy<int>(f);
    cout << "i: " << i << endl;
}

[Code]
[Updated Code]

Edit: As user/GMan correctly pointed out in the comments, a full-featured implementation could check that T and U are PODs. However, given that the name of the function is still memcpy, it might be OK to rely on your developers treating it as having the same constraints as the original memcpy. That's up to your organization. Also, use the size of the destination, not the source. (Thanks, Oli.)

以可爱出名 2024-10-25 02:21:36

基本上,严格的别名规则是“未定义使用与其声明的类型不同的另一种类型访问内存,字符数组除外”。所以,gcc并不过分谨慎。

Basically the strict aliasing rules is "it is undefined to access memory with another type than its declared one, excepted as array of characters". So, gcc isn't overcautious.

扮仙女 2024-10-25 02:21:36

如果这是您经常需要做的事情,您也可以使用联合,恕我直言,对于此特定目的,联合比强制转换或 memcpy 更具可读性:

union floatIntUnion {
  float a;
  int32_t b;
};

int main() {
  floatIntUnion fiu;
  fiu.a = 2.4;
  int32_t &x = fiu.b;
  cout << x << endl;
}

我意识到这并不能真正回答您有关严格别名的问题,但我认为这种方法使代码看起来更干净并且更好地表达您的意图。

并且还要意识到,即使正确地进行复制,也不能保证您得到的 int 会对应于其他平台上的相同 float,因此请计算任何网络/文件如果您计划创建跨平台项目,则这些浮点数/整数的 I/O 会被排除。

If this is something you need to do often, you can also just use a union, which IMHO is more readable than casting or memcpy for this specific purpose:

union floatIntUnion {
  float a;
  int32_t b;
};

int main() {
  floatIntUnion fiu;
  fiu.a = 2.4;
  int32_t &x = fiu.b;
  cout << x << endl;
}

I realize that this doesn't really answer your question about strict-aliasing, but I think this method makes the code look cleaner and shows your intent better.

And also realize that even doing the copies correctly, there is no guarantee that the int you get out will correspond to the same float on other platforms, so count any network/file I/O of these floats/ints out if you plan to create a cross-platform project.

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