从 bool* 到 void* 到 int* 的错误转换
Bug casting from bool* to void* to int*
通常在 C++ 中,一个参数 void* user_data
可以用来传递任意类型。
我用它来传递布尔数组。但是,我有一个错误,我从 bool*
--> void*
--> int*
投射,我得到了奇怪的结果。这是一个例子。
#include <iostream>
int main() {
bool test[2] = { };
void *ptr = static_cast<void*>(test);
std::cout << static_cast<bool*>(ptr)[0] << '\n';
std::cout << static_cast<int*>(ptr)[0] << '\n';
std::cout << static_cast<int>(test[0]) << '\n';
}
输出:
$ g++ int_bool.cpp
$ ./a.out
0
-620756992
0
有人可以向我解释一下问题出在哪里吗?通常当我从 bool 转换为 int 时,没有问题:false 映射到 0,true 映射到 1。显然,这里不是这种情况。
static_cast<int*>(ptr)[0]
将 ptr
转换为 int*
并读取第一个元素。因为原始数组只有 2 个字节,所以你在它外面读取(因为你正在读取一个 4 字节的 int
)并调用未定义的行为,除非 int
是你的 2 字节类型系统。您还违反了 strict aliasing rule by accessing a type using a different pointer type which also invokes UB. Besides you'll get UB if the bool array isn't properly aligned。在 x86 上它不会引起任何问题,因为 x86 默认允许未对齐的访问,但在大多数其他架构上你会遇到段错误
static_cast<int>(test[0])
OTOH 将 test[0]
(这是一个 bool
)转换为 int
,并且是完全有效的值转换。
更新:
The type int*
refers to a pointer whose object is 4-bytes long, whereas bool*
refers to a pointer whose object is 2-bytes long
没有。取消引用变量 var
时,将从该地址开始的内存中读取长度为 sizeof(var)
的内存量,并将其视为该变量的值。所以*bool_ptr
将从内存中读取1个字节而*int_ptr
将从内存中读取4个字节(如果bool
和int
分别是1字节和4字节类型)
在您的例子中,bool
数组包含 2 个字节,因此当从 static_cast<int*>(ptr)
读取 4 个字节时,2 个字节 inside 数组和 2 个字节在 之外读取数组。如果您声明 bool test[4] = {};
(或更多元素),您会看到 int*
取消引用成功完成,因为它读取了属于您的所有 4 个 bool,但您仍然遇到未对齐问题
现在尝试将布尔值更改为 非零 并查看
bool test[4] = { true, false, true, false };
你很快就会意识到,将指针转换为不同的指针类型并不是像简单的值转换(即转换)那样简单地读取旧类型并转换为新类型,而是不同的“内存”治疗”。这本质上只是一个 reinterpret_cast
,您可以阅读它以了解有关此问题的更多信息
I don't understand what you are saying about char*
. You're saying casting from any type to char*
is valid?
从任何其他 指针 类型转换为 char*
是有效的。阅读上面关于严格别名规则的问题:
You can use char*
for aliasing instead of your system's word. The rules allow an exception for char*
(including signed char
and unsigned char
). It's always assumed that char*
aliases other types.
它用于诸如 memcpy
之类的事情,您可以在其中将代表一种类型的字节复制到不同的目的地
bool test[4] = { true, true, true, true };
int v;
memcpy((char*)&test, (char*)&v, sizeof v);
技术上 mempcy
接收 void*
,转换为 char*
仅用于演示
另见
- Strict aliasing rule and 'char *' pointers
- https://en.wikipedia.org/wiki/Pointer_aliasing
通常在 C++ 中,一个参数 void* user_data
可以用来传递任意类型。
我用它来传递布尔数组。但是,我有一个错误,我从 bool*
--> void*
--> int*
投射,我得到了奇怪的结果。这是一个例子。
#include <iostream>
int main() {
bool test[2] = { };
void *ptr = static_cast<void*>(test);
std::cout << static_cast<bool*>(ptr)[0] << '\n';
std::cout << static_cast<int*>(ptr)[0] << '\n';
std::cout << static_cast<int>(test[0]) << '\n';
}
输出:
$ g++ int_bool.cpp
$ ./a.out
0
-620756992
0
有人可以向我解释一下问题出在哪里吗?通常当我从 bool 转换为 int 时,没有问题:false 映射到 0,true 映射到 1。显然,这里不是这种情况。
static_cast<int*>(ptr)[0]
将 ptr
转换为 int*
并读取第一个元素。因为原始数组只有 2 个字节,所以你在它外面读取(因为你正在读取一个 4 字节的 int
)并调用未定义的行为,除非 int
是你的 2 字节类型系统。您还违反了 strict aliasing rule by accessing a type using a different pointer type which also invokes UB. Besides you'll get UB if the bool array isn't properly aligned。在 x86 上它不会引起任何问题,因为 x86 默认允许未对齐的访问,但在大多数其他架构上你会遇到段错误
static_cast<int>(test[0])
OTOH 将 test[0]
(这是一个 bool
)转换为 int
,并且是完全有效的值转换。
更新:
The type
int*
refers to a pointer whose object is 4-bytes long, whereasbool*
refers to a pointer whose object is 2-bytes long
没有。取消引用变量 var
时,将从该地址开始的内存中读取长度为 sizeof(var)
的内存量,并将其视为该变量的值。所以*bool_ptr
将从内存中读取1个字节而*int_ptr
将从内存中读取4个字节(如果bool
和int
分别是1字节和4字节类型)
在您的例子中,bool
数组包含 2 个字节,因此当从 static_cast<int*>(ptr)
读取 4 个字节时,2 个字节 inside 数组和 2 个字节在 之外读取数组。如果您声明 bool test[4] = {};
(或更多元素),您会看到 int*
取消引用成功完成,因为它读取了属于您的所有 4 个 bool,但您仍然遇到未对齐问题
现在尝试将布尔值更改为 非零 并查看
bool test[4] = { true, false, true, false };
你很快就会意识到,将指针转换为不同的指针类型并不是像简单的值转换(即转换)那样简单地读取旧类型并转换为新类型,而是不同的“内存”治疗”。这本质上只是一个 reinterpret_cast
,您可以阅读它以了解有关此问题的更多信息
I don't understand what you are saying about
char*
. You're saying casting from any type tochar*
is valid?
从任何其他 指针 类型转换为 char*
是有效的。阅读上面关于严格别名规则的问题:
You can use
char*
for aliasing instead of your system's word. The rules allow an exception forchar*
(includingsigned char
andunsigned char
). It's always assumed thatchar*
aliases other types.
它用于诸如 memcpy
之类的事情,您可以在其中将代表一种类型的字节复制到不同的目的地
bool test[4] = { true, true, true, true };
int v;
memcpy((char*)&test, (char*)&v, sizeof v);
技术上 mempcy
接收 void*
,转换为 char*
仅用于演示
另见
- Strict aliasing rule and 'char *' pointers
- https://en.wikipedia.org/wiki/Pointer_aliasing