C++: 将字节缓冲区转换为另一种类型时的对齐问题

问题描述 投票:0回答:1

我有一个通过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字节对齐,那么编译器是如何处理的?

c++ pointers
1个回答
2
投票

你的例子违反了严格的别名规则.所以。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位对齐。如果你试图在不对齐的访问中使用这些指令,应用程序将以 "一般保护故障 "崩溃。


0
投票

void* 肯定会导致UB。static_cast 当你先将你的类型投向最通用的类型时,就会失去它的价值。void*因为你可以把所有的东西都投到 void*. 这与使用 reinterpret_cast 用于从您的类型直接转换到任何其他指针类型。

请看下面的例子。

int64_t* int64_view = reinterpret_cast<int64_t*>(buffer);

它可能有用,也可能没用,UB.

© www.soinside.com 2019 - 2024. All rights reserved.