做了几个小实验以验证 static_cast 的各种行为,与各位看官共享。
首先是最简单的,来源数据类型与目标数据类型的大小不等。
1
2
| char src = 2;
int dst = static_cast<int>(src); |
1
2
3
| mov byte ptr [src],2
movsx eax,byte ptr [src]
mov dword ptr [dst],eax |
1
2
| int src = 2;
char dst = static_cast<char>(src); |
1
2
3
| mov dword ptr [src],2
mov al,byte ptr [src]
mov byte ptr [dst],al |
当浮点数加入这个游戏的时候,所有的事情就变得有意思起来了。
1
2
| float src = 2.0;
int dst = static_cast<int>(src); |
1
2
3
4
5
| fld dword ptr [__real@40000000 (4020ECh)]
fstp dword ptr [src]
fld dword ptr [src]
call _ftol2_sse (401030h)
mov dword ptr [dst],eax |
1
2
| int src = 2;
float dst = static_cast<float>(src); |
1
2
3
| mov dword ptr [src],2
fild dword ptr [src]
fstp dword ptr [dst] |
我们看到,在由浮点数转换到整数的时候,编译器偷偷插进去了一个名为 ftol2_sse 的函数来完成这个转换。
当然,这并不是最复杂的转换,最复杂的转换当属有多重继承参与的类对象指针之间的转换:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| class B1
{
public:
virtual void foo1(void) { /* Dummy */ }
protected:
int m_b1;
};
class B2
{
public:
virtual void foo2(void) { /* Dummy */ }
protected:
int m_b2;
};
class D : public B1, public B2
{
public:
virtual void foo3(void) { /* Dummy */ }
protected:
int m_b3;
};
D d;
D* src = &d;
B2* dst = static_cast<B2*>(src); |
1
2
3
4
5
6
7
8
9
10
11
| lea eax,[d]
mov dword ptr [src],eax
cmp dword ptr [src],0
je main+28h (401028h)
mov eax,dword ptr [src]
add eax,8
mov dword ptr [ebp-60h],eax
jmp main+2Fh (40102Fh)
mov dword ptr [ebp-60h],0
mov ecx,dword ptr [ebp-60h]
mov dword ptr [dst],ecx |
在这里面,除了对指针有效性进行检查之外,还根据 C++ 对象的布局做了特殊处理。简而言之,对于 D 的实例 d 而言,它的内存布局应该是类似下面这个样子:

因此,当把 d 转换为 B2 类型的指针时,需要将 d 的实际地址加 8(汇编代码的第 6 行),也就是将指针指向 d 之中 B2 的子对象。
有个问题麻烦下马哥:(src2);(src3);
static_cast,有什么用啊?
我做了一些小测试,从反汇编里面看貌似没有区别。谢谢。
int src2=2;
004113F0 mov dword ptr [src2],2
char dst3=static_cast
004113F7 mov al,byte ptr [src2]
004113FA mov byte ptr [dst3],al
char dst4=(char)src2;
004113FD mov al,byte ptr [src2]
00411400 mov byte ptr [dst4],al
——————————————————————
float src3=2.0;
00411403 fld dword ptr [__real@40000000 (41573Ch)]
00411409 fstp dword ptr [src3]
int dst5=static_cast
0041140C fld dword ptr [src3]
0041140F call @ILT+220(__ftol2_sse) (4110E1h)
00411414 mov dword ptr [dst5],eax
int dst6=(int)src3;
00411417 fld dword ptr [src3]
0041141A call @ILT+220(__ftol2_sse) (4110E1h)
0041141F mov dword ptr [dst6],eax
——————————————————————
简单说来,(type)var 是 C 遗留下来的用法,C++ 之中应当使用 static_cast 来代替,仅此而已。
谢谢马哥。。
太专业了,还是更喜欢看你的旅游的几篇文章。。。