我对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);
}
上面的代码有什么问题?输出与垃圾值有关。
答案 0 :(得分:5)
首先,您必须了解x86架构是所谓的 little-endian 。这意味着在多字节变量中,字节在内存中从最小到最重要的顺序排序。如果你不明白这意味着什么,它会在一秒钟内变得清晰。
char
是8位 - 一个字节。将'A'
存储到一个时,它会获得值0x41
,并且很高兴。 int
更大;在许多架构上它是32位 - 4个字节。将值'A'
分配给int时,它将获得值0x00000041
。这在数字上完全相同,但int
中有三个额外的零字节。
因此int
包含0x00000041
。在内存中,这是以字节为单位排列的,因为你是在一个小端架构上,那些字节的排列从最小到最重要 - 与相反我们通常如何编写它们!记忆实际上是这样的:
+----+----+----+----+
int: | 41 | 00 | 00 | 00 |
+----+----+----+----+
+----+
char: | 41 |
+----+
当你指向int
并将其转换为char*
,然后取消引用它时,编译器将取int
的第一个字节 - 因为{{ 1}}只有一个字节宽 - 并将其打印出来。其他三个字节被忽略了!现在回过头来看一下,如果char
中的字节顺序被反转,就像在big-endian架构上那样,你会检索零值!因此,此代码的行为 - 从int
到int*
的转换按预期工作的事实 - 严格依赖于您运行它的计算机。
另一方面,当您指向char*
并将其转换为char
,然后将其转发时,编译器将获取int*
中的一个字节正如你所期望的那样,但它会再读取三个字节,因为char
是四个字节宽!这三个字节是什么?你不知道!你的记忆如下:
int
您在 +----+
char: | 41 |
+----+
+----+----+----+----+
int: | 41 | ?? | ?? | ?? |
+----+----+----+----+
中获得了垃圾值,因为您正在读取未初始化的内存。在不同的平台上或在不同的行星对齐下,您的代码可能完全正常工作,或者可能是段错误和崩溃。没有说明问题。这就是所谓的 undefined behavior ,它是我们与编译器一起玩的危险游戏。在这样处理内存时我们必须非常小心;没有什么比非确定性代码更可怕了。
答案 1 :(得分:4)
您可以安全地将任何内容表示为char
的数组。它不起作用。这是 STRICT ALIASING 规则的一部分。
您可以在其他问题中阅读严格别名: What is the strict aliasing rule?
与您的问题更密切相关: Once again: strict aliasing rule and char*
答案 2 :(得分:1)
引用此处给出的答案:What is the strict aliasing rule?
[...]解除引用指向别名不兼容类型别名的指针是未定义的行为。不幸的是,您仍然可以使用这种方式进行编码,可能会收到一些警告,让它编译正常,只有在运行代码时才会出现奇怪的意外行为。
还与您的问题相关:Once again: strict aliasing rule and char*
C和C ++都允许通过char *(或者特别是char类型的左值)访问任何对象类型。它们不允许通过任意类型访问char对象。所以,是的,规则是一种方式&#34;规则。
(我必须赞扬@Let_Me_Be的第二个链接)
答案 3 :(得分:1)
根据标准,将char
(或多个char
s)转换为int
是未定义的行为,因此允许任何结果。大多数编译器都会尝试做有意义的事情,因此以下内容可能是您在特定体系结构中看到的行为的原因:
假设32位int
,int
与4 char
s的大小相同
不同的架构会以不同的方式处理这四个字节,将它们的值转换为int,最常见的是小端或大端
看着:
[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'}
以根据架构获得所需内容
如上所述,这是未定义的行为,但应该适用于大多数编译器并让您更好地了解幕后发生的事情
答案 4 :(得分:1)
当你这样做时:
cout << *static_cast<int*>(p);
你实际上是说 p 指向一个整数(在内存中由4个字节表示)但你刚才在其中写了一个字符(由内存中的1个字节表示)所以当你演员它将您的变量扩展为3个垃圾字节。
但是如果你把它归还给一个角色,你就会得到你的&#39; A&#39;因为你正在将你的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 *求一个char *时,他只是改变你取消引用它时读取的字节数
答案 5 :(得分:-1)
请考虑以下代码:
#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
且未初始化的值。当您输出变量的十六进制值时,可以看到它。