立山 发表于 2024-12-24 22:12:03

C++----------函数的调用机制


[*] 栈帧的创建与烧毁

[*]栈帧创建过程

[*]当一个函数被调用时,系统会在步伐的栈空间中为该函数创建一个栈帧。起首,会将函数的返回地址(即调用该函数的下一条指令的地址)压入栈中,这确保函数执行完后能回到精确的位置继续执行后续代码。然后,根据函数参数的类型和数量,将参数的值(如果是值通报)或引用(如果是引用通报)或指针(如果是指针通报)依次压入栈中。最后,为函数内部定义的局部变量分配内存空间。
[*]例如,有函数void func(int a, int b),当调用func(3, 4)时,系统会先将返回地址压入栈,然后将3和4压入栈作为参数a和b的值,接着为func函数内部可能定义的局部变量预留空间。

[*]栈帧烧毁过程

[*]当函数执行竣事(遇到return语句或者函数体的最后一个花括号)时,栈帧会被烧毁。起首,会开释函数内部局部变量所占用的内存空间。然后,根据函数的返回值类型(如果有返回值),将返回值复制到一个临时存储位置(如果是基本数据类型)或者通过移动语义(如果是对象)将返回值通报给调用者。最后,将栈顶指针规复到调用该函数之前的位置,如许就相称于烧毁了这个栈帧,同时将返回地址从栈中弹出,步伐继续从返回地址处执行。


[*] 参数通报方式的细节

[*]值通报深入明白

[*]复制过程:在值通报中,实际参数的值会被完整地复制到函数的情势参数中。对于基本数据类型,这是一个简朴的字节复制过程。例如,通报一个int类型的参数,会将该int值的字节序列复制到函数参数对应的内存位置。对于自定义布局体等复杂类型,会递归地复制每个成员变量的值。
[*]对原始参数的影响:由于是复制了一份新的值给函数参数,所以在函数内部对参数的修改不会影响到原始的实际参数。例如,对于函数void modify(int num),在函数内部num = 10,但如果在函数外部有int original_num = 5; modify(original_num);,original_num的值依然是5。

[*]引用通报深入明白

[*]引用的本质:引用在底层实现上可以看作是一个指针常量,它总是指向被引用的对象。当进行引用通报时,实际上通报的是对象的地址,但是在语法上使用起来就像使用原始对象一样。例如,int& ref = original_num;,ref和original_num在内存中指向同一个位置。
[*]对原始参数的影响:因为引用和原始对象共享同一块内存空间,所以在函数内部通过引用对参数进行操纵,实际上就是对原始对象进行操纵。例如,函数void modifyByReference(int& num),在函数内部num = 10,如果在函数外部有int original_num = 5; modifyByReference(original_num);,original_num的值会变为10。

[*]指针通报深入明白

[*]指针的操纵方式:指针通报是把变量的地址通报给函数。在函数内部,通过解引用指针(使用*操纵符)来访问和修改指针所指向的变量的值。例如,函数void modifyByPointer(int* ptr),当通报&original_num作为参数时,在函数内部通过*ptr = 10来修改original_num的值。
[*]与引用通报的区别:固然指针通报和引用通报都可以在函数内部修改原始变量的值,但指针通报必要显式地解引用指针来访问变量,而引用通报在语法上更简便,直接使用引用变量就可以访问和修改原始变量。另外,指针可以在函数内部重新赋值指向其他对象,而引用一旦初始化就不能再引用其他对象。


[*] 函数返回值通报机制的细节

[*]基本数据类型返回值通报

[*]复制返回值:当函数返回一个基本数据类型(如int、double等)的值时,函数会将返回值复制到一个临时存储位置。这个临时存储位置可能是一个寄存器或者栈中的某个位置,具体取决于编译器和硬件架构。例如,对于函数int add(int a, int b) { return a + b; },当调用add函数时,盘算a + b的结果会被复制到这个临时位置,然后这个值再被赋值给接收返回值的变量(如int result = add(3, 5);中的result)。

[*]对象返回值通报

[*]返回值优化(RVO):当函数返回一个对象时,C++编译器可能会应用返回值优化。在没有返回值优化的情况下,函数会先创建一个临时对象,将函数内部的对象复制到这个临时对象中(通过调用复制构造函数),然后返回这个临时对象。但是,通过返回值优化,编译器可以直接将函数内部的对象构造到接收返回值的对象的内存空间中,制止了不须要的复制操纵。例如,对于函数MyClass createObject(),如果MyClass是一个自定义类,在合适的条件下,编译器会直接将createObject函数内部构造的MyClass对象构造到接收返回值的MyClass对象中,而不是先复制到一个临时对象再进行赋值。
[*]移动语义(Move Semantics):如果编译器没有进行返回值优化,除了复制构造函数外,C++还提供了移动构造函数来更高效地处理对象返回值。移动构造函数答应将一个对象的资源(如动态分配的内存)“移动”到另一个对象中,而不是进行复制。例如,对于一个包罗动态分配数组的类,移动构造函数可以将数组的指针从一个对象转移到另一个对象,制止了重新分配内存和复制数组元素的开销。当函数返回对象时,编译器可能会优先调用移动构造函数(如果定义了)来提高服从。
https://i-blog.csdnimg.cn/direct/1da2de2d5b88450895b6c29ddcc9c68e.png#pic_center
下面是一些代码示例,用来展示 C++ 函数的调用机制,包括栈帧、参数通报以及返回值通报相关内容:


#include <iostream>

// 用于展示栈帧中的局部变量
void localVarDemo() {
    int localVar = 10;
    std::cout << "局部变量 localVar 的值: " << localVar << std::endl;
}

// 值传递示例
void valuePassing(int num) {
    num = 20;
    std::cout << "值传递中函数内 num 的值: " << num << std::endl;
}

// 引用传递示例
void referencePassing(int& num) {
    num = 30;
    std::cout << "引用传递中函数内 num 的值: " << num << std::endl;
}

// 指针传递示例
void pointerPassing(int* num) {
    *num = 40;
    std::cout << "指针传递中函数内 *num 的值: " << *num << std::endl;
}

// 返回基本数据类型示例
int returnBasicType() {
    return 50;
}

// 简单类定义,用于展示对象返回
class MyClass {
public:
    int data;
    MyClass(int value) : data(value) {}
};

// 返回对象示例
MyClass returnObject() {
    MyClass obj(60);
    return obj;
}

int main() {
    // 栈帧中的局部变量演示
    localVarDemo();

    int value = 15;
    // 值传递
    valuePassing(value);
    std::cout << "值传递后 value 的值: " << value << std::endl;

    // 引用传递
    referencePassing(value);
    std::cout << "引用传递后 value 的值: " << value << std::endl;

    // 指针传递
    pointerPassing(&value);
    std::cout << "指针传递后 value 的值: " << value << std::endl;

    // 返回基本数据类型
    int basicResult = returnBasicType();
    std::cout << "返回基本数据类型的结果: " << basicResult << std::endl;

    // 返回对象
    MyClass objResult = returnObject();
    std::cout << "返回对象的 data 值: " << objResult.data << std::endl;

    return 0;
}
代码表明:


[*]localVarDemo 函数展示了在栈帧中定义和使用局部变量,函数执行时会创建栈帧来存放 localVar。
[*]valuePassing 是值通报示例,函数接收参数的副本,所以内部修改不影响外部变量。
[*]referencePassing 利用引用通报,函数形参是实参的别名,内部修改会同步到外部变量。
[*]pointerPassing 通过指针通报,通报变量地址,函数可通过解引用修改外部变量的值。
[*]returnBasicType 返回基本数据类型,返回值被复制给 basicResult。
[*]returnObject 返回自定义类对象,编译器可能会进行返回值优化,高效地把对象通报给 objResult。
https://i-blog.csdnimg.cn/direct/5debc63dba534d968f907144474da802.png#pic_center

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: C++----------函数的调用机制