在下面的代码中,我尝试将数据从float f逐位复制到int I,而不进行任何数据转换。我将f的地址强制转换为(int*),并在将其分配给I时取消引用该地址;f作为一个int指针,当(int*)f被取消引用并分配给i时,它不会进行类型转换。但这不起作用,我不明白为什么。
void main(){
float f=3.0;
int i;
/* Check to make sure int and float have the same size on my machine */
printf("sizeof(float)=%dn",sizeof(float)); /* prints "sizeof(float)=4" */
printf("sizeof(int)=%dn",sizeof(int)); /* prints "sizeof(int)=4" */
/* Verify that &f and (int*) &f have the same value */
printf("&f = %pn",&f); /* prints &f = 0x7ffc0670dff8 */
printf("(int*) &f = %pn",(int*) &f); /* prints (int*) &f = 0x7ffc0670dff8 */
i=*((int*) &f);
printf("%fn", i); /* prints 0.000000 (I would have expected 3.000000) */
return;
}
通过类型转换进行赋值,将原始的4字节数据从一个变量复制到另一个变量。问题是32位浮点变量中的3不像整数变量中的三那样存储。
例如,Mac上的3位64位浮点格式存储为0x4e808000。将其赋值为整数将产生1077936128。
请参阅https://en.wikipedia.org/wiki/IEEE_floating_point或http://www.madirish.net/240或https://users.cs.duke.edu/~raw/cps104/TWFNotes/foating.html
为同一对象创建两个不相关类型的指针违反了严格的别名规则。您需要避免这种情况,因为在复杂的代码中,它可能会导致编译器生成不符合您要求的二进制文件。这也是未定义的行为。
在int
和float
之间更改C中位模式类型的正确方法是完全避免指针,并使用并集:
union int_float { int i; float f; };
int ival = (union int_float){ .f = 4.5 }.i;
float fval = (union int_float){ .i = 45 }.f;
结果可能略有不同。请确保检查浮动类型和整数类型的大小是否相同,否则数据将丢失/生成垃圾数据。
请注意,以这种方式生成的值可能不是目标类型的有效元素(当非零整数值被解释为浮点零时,尽管没有所有的零位模式,但这或多或少就是您在上面看到的情况),这可能会导致后续的未定义或意外行为。确保验证输出。
转换后,您将获得存储在变量i中的整数。因此,如果你想打印这样一个值,你必须使用:
printf("%dn", i); /* prints 1077936128 */
现在printf将正确解释内存位并打印正确的值。这不是一个演员阵容,而是一个有点像你说的复制品。请记住((int)&f) 将该指针取消引用为int值。在int和float大小不同的机器上,它不会做你认为的事情。
如果int和float的大小相同,则复制的另一种方法是:
memcpy(&i, &f, sizeof f);
在复制了内存区域中包含的四个字节之后,如果我们试图将内容打印为四个十进制值的序列,我们可以理解刚刚发生的重新分发:
用于浮动:
3 160 0 3
对于整数
0 204 204 0
这意味着这四个字节的管理方式与计算机不同,具体取决于representation的类型:int或float。