从汇编看c++内联函数评估求值

在c++中,一个inline函数实体,在整个class 声明未被完全看到之前,是不会被评估求值的,也就是说,对于类里面内联的成员函数本身的分析,要等到class的声明完全结束之后才开始。
下面试c++源码:

复制代码
extern int x;//外部声明的x

class X {
public:
    float getX() const {
        return x;//x绑定的是哪个?
    }
private:
    float x;//类自身的成员变量x
};

int main() {
   X xObj;
   float x;
   x = xObj.getX();
}
复制代码

依据上面的规则,内联函数getX绑定的将会是成员变量x(float型)
下面我们只看成员函数getX的汇编码:

复制代码
?getX@X@@QBEMXZ PROC                    ; X::getX, COMDAT
; _this$ = ecx

; 5    :     float getX() const {

    push    ebp
    mov    ebp, esp
    push    ecx;压栈到的目的是为了保存对象xObj的首地址(即this指针)预留空间
    mov    DWORD PTR _this$[ebp], ecx;寄存器ecx中保留xObj对象首地址,存放到刚才分配的空间

; 6    :         return x;

    mov    eax, DWORD PTR _this$[ebp];将对象xObj对象首地址给eax寄存器
    fld    DWORD PTR [eax];将对象首地址处内存内容写入到浮点数寄存器ST(0)中,作为返回值 也就是将成员变量x的值返回
                       ;可以看到,x确实绑定的是成员变量

; 7    :     }

    mov    esp, ebp
    pop    ebp
    ret    0
?getX@X@@QBEMXZ ENDP
复制代码


但是,这种规则对于成员函数的参数却不是这样。
下面是c++源码:

复制代码
typedef int length;//全局

class X {
public:
    void setI(length x)  {//length是什么类型?
        i = x;
    }
private:
    typedef float length;//类成员
    length i;
};

int main() {
   X xObj;
   xObj.setI(1.1);
}
复制代码

下面通过汇编码看成员变量i和成员函数setI的参数x到底是什么类型
下面是mian函数汇编码:

复制代码
; 13   : int main() {

    push    ebp
    mov    ebp, esp
    push    ecx;压栈寄存器ecx的目的,是为了为对象xObj预留4byte空间
               ;这里没有调用xObj默认构造器是因为编译器没有必要为其提供非无用的默认构造器

; 14   :    X xObj;
; 15   :    xObj.setI(1.1);

    push    1;将1压栈作为参数传递(虽然传递的是浮点数1.1,但是这里将参数截取为整型,说明参数length确实为int)
    lea    ecx, DWORD PTR _xObj$[ebp];将对象xObj的首地址给寄存器ecx,作为隐含参数传递给成员函数setI
    call    ?setI@X@@QAEXH@Z            ; 调用setI

; 16   : }

    xor    eax, eax
    mov    esp, ebp
    pop    ebp
    ret    0
_main    ENDP
复制代码

下面是setI函数的汇编码:

复制代码
?setI@X@@QAEXH@Z PROC                    ; X::setI, COMDAT
; _this$ = ecx

; 5    :     void setI(length x)  {//length是什么类型?

    push    ebp
    mov    ebp, esp
    push    ecx;压栈寄存器ecx是为保留xObj对象的首地址预留空间
    mov    DWORD PTR _this$[ebp], ecx;寄存器ecx中存放对象xObj首地址,存放到刚才预留的空间

; 6    :         i = x;

    fild    DWORD PTR _x$[ebp];将参数x(int 型)放入浮点数寄存器ST(0)中, 这条指令专门将整型压入浮点寄存器
    mov    eax, DWORD PTR _this$[ebp];将对象首地址给寄存器eax
    fstp    DWORD PTR [eax];将浮点数寄存器ST(0)的内容给对象首地址存内存,即给成员变量i赋值(说明i是浮点型)

; 7    :     }

    mov    esp, ebp
    pop    ebp
    ret    4
?setI@X@@QAEXH@Z ENDP
复制代码

通过汇编码可以看到,成员函数setI的参数类型在第一次遇见就绑定了类型。

posted @   chaoguo1234  阅读(638)  评论(2编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示