【问题标题】:Pointer casting指针铸造
【发布时间】:2018-03-24 15:54:36
【问题描述】:

这段代码是什么意思?我的意思是我们似乎获取了一个变量的地址并将其转换为一个(指针类型),然后我们将其推迟,因为我们将知道该值。我错了吗?

#include "stdio.h"

int main(void) {

  int numI = 3;
  float numF = * (float *)&numI;

  printf("%f", numF); 

  numF = 3.0;
  numI = * (int*)&numF;

  printf("\n%d", numI); 

  return 0;
}

【问题讨论】:

标签: c pointers


【解决方案1】:

考虑以下步骤:

  • numInumF 是 2 个对象,它们恰好在您的架构上具有相同的大小,即 4 个字节。
  • &numI 是一个类型为int * 的表达式,其值为numI 对象的地址。
  • (float *)&numI 转换它是具有相同值的float * 类型的表达式。告诉编译器,这个地址是float的地址
  • 取消引用此表达式*(float *)&numI 会生成float 类型的值,该值取决于int 对值3 的实际表示。例如,在英特尔核心处理器(小端、32 位、2s 补码)上,地址intI 处的字节将包含03 00 00 00float 对象使用 IEEE-756 标准在同一处理器的内存中表示:03 00 00 00 表示 非常 小值 1.5 x 2-148,大约 4.2039e-45
  • 将此值作为变量参数传递给printf首先将其转换为double类型,具有相同的值,printf将值转换为0.000000,因为格式%f只指定了6个小数位并且没有指数。为了获得更精确的转换,您可以使用%g,它会产生4.2039e-45,或%a,它会产生十六进制表示0x1.8p-148
  • 程序的第二部分执行相反的转换:将 IEEE-756 表示为 00 00 40 40float00 00 40 40 重新解释为 int,生成值 1077936128

这是您的程序的修改版本,使其更加明确:

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

int main(void) {
    int numI;
    float numF;
    unsigned char *p;

    assert(sizeof numI == sizeof numF);

    numI = 3;
    p = (unsigned char *)&numI;
    printf("int value %d is represented in memory as %02X %02X %02X %02X\n",
           numI, p[0], p[1], p[2], p[3]);

    //numF = *(float *)&numI;
    memcpy(&numF, &numI, sizeof numF);
    printf("reinterpreted as float with format %%f: %f\n", numF);
    printf("reinterpreted as float with format %%g: %g\n", numF);
    printf("reinterpreted as float with format %%a: %a\n", numF);
    printf("numF exact value: %g * 2^-148\n", numF * pow(2.0, 148));

    numF = 3.0;
    p = (unsigned char *)&numF;
    printf("float value %.1g is represented in memory as %02X %02X %02X %02X\n",
           numF, p[0], p[1], p[2], p[3]);

    //numI = *(int *)&numF;
    memcpy(&numI, &numF, sizeof numI);
    printf("reinterpreted as int with format %%d: %d\n", numI);
    printf("reinterpreted as int with format %%#X: %#X\n", numI);

    return 0;
}

输出:

int value 3 is represented in memory as 03 00 00 00
reinterpreted as float with format %f: 0.000000
reinterpreted as float with format %g: 4.2039e-45
reinterpreted as float with format %a: 0x1.8p-148
numF exact value: 1.5 * 2^-148
float value 3 is represented in memory as 00 00 40 40
reinterpreted as int with format %d: 1077936128
reinterpreted as int with format %#X: 0X40400000

但请注意:

  • 这些转换违反了严格的别名规则,因此具有未定义的行为。
  • intfloat 类型在内存中的表示是特定于体系结构的。一些系统以大端字节顺序表示int,一些具有填充位和陷阱值,一些老式系统甚至使用一个补码或符号+幅度表示。 float 的表示可能更加奇特,尽管大多数当前系统使用 IEEE-756,其字节顺序与整数相同。因此,您的程序再次出现未定义的行为,并且充其量会产生特定于实现的输出。
  • 在内存中重新解释表示的推荐方法是复制带有memcpy 的字节,如修改后的代码所示。现代编译器会为此生成非常高效的代码,将 memcpy 扩展为仅 2 条指令(如果不是更少的话)。

【讨论】:

    猜你喜欢
    • 2011-04-03
    • 2010-12-27
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    相关资源
    最近更新 更多