c++ 函数

本文深入探讨C++中的函数,包括带默认参数的函数,其从右向左赋值规则,以及内联函数如何减少调用开销。接着,详细介绍了函数重载的概念,C++与C之间的函数调用差异,以及__cplusplus宏在跨语言调用中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 程序示例

#include "phc.h"
#include <iostream>
using namespace std;

int sum(int a, int b) {
    return a + b;
}

int main() {
    int a = 10;
    int b = 20;
    int ret = sum(a, b);
    cout << "ret:" << ret << endl;
    return 0;
}

2. 带形参默认值的函数

int sum(int a=10, int b=20)
  • 给默认值的时候,必须是从右向左
  •  回顾,函数调用过程中的汇编指令
int ret = sum(a, b);

/*
mov eax, dword ptr[ebp-8];
push eax
mov ecx, dword prt[ebp-4];
push ecx
call sum
*/

ret = sum(a); (在这里没有传b,作为形参,所以,直接将b的默认值进行压栈)
/*
push 14H 
mov ecx, dword prt[ebp-4];
push ecx
call sum
*/

ret = sum(); (在这里没有传a ,b, 所以,直接将a,b的默认值进行压栈)
/*
push 14H
push 0AH
call sum
*/


  • 带默认值和函数和普通函数相比,效率有没有增长?
    • 根据压栈的过程来看,不用再进行数据传输
    • 注: 以下直接传值的调用方式和原来相比,没有任何的区别
int ret = sum(20, 40);
  • 定义处可以给形参默认值,声明也可以给形参默认值, 如下:
int sum(int a=10, b=20);
  •  形参给默认值的时候,不管是定义处给,还是声明处给,形参默认值只能出现一次
//---------=编译错误
int sum (int a=10, int b=20);
int sum (int a=10, int b=20);


//-=-------编译错误(只能从右向左赋值)
int sum (int a=10, int b);
int sum (int , int b=20);

//---------正确
int sum (int a, int b=20);
int sum (int a=10, int b);

3.inline内联函数

int sum (int a, int b) {
    return a + b;
}

int main ()
{
    int a = 10;
    int b = 20;

    int ret = sum(a, b);
}
  • 函数的标准调用过程        参数压栈,函数栈帧的开辟和回退过程
  • 函数的调用过程经常被称为函数的调用开销
    • 回顾:函数调用过程中函数体内语句对应的汇编指令
x + y

/* 
mov eax, dword ptr[ebp + 0Ch] --> 访问y的值
add eax, dword ptr[ebp + 8] --> 访问x的值,然后再加上y的值

缺点:执行简单的加法只需要两条汇编指令,但是函数调用开销要大于函数体内语句的执行开销

  • 优化方式:使用内联函数
inline int sum (int x, int y)
{
    return x + y;
}
  • 内联函数的作用
    • 优点:inline函数不用再生成相应的函数符号 *.o, sum_int_int, .text
ret = sum(a, b);

// 使用内联函数之后的效果: ret = a + b;
// 相当于直接将sum函数的语句直接在主函数进行了展开
  • 注:不是所有的inline都会被编译器处理成内联函数, 如: 递归
  • 注:inline只是建议编辑器将函数处理成内敛函数,是否真的处理成内联函数由编辑器决定
  • 如果函数体十分复杂,函数调用开销可以忽略,则也没有必要使用inline
  • debug版本上,inline是不起作用的(依然会和普通函数一样产生函数调用开销); inline只有在release版本下才能出现

问题:内联函数和普通函数的区别

  • 内联函数在编译过程中,没有函数的调用开销,在函数的调用点直接把函数的代码进行展开处理了+说明什么是函数的调用开销
  • 函数在短时间内进行多次调用且函数非常简单,则可以将该函数设置成内联函数
  • 内联函数如果编译成功,是不会在符号表中生成符号的
  • inline函数只是程序设计者给编译器的建议,并不是添加inline关键字就一定会处理成内联函数,比如递归(编译器并不清楚需要递归多少次)

如何判断是否由成功编译成内联函数呢?

  • 可以看可执行文件的符号表,没有生成相应的函数符号,说明编译器按照内联函数进行的编译

4. 函数重载

  • 定义

bool compare(int a, int b) {
    cout << "compare_int_int" << endl; // 符号名
    return a > b;
}

bool compare(double a, double b) {
    cout << "compare_double_double" << endl; // 符号名
    return a > b;
}

bool compare(const char *a, const char *b) {
    cout << "compare_char*_char*" << endl; // 符号名
    return strcmp(a, b) > 0;
}

int main () {
    compare (10, 20);
    compare (10.0, 20.0);
    compare ("aaa", "bbb");
    return 0;
}
  • 为什么C++能支持函数重载,C语言不支持函数重载
    • C++代码产生函数符号的时候,符号是由函数名和参数列表类型共同组成的
    • C代码在产生函数符号的时候,符号只由函数名来决定
  • 说明:在main函数中进行声明如下
    • 报错
      • double参数类型的compare,和char* 类型的compare都会报错
      • 原因:声明之后,在main函数的作用域当中,只能使用int参数类型的compare函数
int main () {
    
    bool compare (int a, int b); 

    compare (10, 20);
    compare (10.0, 20.0);
    compare ("aaa", "bbb");
    return 0;
}

满足函数重载必须处在同一个作用域

  • 说明:函数重载参数为const volatile的时候
     
    • 明确变量类型
int a = 10;
cosnt int a = 10;

cout << typeid(a).name << endl;
cout << typeid(b).name << endl;

结果:输出类型都是int, 对编辑器来说,都是整型

void func(int a)
void func(const int a) {}

int main () {
    int a = 10;
    const int b = 10;
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    return 0;
}

结果:重载都是按照参数类型为int进行编译,编译报错==》重定义, volatile也是同样的情况

4.1 C++调用C & C调用C++

1)C++调用C代码

  • C源文件

编译过程中生成的函数符号:// sum        .text 

  • C++源文件 

编译过程中生成的符号 sum_int_int, 由于是外部引用,所有类型为*UND*

编译器在寻找sum_int_int的时候, 或找不到,因为C源文件中的函数名为sum

无法直接调用,解决方案如下:

extern "C" {
    int sum(int a, int b);
}

int main () {
    int ret = sum(10, 20);
    cout << "ret:" << ret << endl;
    return 0;
}

 在C++文件中,利用extern对函数声明进行包含,指定编译器按照C语言的方式生成函数符号: sum *UND*

2)C调用C++代码

  • C++源文件

  •  C源文件

 ​​​​产生的报错以及原因同1),解决方案如下:

 注:在C++文件当中利用extern将函数进行包含,C语言中没有这种语法

3)__cplusplus 宏在C调用C++程序中的作用

  • __cplusplus是C++内置的宏,如__FILE__ (输出文件名)__LINE__(输出行数)

  • 在extern前后添加宏的作用:
    • 如果该文件是以C文件的方式进行编译,则因为C不识别extern, 所以宏不执行,因此,即为普通的C文件方式编译
    • 如果是以C++文件的方式进行编译,宏会执行,通知编译器按照C语言的编译方式产生函数符号
    • 优点:使用这种方式,能够方便其他的C语言文件对该源文件的函数进行引用,无论该源文件是C文件还是C++文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值