Char指向Int指针转换不起作用

时间:2015-07-09 13:39:51

标签: c++

我对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);

}

上面的代码有什么问题?输出与垃圾值有关。

6 个答案:

答案 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架构上那样,你会检索零值!因此,此代码的行为 - 从intint*的转换按预期工作的事实 - 严格依赖于您运行它的计算机。

另一方面,当您指向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位intint与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且未初始化的值。当您输出变量的十六进制值时,可以看到它。