browser icon
You are using an insecure version of your web browser. Please update your browser!
Using an outdated browser makes your computer unsafe. For a safer, faster, more enjoyable user experience, please update your browser today or try a newer browser.

关于 static_cast 的几个实验

Posted by on 2009 年 06 月 25 日

做了几个小实验以验证 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 的子对象。

4 条评论

  • At 2009.06.26 16:48, said:

    有个问题麻烦下马哥:
    static_cast,有什么用啊?
    我做了一些小测试,从反汇编里面看貌似没有区别。谢谢。
    int src2=2;
    004113F0 mov dword ptr [src2],2
    char dst3=static_cast(src2);
    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(src3);
    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
    ——————————————————————

    • At 2009.06.26 17:03, 李马 said:

      简单说来,(type)var 是 C 遗留下来的用法,C++ 之中应当使用 static_cast 来代替,仅此而已。

      • At 2009.06.26 18:58, said:

        谢谢马哥。。

        • At 2009.06.27 02:07, 追麦 said:

          太专业了,还是更喜欢看你的旅游的几篇文章。。。

          (Required)
          (Required, will not be published)