背景
数值
的类型强转和指针
的类型强转是不一样的,前者会改变数据的内容
,后者只会改变数据的解释方式
。
解析
示例
定义浮点数1.5
,强转成int
时会截断成1
,而以*(int *)
解释其内容时会解释成1069547520
示例代码:
#include<stdio.h>
float a = 1.5;
int *b = &a;
int main() {
printf("a == %f\n", a);
printf("&a == %p\n", &a);
printf("(int)a == %d\n", (int)a);
printf("b == %p\n", b);
printf("*b == %d\n", *b);
return 0;
}
编译:
wanghaipeng@v03p:~/demo$ gcc float_to_int.c
float_to_int.c:4:10: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
int *b = &a;
^
运行:
a == 1.500000
&a == 0x5593b7253010
(int)a == 1
b == 0x5593b7253010
*b == 1069547520
数值强转时底层发生了什么?
数值强转需要CPU硬件的支持,对于X86-64,示例代码的main()
反汇编:
printf("a == %f\n", a);
打印前cvtss2sd
指令将a扩展成double
000000000000065a <main>:
65a: 55 push %rbp
65b: 48 89 e5 mov %rsp,%rbp
65e: f3 0f 10 05 aa 09 20 movss 0x2009aa(%rip),%xmm0 # 201010 <a>
665: 00
666: f3 0f 5a c0 cvtss2sd %xmm0,%xmm0
66a: 48 8d 3d 03 01 00 00 lea 0x103(%rip),%rdi # 774 <_IO_stdin_used+0x4>
671: b8 01 00 00 00 mov $0x1,%eax
676: e8 b5 fe ff ff callq 530 <printf@plt>
printf("&a == %p\n", &a);
不涉及数值强转
67b: 48 8d 35 8e 09 20 00 lea 0x20098e(%rip),%rsi # 201010 <a>
682: 48 8d 3d f4 00 00 00 lea 0xf4(%rip),%rdi # 77d <_IO_stdin_used+0xd>
689: b8 00 00 00 00 mov $0x0,%eax
68e: e8 9d fe ff ff callq 530 <printf@plt>
printf("(int)a == %d\n", (int)a);
打印前cvttss2si
指令将a截断成int
,存放到eax
寄存器
693: f3 0f 10 05 75 09 20 movss 0x200975(%rip),%xmm0 # 201010 <a>
69a: 00
69b: f3 0f 2c c0 cvttss2si %xmm0,%eax
69f: 89 c6 mov %eax,%esi
6a1: 48 8d 3d df 00 00 00 lea 0xdf(%rip),%rdi # 787 <_IO_stdin_used+0x17>
6a8: b8 00 00 00 00 mov $0x0,%eax
6ad: e8 7e fe ff ff callq 530 <printf@plt>
printf("b == %p\n", b);
不涉及数值强转
6b2: 48 8b 05 5f 09 20 00 mov 0x20095f(%rip),%rax # 201018 <b>
6b9: 48 89 c6 mov %rax,%rsi
6bc: 48 8d 3d d2 00 00 00 lea 0xd2(%rip),%rdi # 795 <_IO_stdin_used+0x25>
6c3: b8 00 00 00 00 mov $0x0,%eax
6c8: e8 63 fe ff ff callq 530 <printf@plt>
printf("*b == %d\n", *b);
不涉及数值强转,仅更改解释方式
6cd: 48 8b 05 44 09 20 00 mov 0x200944(%rip),%rax # 201018 <b>
6d4: 8b 00 mov (%rax),%eax
6d6: 89 c6 mov %eax,%esi
6d8: 48 8d 3d bf 00 00 00 lea 0xbf(%rip),%rdi # 79e <_IO_stdin_used+0x2e>
6df: b8 00 00 00 00 mov $0x0,%eax
6e4: e8 47 fe ff ff callq 530 <printf@plt>
6e9: b8 00 00 00 00 mov $0x0,%eax
6ee: 5d pop %rbp
6ef: c3 retq
结论
对于x86-64平台,实现float
到int
强转的指令是cvttss2si
总结
当我们将值类型强转时,必定有对应的CPU指令实现,不要以为没有开销。