【问题标题】:What actually happens when a pointer to integer is cast to a pointer to char?当指向整数的指针转换为指向 char 的指针时,实际上会发生什么?
【发布时间】:2010-09-20 06:44:06
【问题描述】:
int i=40;
char *p;
p=(char *)&i;//What actually happens here?
printf("%d",*p);

输出会是什么?请帮忙!

【问题讨论】:

    标签: c casting


    【解决方案1】:
    p=(char *)&i;//What actually happens here?
    

    它获取i 的地址并将其转换为char 指针。所以*p 的值现在是i 的第一个字节。该值是什么,取决于平台。

    【讨论】:

    • 最有可能是 0 或 40(取决于机器的字节序)。
    • 为什么需要演员表,char *p = (char *) &i char *p = &i 有什么区别?
    【解决方案2】:

    让我们先看看ip 的内容在内存中的布局方式(假设大端顺序):

    项目地址 0x00 0x01 0x02 0x03 ---- -------- ---------- 我 0x08000000 0x00 0x00 0x00 0x28 p 0x08000004 0x?? 0x?? 0x?? 0x??

    由于p 被声明为自动变量,它没有被初始化为任何东西并且包含由0x?? 表示的随机位模式。

    排队

    p = (char *)&i;
    

    表达式&i 的计算结果为i 或0x08000000 的地址,其类型为pointer to intint *。强制转换将类型从int * 转换为char *,并将结果分配给p

    这是分配后内存中的情况:

    项目地址 0x00 0x01 0x02 0x03 ---- -------- ---------- 我 0x08000000 0x00 0x00 0x00 0x28 p 0x08000004 0x08 0x00 0x00 0x00

    所以p 的值现在是i地址。在行中

    printf("%d", *p);
    

    表达式*p 的类型是char,它的值是存储在地址0x08000000 中的任何值,在本例中为0。由于printf 是一个可变参数函数,所以*p 的值从char 类型提升为int 类型。

    所以对于这种特殊情况,输出为“0”。如果顺序是 little-endian,则地图看起来像

    项目地址 0x03 0x02 0x01 0x00 ---- -------- ---------- 我 0x08000000 0x00 0x00 0x00 0x28 p 0x08000004 0x08 0x00 0x00 0x00

    输出将是“40”。

    请注意,整个示例假定整数和字符指针具有相同的大小和布局;这不能保证在任何地方都是正确的(参见Online C Standard (n1256 draft),第 6.2.5 节,第 27 段),所以你不能依赖它在任何地方按照你期望的方式工作(假设我认为intchar 不是标准定义的兼容类型,但我可能错了)。类型双关语通常是不安全的。

    【讨论】:

    • 您的描述假定int*char* 指针具有相同的大小和布局,但程序的可见行为却不同。 C 标准保证任何对象都可以作为字符数组访问,因此 OP 的代码将访问i 的第一个(最低地址)字节。正如其他人指出的那样,该字节的值取决于实现。
    • @KeithThompson 为什么需要演员表,char *p = (char *) &i char *p = &i 有什么区别?
    • 为什么需要演员表,char *p = (char *) &i char *p = &i 有什么区别?
    • @SurajJain:区别在于char *p = (char *) &i;islegal,而char *p = &i;不是。 char*int* 之间没有隐式转换(尽管某些编译器可能允许这样做,最好带有警告)。指针转换可能很危险,让您将一种类型的对象视为另一种类型,因此语言要求此类转换是显式的。
    • @KeithThompson 如果编译器允许它带有警告,例如“从不兼容类型初始化”,那么它们是否相同,它们的行为是否相同。这困扰了我很长时间。
    【解决方案3】:

    你来了

    int i = 40; //为整数i分配内存并赋值为40

    char *p = (char*)&i; 
    

    所以在这里你定义了一个指针变量,并在将其转换为char* 后为其分配 i 的地址

    假设i 分配在1021 address,那么p 将拥有限制为1 字节的地址,因此它应该保存first 8 bit from the representation of 40;

    由于 40 已被 2 字节的前 8 位覆盖,它将持有 40 的 char 等效值,但当您使用 %d 打印它时,它会打印 40

    【讨论】:

    • 除非机器可能是大端在这种情况下,你会得到0。
    • 为什么需要演员表,char *p = (char *) &i char *p = &i 有什么区别?
    【解决方案4】:

    这取决于。在 Windows 上,输出将是 40,但这只是因为很多巧合:

    首先,printf 不(不能)检查其参数的类型,因此由于它在格式字符串中看到 %d,它假定给定的参数是一个 int。虽然 *p 只是一个字符,但结果会被提升为一个 int(对于函数原型中未指定的每个参数也是如此)。

    其次,p会指向变量i占用的内存,但是由于它是一个char指针,所以它只会从i的内存中占用一个字节。由于 Windows/Intel 使用 Least-Significant-Byte 第一个约定,40 将存储为字节模式“40 0 0 0”,因此,由于 *p 采用第一个字节(char),结果将为 40。如果我有值 256 或更大,结果将不正确。

    【解决方案5】:

    What happens when int pointer is typecasted to char? 还有一个问题标记为重复到这里,我试着解释一下。

    $ cat a.c
    #include <stdio.h>
    
    int main(){
        int a;
        char *x;
        x = (char *) &a;
        a=512;
        x[0]=1;
        x[1]=2;
        printf("%d\n",a);
        return 0;
    }
    

    编译运行:

    $ gcc a.c && ./a.out
    513
    

    为什么是 513?我们可以使用 gdb 来查看根本原因。

    $ gcc a.c -g && gdb ./a.out
    (gdb) list
    1       #include <stdio.h>
    2
    3        int main(){
    4            int a;
    5            char *x;
    6            x = (char *) &a;
    7            a=512;
    8            x[0]=1;
    9            x[1]=2;
    10           printf("%d\n",a);
    

    在 a.c 的第 8 行设置断点,然后运行

    (gdb) b a.c:8
    Breakpoint 1 at 0x40113d: file a.c, line 8.
    (gdb) run
    

    一旦程序在断点处停止,打印变量a的内存地址。

    (gdb) p &a
    $2 = (int *) 0x7fffffffd9d4
    (gdb) p x
    $3 = 0x7fffffffd9d4 ""
    

    变量a的内存地址为0x7fffffffd9d4,变量x的值相同。

    在显示内存内容之前,我们先来了解一下16进制格式的512,它是:

    00 00 02 00
    

    x86 是小端,所以在内存中应该是:

    [higher address] 00 02 00 00 [lower address]
    

    让我们展示真实的记忆,就像我们想象的一样。

    (gdb) x/4xb 0x7fffffffd9d4
    0x7fffffffd9d4: 0x00    0x02    0x00    0x00
    

    然后,显示x[0]和x[1]的内存地址,并将内存内容转换为实际值,应该不难理解为什么会打印出513。

    (gdb) p &x[0]
    $4 = 0x7fffffffd9d4 ""
    (gdb) p &x[1]
    $5 = 0x7fffffffd9d5 "\002"
    

    【讨论】:

      猜你喜欢
      • 2018-06-08
      • 1970-01-01
      • 2018-04-05
      • 1970-01-01
      • 2014-10-22
      • 2016-01-27
      • 2015-11-24
      • 2017-09-03
      • 2015-09-10
      相关资源
      最近更新 更多