Reputation: 5939
I have a block of memory allocated through char buffer, is it legal to view it via buffer of another type?
char* buffer = new char[1000];
int64_t* int64_view = static_cast<int64_t*>(static_cast<void*>(buffer))
Is int64_view[0]
guaranteed to correspond to first 8 bytes of buffer
?
I am a bit concerned about aliasing, if the char
buffer is only 1-byte aligned, and int64_t
must be 8-byte aligned then how does compiler handle it?
Upvotes: 2
Views: 810
Reputation: 621
Your example is violation of the strict aliasing rule.
So, int64_view
anyway will point to the first byte, but it can be unaligned access. Some platforms allow it, some not. Anyway, in C++ it's UB.
For example:
#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;
}
}
Now compile and run it with -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
It works on x86_64, but there is undefined behavior and you pay with execution speed.
This example on godbolt
In C++20 there is bit_cast. It will not help you in this example with unaligned access, but it can resolve some issues with aliasing.
UPDATE: There is instructions on x86_64, that requires aligned access. For example, SSE, that requires 16-bit alignment. If you will try to use these instructions with unaligned access, application will crash with "general protection fault".
Upvotes: 4
Reputation: 3506
void*
will definitely lead to UB. static_cast
lost it's value when you cast your type first to the most generic type void*
, because you can cast everything to/from void*
. It is no different from using reinterpret_cast
for casting straight from your type to any other pointer type.
Consider the following example:
int64_t* int64_view = reinterpret_cast<int64_t*>(buffer);
It might work, and it might not - UB.
Upvotes: 0