C++学习笔记05--函数
01函数基础
函数:可以多次调用的一段代码;
函数头: 函数名称 形式参数 返回类型
函数体:包含具体逻辑的语句块
函数声明:声明只包含函数头,不包含函数体,通常放在头文件中,函数声明可以多次定义
函数定义:函数定义通常只能定义一次 -----一次定义原则 可以多次声明
函数定义对应一段汇编代码 若多次定义编译器不知如何选
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<iostream>
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<<Z<<endl;cout<<Q<<endl;}
02函数详解
参数
函数的参数可以包含零或者多个形参.
包含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<<arg<<endl;
}传址
void fun(int* par) int *par=&arg
{++par;
}int main()
{int arg=3;fun(&arg); arg改变cout<<arg<<endl;
}传引用
void fun(int& par) int &par=arg
{++par;
}int main()
{int arg=3;fun(arg); arg改变cout<<arg<<endl;
}
函数传参数过程中会引入类型的退化
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_list<int>par){........}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<<y<<'\n';} int x=4;fun(); ----3
int x=3; void fun(int y=x){cout<<y<<'\n';} x=4;fun(); ----4
int x=3; void fun(int y=x){cout<<y<<'\n';} fun(); ----3
main.cpp#include<iostream>
using namespace std;
#include"header.h"void source();
void fun(int x,int y,int z) //实参缺省值 写在声明中 函数根据声明中的值 执行函数体的内容
{cout<<x+y+z<<endl;
}
int main()
{fun(1); -----3source(); -----6
}source.cppvoid fun(int x,int y=2,int z=2);
void source()
{fun(2);
}header.h
void fun(int x,int y=1,int z=1);
int argc char**argv/ char *argv[ ]
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<iostream>
using namespace std;int main(int argc,char* argv[])
{cout<<"argc="<<argc<<endl;//argc 执行程序时表示输入的个数for(int i=0;i<argc;++i){cout<<*(argv+i)<<'\t'; cout<<argv[i]<<endl; //argv[i]指针索引 argv是一个指针 指向了一个 argc+1 大小的数组}
}
函数体
函数体形成域:
形成语句块
函数体执行完成时的返回:
1. 隐式返回
void fun() { } fun();
2.显示返回 return
return ;
return 表达式;
return 初始化列表
void fun()
{cout<<"Hello"<<endl;return ; 显示返回空语句cout<<"world<<endl; 不再执行
}int fun()
{cout<<"Hello"<<endl;return 1; 返回表达式
}std::vector<int>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.xs.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<<x<<endl;
}
03函数重载与重载解析
函数重载:使用相同的函数名定义多个函数,每个函数具有不同的参数列表
-不能基于不同的返回类型进行重载 ----因为返回值可以不接受 故编译器不知道调用那函数
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 完美匹配
04函数其他
递归函数
递归函数:在函数中调用自己 ----典型案例二分查找(有序表)
int binary_search(int *arr,int p,int q,int ele) {int mid = 0;//如果[p,q] 不存在,返回 -1if (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<iostream>
void fun()
{cout<<"Hello"<<endl;
}
int main()
{fun(); 调用fun()本质 是将fun()非简单替换为cout<<"Hello"<<endl;
}
main.cpp
#include<iostream>
#include"header.h"
int main()
{fun(); 两次调用fun 出现重复定义 加入inline 将fun变为翻译单元一次定义即可解决报错
}header.h
#include<iostream>
inline void fun() 加入inline定义一个内联函数 一定要定义 不能只声明
{cout<<"Hello<<endl;
}source.cpp
#include<iostream>
void fun2()
{fun();
}
constexpr C++11 14做了修改
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 C++20consteval 说明符 (C++20 起) - cppreference.comhttps://zh.cppreference.com/w/cpp/language/consteval
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)<<endl; 101cout<<(inc(100))<<endl; 101cout<<Twice(&inc,100)<<endl; 202
}auto fun=inc; 函数数组不能复制 默认转化为指针 fun为函数指针类型 int(*)(int)
using K=int(int);
K* fun; 等价于int(*fun)(int) 函数指针void demo(K input) /(K*input)
{......}
demo(inc); 传入指针 接受也是指针
demo(&inc);
函数指针与重载
将函数指针作为函数参数
将函数指针作函数返回值
函数指针与重载
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)<<endl; 101
}
持续更新...............................
C++学习笔记05--函数相关推荐
- 翻转电平函数实现LED闪烁-STM32电控学习笔记05
翻转电平函数实现LED闪烁-STM32电控学习笔记05 day5:2022/9/23 [函数介绍] 在前面帖子大致了解了一下HAL_GPIO_WritePin()函数和HAL_Delay()函数的用法 ...
- opencv学习笔记05
原创:opencv学习笔记05 OpenCV-Python教程:40.ORB https://www.jianshu.com/p/49a84ddef11d ORB最重要的事情是它是OpenCV实验室出 ...
- Vue学习笔记05 组件的自定义事件-组件通信-$nextTick-脚手架解决ajax跨域-插槽-过渡动画
文章目录 Vue学习笔记05 父组件给子组件传值 注意点 子组件给父组件传值 父组件接受子组件的传值 通过函数 组件的自定义事件 事件绑定的第一种写法 @或v-on 事件绑定的第二种写法:使用ref ...
- ESP32 单片机学习笔记 - 05 - AP/Smart Config
ESP32 单片机学习笔记 - 05 - AP/Smart Config 终于把感觉必要的基础外设学完了,开始学esp32的主要特色功能--物联网~~?(大概) 一.WIFI热点 AP模式 编程指南: ...
- python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用
本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...
- JavaWeb黑马旅游网-学习笔记05【分类数据展示功能】
Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...
- JavaWeb-综合案例(用户信息)-学习笔记05【分页查询功能】
Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb-综合案例(用户信息)-学习笔记01[列表查询] JavaWeb-综合案例(用户信息)-学习笔记02[登录功能] JavaWeb-综合案 ...
- JavaScript学习笔记05【高级——DOM对象】
w3school 在线教程:https://www.w3school.com.cn JavaScript学习笔记01[基础--简介.基础语法.运算符.特殊语法.流程控制语句][day01] JavaS ...
- MySQL学习笔记05【多表操作、三大范式、数据库的备份和还原】
MySQL 文档-黑马程序员(腾讯微云):https://share.weiyun.com/RaCdIwas 1-MySQL基础.pdf.2-MySQL约束与设计.pdf.3-MySQL多表查询与事务 ...
- Python学习笔记:函数(Function)
Python学习笔记:函数(Function) 一.函数基本概念 函数是Python里组织与重用代码最重要的方法.一般来说,如果你期望多次重复相同或相似的代码,写一个可重用的函数可能是值得的.函数通过 ...
最新文章
- 【优秀作业】粒子群算法(Python)
- IT十八掌作业_java基础第十一天_集合
- JavaScript学习笔记(6)BOM(浏览器对象模型)pc网页特效(停止学习)
- CentOS 6.5 升级 PHP 到5.6
- Python类的实例属性详解
- js 错误/异常处理
- python有趣的面试题_python面试题目
- 常见位操作:获取,设置,清零
- php smtp邮件类,php利用smtp类发送邮件
- 你是怎么看待那些拿几千块钱炒股的人?
- 补发《超级迷宫》站立会议八
- 如何手动优化机器学习模型超参数
- [导入]一个Form验证的方案
- 喷墨打印机一体机清零软件使用图解
- 3dmax快捷键大全
- 宇视服务器硬件如何安装,宇视科技无需后端平台与服务器支撑 即可形成小型人脸识别方案...
- Scan Context回环检测解读和使用
- 2021年电子竞赛四天三夜征程—-信号失真度测量装置(A题)
- Event Bubbling Exampl
- 未来计算机的图片大全集,小学生未来的机器人儿童画图片大全