我有一个通过char缓冲区分配的内存块,通过其他类型的缓冲区查看是否合法?
char* buffer = new char[1000];
int64_t* int64_view = static_cast<int64_t*>(static_cast<void*>(buffer))
缓冲区 int64_view[0]
保证对应于前8个字节的 buffer
我有点担心别名的问题。char
缓冲区只有1个字节对齐,而 int64_t
必须是8字节对齐,那么编译器是如何处理的?
你的例子违反了严格的别名规则.所以。int64_view
反正会指向第一个字节,但可以不对齐访问。有些平台允许,有些不允许。总之,在C++中就是UB。
比如说,在C++中,它是UB。
#include <cstdint>
#include <cstddef>
#include <iostream>
#include <iomanip>
#define COUNT 8
struct alignas(1) S
{
char _pad;
char buf[COUNT * sizeof(int64_t)];
};
int main()
{
S s;
int64_t* int64_view alignas(8) = static_cast<int64_t*>(static_cast<void*>(&s.buf));
std::cout << std::hex << "s._pad at " << (void*)(&s._pad) << " aligned as " << alignof(s._pad) << std::endl;
std::cout << std::hex << "s.buf at " << (void*)(s.buf) << " aligned as " << alignof(s.buf) << std::endl;
std::cout << std::hex << "int64_view at " << int64_view << " aligned as " << alignof(int64_view) << std::endl;
for(std::size_t i = 0; i < COUNT; ++i)
{
int64_view[i] = i;
}
for(std::size_t i = 0; i < COUNT; ++i)
{
std::cout << std::dec << std::setw(2) << i << std::hex << " " << int64_view + i << " : " << int64_view[i] << std::endl;
}
}
现在编译并运行它 -fsanitize=undefined
:
$ g++ -fsanitize=undefined -Wall -Wextra -std=c++20 test.cpp -o test
$ ./test
s._pad at 0x7ffffeb42300 aligned as 1
s.buf at 0x7ffffeb42301 aligned as 1
int64_view at 0x7ffffeb42301 aligned as 8
test.cpp:26:23: runtime error: store to misaligned address 0x7ffffeb42301 for type 'int64_t', which requires 8 byte alignment
0x7ffffeb42301: note: pointer points here
7f 00 00 bf 11 00 00 00 00 00 00 ff ff 00 00 01 00 00 00 20 23 b4 fe ff 7f 00 00 7c a4 9d 2b 98
^
test.cpp:31:113: runtime error: load of misaligned address 0x7ffffeb42301 for type 'int64_t', which requires 8 byte alignment
0x7ffffeb42301: note: pointer points here
7f 00 00 bf 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 03 00 00 00
^
0 0x7ffffeb42301 : 0
1 0x7ffffeb42309 : 1
2 0x7ffffeb42311 : 2
3 0x7ffffeb42319 : 3
4 0x7ffffeb42321 : 4
5 0x7ffffeb42329 : 5
6 0x7ffffeb42331 : 6
7 0x7ffffeb42339 : 7
它可以在x86_64上工作,但有未定义的行为,你要付出执行速度的代价。
这个例子在 榫头
在C++20中,有 bit_cast. 在这个例子中,它对不对齐的访问没有帮助,但它可以解决一些别名的问题。
UPDATE:x86_64上有一些指令,需要对齐访问。例如,SSE,需要16位对齐。如果你试图在不对齐的访问中使用这些指令,应用程序将以 "一般保护故障 "崩溃。
void*
肯定会导致UB。static_cast
当你先将你的类型投向最通用的类型时,就会失去它的价值。void*
因为你可以把所有的东西都投到 void*
. 这与使用 reinterpret_cast
用于从您的类型直接转换到任何其他指针类型。
请看下面的例子。
int64_t* int64_view = reinterpret_cast<int64_t*>(buffer);
它可能有用,也可能没用,UB.