Char到Int指针转换不工作
Char to Int Pointer Casting Not Working
我对char to int pointer
的铸造感到困惑。我正在检查指针的转换是如何工作的,下面的代码int to char
工作正常。
#include <iostream>
using namespace std;
int main(){
int a=65;
void *p=&a;
cout << *static_cast<char*>(p);
}
输出A
但是当我试图从char to int
投射时,它没有显示正确的值。
#include <iostream>
using namespace std;
int main(){
char a='A';
void *p=&a;
cout << *static_cast<int*>(p);
}
上面的代码有什么问题?输出是关于垃圾值的。
首先,您必须了解x86架构是所谓的little-endian。这意味着在多字节变量中,字节在内存中从最小到最大排序。如果你不明白这是什么意思,马上就会明白了。
A char
是8位——一个字节。当您将'A'
存储到其中时,它将获得0x41
的值并且很高兴。int
更大;在许多体系结构中,它是32位——4字节。当将值'A'
赋值给int类型时,它将获得值0x00000041
。这在数字上是完全相同的,但是在int
中有三个额外的零字节。
你的int
包含0x00000041
。在内存中,这是以字节为单位排列的,因为你是在一个小端架构上,这些字节从最小到最大排列——与我们通常写它们的相反!内存实际上是这样的:
+----+----+----+----+
int: | 41 | 00 | 00 | 00 |
+----+----+----+----+
+----+
char: | 41 |
+----+
当你获取一个指向int
的指针并将其强制转换为char*
,然后对其解引用时,编译器将获取int
的第一个字节——因为char
只有一个字节宽——并将其打印出来。其他三个字节被忽略!现在回顾一下,注意到如果int
中的字节顺序颠倒,就像在大端架构中一样,您将检索到值0 !因此,这段代码的行为——从int*
到char*
的强制转换如您所期望的那样工作——严格依赖于您正在运行它的机器。
另一方面,当你拿一个指向char
的指针并将其强制转换为int*
,然后引用它时,编译器将抓取char
中的一个字节,正如你所期望的那样,但随后它还将再读取三个字节,因为int
s是四个字节宽!这三个字节是什么?你不知道!你的记忆是这样的:
+----+
char: | 41 |
+----+
+----+----+----+----+
int: | 41 | ?? | ?? | ?? |
+----+----+----+----+
你在int
中得到一个垃圾值,因为你正在读取未初始化的内存。在不同的平台上或在不同的行星排列下,您的代码可能工作得很好,也可能出现分段错误并崩溃。这很难说。这就是所谓的未定义行为,这是我们与编译器玩的一个危险的游戏。我们在处理这样的记忆问题时必须非常小心;没有什么比不确定代码更可怕的了。
您可以安全地将任何内容表示为char
的数组。反之则不然。这是STRICT ALIASING规则的一部分。
你可以在其他问题中阅读严格混叠:什么是严格的混叠规则?
与你的问题更密切相关:再一次:严格混叠规则和char*
引用这里给出的答案:什么是严格混叠规则?
[…解引用一个别名另一个不兼容类型的指针是未定义的行为。不幸的是,你仍然可以这样编码,也许会得到一些警告,让它编译得很好,只是在运行代码时出现奇怪的意外行为。
也与你的问题相关:再一次:严格的混叠规则和char*
C和c++都允许通过char *访问任何类型的对象(特别是char类型的左值)。它们不允许通过任意类型访问char对象。所以,是的,这个规则是"单向"规则。
(我必须感谢@Let_Me_Be的第二个链接)
当你这样做的时候:
cout << *static_cast<int*>(p);
你实际上是在说p指向一个整数(在内存中由4个字节表示),但是你之前刚刚在它里面写了一个字符(在内存中由1个字节表示),所以当你将它转换为一个整数时,你将变量扩展到3个垃圾字节。
但是如果你把它转换回char类型你会得到a,因为你把int类型切成了char类型:
cout << (char) *static_cast<int*>(p);
否则,如果你只想要ASCII值,将void*转换为char*(所以当你对它解引用时,你只访问1个字节),并将其中的内容转换为int。
char a = 'A';
void *p=&a;
cout << static_cast<int>(*((char*)p));
事实是,静态强制转换能够理解你想要将一个char转换为int(并获得他的ASCII值),但是当要求一个char*转换为int*时,他只是在你解引用它时改变读取的字节数。
根据标准,将char
(或多个char
)转换为int
是未定义的行为,因此任何结果都是允许的。大多数编译器将尝试做有意义的事情,因此以下是您在特定架构中看到的行为的可能原因:
假设一个32位的int
,一个int
的大小等于4个char
s
不同的体系结构会以不同的方式处理这四个字节,将它们的值转换为整型,最常见的是小端序或大端序
看:
[Byte1][Byte2][Byte3][Byte4]
int值可以是:
(Little Endina) Byte1+Byte2*256+Byte3*256^2+Byte4*256^3
(Big Endian ) Byte4+Byte3*256+Byte2*256^2+Byte1*256^3
在你的情况下,Byte1或Byte4被设置,剩下的字节是内存中发生的任何事情,因为你只保留一个字节,你需要4
尝试以下操作:
int main(){
char a[4]={'A', 0, 0, 0};
void *p=a;
cout << *static_cast<int*>(p);
}
您可能必须将初始化切换到{0,0,0, 'A'}
以获得基于架构的您想要的
如前所述,这是一个未定义的行为,但应该与大多数编译器一起工作,并使您更好地了解底层发生的事情
考虑以下代码:
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
{
int a=65;
cout << hex << static_cast<int>(a) << "n";
void *p=&a;
cout << hex << setfill('0') << setw(2 * sizeof(int)) << *static_cast<int*>(p) << "n";
}
{
char a='A';
cout << hex << static_cast<int>(a) << "n";
void *p=&a;
cout << hex << *static_cast<int*>(p) << "n";
}
}
在输出中确实有'A'
字符代码(0x41),但是它被填充到int
的大小,并且没有初始化值。您可以在输出变量的十六进制值时看到它。
- 如何定义在用作函数参数时工作的类模板的转换
- 将双精度值转换为 char 变量时字符串流如何工作
- 隐式转换是否应该在模板参数的上下文中工作?
- strcmp/char* 转换无法按预期工作
- 如何在 Poco 中将工作 HTTP 代码转换为 HTTPS
- Qt-工作线程崩溃时将cv::Mat转换为QImage
- 我的代码在作为参数传入 .begin() 时不起作用,但在我将 .begin() 转换为迭代器后工作
- 字符串文字到 char 数组的转换如何在C++中实际工作
- 将缓冲区转换为在现有程序中工作的iStream
- 更新MySQL连接器后,隐式转换停止工作
- 为什么Arduino(小端序)上的Sha1在没有转换为大端序的情况下工作?
- 如何使转换功能工作?
- 以下对二进制转换代码的小数如何工作
- 隐式转换未按预期工作
- 尝试将工作python程序转换为C++但不起作用
- 字符串到字符指针的转换如何在C++中工作
- 如何将负数转换为更广泛的类型在内部工作
- 十六进制、二进制和 oct 到 dec 转换器无法正常工作C++
- 使用模板无法按预期工作进行继承和强制转换
- 超级强大:实时音调转换,时间扩展器不工作