函数:可以多次调用的一段代码;
函数头: 函数名称 形式参数 返回类型
函数体:包含具体逻辑的语句块
函数声明:声明只包含函数头,不包含函数体,通常放在头文件中,函数声明可以多次定义
函数定义:函数定义通常只能定义一次 -----一次定义原则 可以多次声明
函数定义对应一段汇编代码 若多次定义编译器不知如何选
int add(int x,int y); 声明此处有;
int main()
{
add(2,3); 编译器顺序执行 所以需要声明
}
int add(int x,int y)
{
return x+y;
}
函数调用:
需要提供函数名与实际参数
实际参数拷贝初始化形式参数
返回值会拷贝给函数调用者
栈 帧 结构 ( stack frame )
栈帧结构(程序调用栈结构)详解_途~乐的博客-CSDN博客_栈帧结构栈帧结构(程序调用栈结构)详解知识点扫描ESP:栈指针寄存器(extended stack pointer),放着一个指针,该指针永远指向系统栈最上面一个栈帧(栈帧不理解没关系)的栈顶。EBP:基址指针寄存器(extended base pointer),放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。EIP:指令寄存器(extended instruction pointer),存储CPU下一个要执行的指令的地址。简介栈帧结构就是在程序运行中发生函数调用时,会产生一个调用栈,调用栈https://blog.csdn.net/weixin_41355162/article/details/109478638
函数的外部链接:
nm 可执行程序 ----------查看程序的外部链接
nm 可执行程序 | c++filt -t ------可视化信息
#include
using namespace std;
将add函数编译为C语言形式的链接 用nm a.out 查看
extern "C" nm a.out | c++filt -t 过滤为C++方式的信息
int Add(int x,int y)
{
return x+y;
} C++有函数重载 编译为链接时函数名会被处理,对C语言兼容不友好
extern "C"将函数编译为支持C语言的链接
int Sub(int x,int y)
{
return x-y;
}
int main()
{
int Z=Add(2,3);
int Q=Sub(2,3);
cout<
函数的参数可以包含零或者多个形参.
包含0个形参可以用void标记 或者空 void fun()/void fun(void)
对于非模板函数,每个形参可以没有名称,但是必须有确定的类型, void fun(int,int){..... }
形参名称的变换不会引入函数不同的版本 类型的变化会引入函数重载
实参到形参的拷贝求值顺序不一定 c++17强制省略复制临时对象
void fun(int x,int y){}
int main()
{
fun(1,2) 1 2拷贝初始化x y由编译器决定 使性能最好
}
函数传值 传址 传引用
传值
void fun(int par) int par=arg
{
++par;
}
int main()
{
int arg=3;
fun(arg); arg不会改变
cout<
函数传参数过程中会引入类型的退化
void fun(int* par){....}
int a[3];
fun(a) a退化为指针
void fun(int (&par)[3]){....}
int a[3];
fun(a) 加入引用 防止a退化为指针
变长参数:
initializer_list
可变长度模板参数
使用省略号表示形式参数
void fun(std::initializer_listpar){........}
fun({1,2,3,4,5});
fun({1,2,3,"abc",4}); error 类型错误
initializer_list 本质是一个指针 指向开头元素和结尾后一个元素
缺省实参的定义:
1.如果函数具有缺省实参,那么右侧的形参都必须具有缺省实参 原因见3
void fun(int x,int y=1,int z=9){}
void fun(int x,int y=1,int z){} error
2.在一个翻译单元中,每个形参的缺省实参只能定义一次
(声明和定义只能定义一次 一般定义在声明中)
void fun(int x,int y=2,int z=9); void fun(int x,int y,int z){.........}
void fun(int x,int y=2,int z=9); void fun(int x,int y=2,int z=9){.........} error
因为函数满足 一次定义原则 可以多次声明 所以
void fun(int x,int y,int z=9); void fun(int x,int y=2,int z); void fun(int x,int y,int z){........} 3.具有缺省实参调用时,传入实参会从左到右的顺序匹配形参
(所以1必须满足)
void fun(int x,int y=1,int z=9){} fun(1,2,3);从左到右一次匹配
4.缺省实参为对象时,实参的缺省值会岁对象的变化而变化
int x=3; void fun(int y=x){cout<
int x=3; void fun(int y=x){cout<
int x=3; void fun(int y=x){cout<
main.cpp
#include
using namespace std;
#include"header.h"
void source();
void fun(int x,int y,int z) //实参缺省值 写在声明中 函数根据声明中的值 执行函数体的内容
{
cout<
argc 非负数 表示从程序运行环境传递给程序的实参个数
argv 数组指针 指向一个包含argc+1个元素的数组的首地址 数组末尾元素是空地址
主函数 - cppreference.comhttps://zh.cppreference.com/w/cpp/language/main_function./demo 1 2 3 nullptr
argv[0] argv[1] argv[2] argv[3] argv[4]
#include
using namespace std;
int main(int argc,char* argv[])
{
cout<<"argc="<
函数体形成域:
形成语句块
函数体执行完成时的返回:
1. 隐式返回
void fun() { } fun();
2.显示返回 return
return ;
return 表达式;
return 初始化列表
void fun()
{
cout<<"Hello"<fun()
{
return {1,2,3,4,5}; 返回列表
}
小心返回时自动对象的引用或指针
int &fun()
{
int x=9;
return x;
}
int &ref=fun(); error 返回值x只生存在fun中 ref绑定了一个即将销毁的对象
int *fun()
{
int x=9;
return &x;
}
int *ptr=fun(); error 返回值x只生存在fun中 ptr指向了一个即将销毁的对象
将int x=9;变为 static int x=9 则不再报错 x是一个局部静态变量 在程序执行完才销毁
返回类型表示函数计算结果的类型 可以为void
返回类型的书写方法:
1.位于函数头的前部----经典写法
2.C++11引入:位于函数头的后部
3.C++14引入:返回类型的自动推导 可以使用constexpr if 构造具有不同返回类型的函数
auto fun(int a,int b)->int 位于函数头后部
{
return a+b;
}
auto fun(int a,int b) 自动推导类型
{
return a+b;
}
decltype(auto) fun(bool input)
{
int x=3;
return x; ----int类型
return (x); ---(x)为表达式 左值 为int&类型
constexpr bool value=false; constexpr定义一个编译期的常量
auto fun()
{
if constexpr (value) 编译器决定返回不同的类型
{
return 1;
}
else
{
return 3.14;
}
返回类型与结构化绑定 ---语法糖 c++17
struct Str
{
int x;
int y;
}
Str fun()
{
return Str();
}
int mian()
{
Str s;
s=fun();
s.x
s.y ---通常
auto &[v1,v2]=fun();
v1;
v2;
}
[[nodiscard]]属性 c++17
[[nodiscard]] int fun(int a,int b)
{
return a+b;
}
int main()
{
int x=fun(2,3); 有了[[nodiscard]]说明函数返回很重要 必须接受 否则报错
cout<
函数重载:使用相同的函数名定义多个函数,每个函数具有不同的参数列表
-不能基于不同的返回类型进行重载 ----因为返回值可以不接受 故编译器不知道调用那函数
int fun(int x){..} double fun(int x){....} error
int fun()
{
return x+1;
}
double fun(double x)
{
return x+1;
}
fun(1)
fun(2.1)
编译器如何选择正确的版本完成函数调用?
名称查找-----xxx------重载解析---------xxx----------xxx------------xxxx-------------xxx
本例重点介绍名称查找和重载解析
名称查找:
1.限定查找
-通常指定域 全局域 还是命名空间域 ::fun(); MyNS::fun();
2.非限定查找
-会进行域的逐级查找 ----名称隐藏
3.查找通常只会在已经申明的名称集合中进行 从上到下按顺序执行查找
void fun(int x) {.....}
void g()
{
fun(3);
}
void fun (double x)
{....}
int main()
{
g(); 执行第一个fun 因为调用个g() fun(3) 第二个fun没有生成 除非在g()前声明
}
重载解析:在名称查找后 进一步选择合适的调用函数
---过滤不能被调用的版本
参数个数不对, 无法将实参转化为实参, 实参不满足形参的限制条件
---在剩余版本中查找与调用表达式最匹配的版本 匹配级别越低越好(有例外情况)
● 级别 1 :完美匹配 或 平凡转换(比如加一个 const )
● 级别 2 : promotion 或 promotion 加平凡转换
● 级别 3 :标准转换 或 标准转换加平凡转换
● 级别 4* :自定义转换 或 自定义转换加平凡转换 或 自定义转换加标准转换
● 级别 5* :形参为省略号的版本
● 函数包含多个形参时,所选函数的所有形参的匹配级别都要优于或等于其它
void fun(int x){..........}
void fun(double x){........}
fun(3); 调用第一个
void fun(int &x){........}
void fun(const int &x){.........}
int x;
fun(x); 调用第一个fun() x为左值可修改
fun(3); 调用第二个fun() 因为第一个fun 被过滤 int &x不能绑定常量
void fun(int x,int y){....}
void fun(int x,double y){..........}
fun(1,1.0) 调用第二个fun int x int y
级别: 1 完美匹配 3 标准转换
1 完美匹配 1 完美匹配
递归函数:在函数中调用自己 ----典型案例二分查找(有序表)
int binary_search(int *arr,int p,int q,int ele) {
int mid = 0;
//如果[p,q] 不存在,返回 -1
if (p > q) {
return -1;
}
// 找到中间元素所在的位置
mid = p + (q - p) / 2;
//递归的出口
if (ele == arr[mid]) {
return mid;
}
//比较 ele 和 arr[mid] 的值,缩小 ele 可能存在的区域
if (ele < arr[mid]) {
//新的搜索区域为 [p,mid-1]
return binary_search(arr, p, mid - 1, ele);
}
else {
//新的搜索区域为 [mid+1,q]
return binary_search(arr, mid + 1, q, ele);
}
}
二分查找算法(折半查找算法)二分查找算法又称折半查找算法,是在分治算法基础上实现的查找算法。本文将详细讲解二分查找算法的实现思路,还会给出二分查找算法对应的C/C++、Java、Python实现代码。http://c.biancheng.net/algorithm/binary-search.html
避免函数调用的栈帧结构,本质是一种优化方法.用inline放在函数开头,且函数必须有定义,函数从程序一次定义变为翻译单元一次定义
#include
void fun()
{
cout<<"Hello"<
main.cpp
#include
#include"header.h"
int main()
{
fun(); 两次调用fun 出现重复定义 加入inline 将fun变为翻译单元一次定义即可解决报错
}
header.h
#include
inline void fun() 加入inline定义一个内联函数 一定要定义 不能只声明
{
cout<<"Hello<
void fun2()
{
fun();
}
C++11 constexpr:验证是否为常量表达式(长篇神文)constexpr 是 C++ 11 标准新引入的关键字,不过在讲解其具体用法和功能之前,读者需要先搞清楚 C++ 常量表达式的含义。 所谓常量表达式,指的就是由多个(1)常量组成的表达式。换句话http://c.biancheng.net/view/7781.html
constexpr int x=0; 定义一个编译器的常量
constexpr int x=3;
constexpr int fun(int x) 修饰函数--常量表达式函数 函数既能在编译器也能在运行期执行
{
return x+1;
}
int main()
{
constexpr int x=fun(3);
returnx;
}
获得在编译阶段计算出结果的能力,并不代表 constexpr 修饰的表达式一定会在程序编译阶段被执行,具体的计算时机还是编译器说了算
consteval:只能在编译期执行
consteval int fun(int x)
{
return x+1;
}
int main()
{
constexpr int x=3; 若无constexpr则报错 因为consteval只能在编译器运行
int y=fun(x);
}
函数类型与函数指针
函数类型
int fun(int x)
{
return x+1;
}
using k=int(int);
k fun; 相当于函数声明
int(int) fun ; error
函数指针
int inc(int x)
{
return x+1;
}
using K=int(int);
int Twice(K*fun,int x) 高阶函数
{
int temp=(*fun)(x);
return temp*2;
}
int main()
{
K*fun=&inc;
cout<<(*fun)(100)<
函数指针与重载
将函数指针作为函数参数
将函数指针作函数返回值
函数指针与重载
void fun(int)
{.......}
int main()
{
auto x=fun(); x为函数指针类型void(*)(int)
}
void fun(int){...}
void fun(int ,int){...}
int main()
{
using K=void(int);
K* x=fun(); 显示写出x的类型
}
做函数返回值
void inc(int x)
{
return x+1;
}
void dec(int x)
{
return x-1;
}
auto fun(bool input)
{
if(input)return inc;
else return dec; inc dec 做返回值均转化为函数指针
}
int main()
{
cout<<(*fun(true))(100)<
持续更新...............................