现代 C++ 编译器会在类型转换后优化赋值吗?
Do modern c++ compilers optimize assignments after type casting?
取以下代码:
char chars[4] = {0x5B, 0x5B, 0x5B, 0x5B};
int* b = (int*) &chars[0];
(int*) &chars[0]
值将在循环(长循环)中使用。在我的代码中使用 (int*) &chars[0]
比 b
有什么优势吗?创建 b
是否有任何开销?由于我只想将它用作别名并提高代码可读性。
另外,只要我知道我在做什么,就可以进行这种类型转换吗?或者我应该总是 memcpy()
到另一个具有正确类型的数组并使用它吗?我会遇到任何一种未定义的行为吗?因为到目前为止,在我的测试中,它是有效的,但我看到人们不鼓励这种类型转换。
AFAIK,C 编译器在转换指针时不会插入任何代码——这意味着 chars
和 b
都只是内存地址。通常 C++ 编译器应该以与 C 编译器相同的方式编译它——这就是 C++ 具有不同的、更高级的转换语义的原因。
但您始终可以编译它,然后在 gdb
中反汇编它以供您自己查看。
否则,只要您知道字节顺序问题或在异国平台上可能存在不同的 int
大小,您的转换是安全的。
另请参阅此问题:
is it OK to do this kind of type casting as long as I know what I'm doing?
不,这不行。 这不安全。 C++ 标准不允许这样做。您可以访问对象表示(即,将对象指针转换为 char*
),尽管结果取决于目标平台(由于 endianess 和填充)。然而,你不能安全地做相反的事情(即没有未定义的行为)。
更具体地说,int
类型可以具有 与 char
(未对齐)不同的对齐要求(通常对齐到 4 或 8 个字节)。因此,您的数组可能未对齐,并且当 b
将被取消引用时,强制转换会导致未定义的行为。请注意,它可能会导致某些处理器(例如 AFAIK、POWER)崩溃,尽管主流 x86-64 处理器支持它。此外,编译器可以假定 b
在内存中对齐 (ot alignof(int)
).
Or should I always memcpy() to another array with the correct type and use that?
是,或者替代 C++ 操作,例如自 C++20 以来可用的新 std::bit_cast
。不要担心性能:大多数编译器(GCC、Clang、ICC,当然还有 MSVC)确实优化了此类操作(称为 类型双关)。
Would I encounter any kind of undefined behavior?
如前所述,可以,只要类型双关没有正确完成。有关这方面的更多信息,您可以阅读以下链接:
- What is the Strict Aliasing Rule and Why do we care?
- reinterpret_cast conversion
- Objects and alignment
because in my testing so far, it works, but I've seen people discouraging this kind of type casting.
它通常适用于 x86-64 处理器上的简单示例。但是,当您处理大代码时,编译器会执行愚蠢的优化(但对于 C++ 标准而言是完全正确的)。引用 cppreference :“编译器不需要诊断未定义的行为(尽管诊断出许多简单的情况),并且编译后的程序不需要做任何有意义的事情。”。此类问题很难调试,因为它们通常仅在启用优化时以及在某些特定情况下才会出现。程序的结果可能会因函数内联而改变,这取决于编译器试探法。在您的情况下,这取决于堆栈的对齐方式,堆栈对齐方式取决于编译器优化和当前范围内的 declared/used 变量。某些处理器不支持未对齐的访问(例如,跨缓存行边界的访问),这会导致数据损坏的硬件异常。
所以简而言之,到目前为止“有效”并不意味着它随时随地都有效。
The (int*) &chars[0] value is going to be used in a loop (a long loop). Is there any advantage in using (int*) &chars[0] over b in my code? Is there any overhead in creating b? Since I only want to use it as an alias and improve code readability.
假设您使用正确的方式进行类型双关(例如memcpy
),那么只要启用优化标志,优化编译器就可以完全优化此初始化。除非您发现生成的代码优化不佳,否则您不应该担心这一点。 正确性比性能更重要。
如果代码使用从指向需要字对齐的类型的指针派生的指针执行多个离散字节操作,clang 有时会用字写入替换离散写入,如果原始对象针对该字对齐,则该字写入会成功类型,但如果对象未按照编译器预期的方式对齐,则在不支持未对齐访问的系统上会失败。
除其他外,这意味着如果将指向 T 的指针转换为指向包含 T 的联合的指针,如果联合包含任何类型,则尝试使用联合指针访问原始类型的代码可能会失败需要比原始类型更严格的对齐方式,即使只能通过原始类型的成员访问联合。
取以下代码:
char chars[4] = {0x5B, 0x5B, 0x5B, 0x5B};
int* b = (int*) &chars[0];
(int*) &chars[0]
值将在循环(长循环)中使用。在我的代码中使用 (int*) &chars[0]
比 b
有什么优势吗?创建 b
是否有任何开销?由于我只想将它用作别名并提高代码可读性。
另外,只要我知道我在做什么,就可以进行这种类型转换吗?或者我应该总是 memcpy()
到另一个具有正确类型的数组并使用它吗?我会遇到任何一种未定义的行为吗?因为到目前为止,在我的测试中,它是有效的,但我看到人们不鼓励这种类型转换。
AFAIK,C 编译器在转换指针时不会插入任何代码——这意味着 chars
和 b
都只是内存地址。通常 C++ 编译器应该以与 C 编译器相同的方式编译它——这就是 C++ 具有不同的、更高级的转换语义的原因。
但您始终可以编译它,然后在 gdb
中反汇编它以供您自己查看。
否则,只要您知道字节顺序问题或在异国平台上可能存在不同的 int
大小,您的转换是安全的。
另请参阅此问题:
is it OK to do this kind of type casting as long as I know what I'm doing?
不,这不行。 这不安全。 C++ 标准不允许这样做。您可以访问对象表示(即,将对象指针转换为 char*
),尽管结果取决于目标平台(由于 endianess 和填充)。然而,你不能安全地做相反的事情(即没有未定义的行为)。
更具体地说,int
类型可以具有 与 char
(未对齐)不同的对齐要求(通常对齐到 4 或 8 个字节)。因此,您的数组可能未对齐,并且当 b
将被取消引用时,强制转换会导致未定义的行为。请注意,它可能会导致某些处理器(例如 AFAIK、POWER)崩溃,尽管主流 x86-64 处理器支持它。此外,编译器可以假定 b
在内存中对齐 (ot alignof(int)
).
Or should I always memcpy() to another array with the correct type and use that?
是,或者替代 C++ 操作,例如自 C++20 以来可用的新 std::bit_cast
。不要担心性能:大多数编译器(GCC、Clang、ICC,当然还有 MSVC)确实优化了此类操作(称为 类型双关)。
Would I encounter any kind of undefined behavior?
如前所述,可以,只要类型双关没有正确完成。有关这方面的更多信息,您可以阅读以下链接:
- What is the Strict Aliasing Rule and Why do we care?
- reinterpret_cast conversion
- Objects and alignment
because in my testing so far, it works, but I've seen people discouraging this kind of type casting.
它通常适用于 x86-64 处理器上的简单示例。但是,当您处理大代码时,编译器会执行愚蠢的优化(但对于 C++ 标准而言是完全正确的)。引用 cppreference :“编译器不需要诊断未定义的行为(尽管诊断出许多简单的情况),并且编译后的程序不需要做任何有意义的事情。”。此类问题很难调试,因为它们通常仅在启用优化时以及在某些特定情况下才会出现。程序的结果可能会因函数内联而改变,这取决于编译器试探法。在您的情况下,这取决于堆栈的对齐方式,堆栈对齐方式取决于编译器优化和当前范围内的 declared/used 变量。某些处理器不支持未对齐的访问(例如,跨缓存行边界的访问),这会导致数据损坏的硬件异常。
所以简而言之,到目前为止“有效”并不意味着它随时随地都有效。
The (int*) &chars[0] value is going to be used in a loop (a long loop). Is there any advantage in using (int*) &chars[0] over b in my code? Is there any overhead in creating b? Since I only want to use it as an alias and improve code readability.
假设您使用正确的方式进行类型双关(例如memcpy
),那么只要启用优化标志,优化编译器就可以完全优化此初始化。除非您发现生成的代码优化不佳,否则您不应该担心这一点。 正确性比性能更重要。
如果代码使用从指向需要字对齐的类型的指针派生的指针执行多个离散字节操作,clang 有时会用字写入替换离散写入,如果原始对象针对该字对齐,则该字写入会成功类型,但如果对象未按照编译器预期的方式对齐,则在不支持未对齐访问的系统上会失败。
除其他外,这意味着如果将指向 T 的指针转换为指向包含 T 的联合的指针,如果联合包含任何类型,则尝试使用联合指针访问原始类型的代码可能会失败需要比原始类型更严格的对齐方式,即使只能通过原始类型的成员访问联合。