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--函数相关推荐

  1. 翻转电平函数实现LED闪烁-STM32电控学习笔记05

    翻转电平函数实现LED闪烁-STM32电控学习笔记05 day5:2022/9/23 [函数介绍] 在前面帖子大致了解了一下HAL_GPIO_WritePin()函数和HAL_Delay()函数的用法 ...

  2. opencv学习笔记05

    原创:opencv学习笔记05 OpenCV-Python教程:40.ORB https://www.jianshu.com/p/49a84ddef11d ORB最重要的事情是它是OpenCV实验室出 ...

  3. Vue学习笔记05 组件的自定义事件-组件通信-$nextTick-脚手架解决ajax跨域-插槽-过渡动画

    文章目录 Vue学习笔记05 父组件给子组件传值 注意点 子组件给父组件传值 父组件接受子组件的传值 通过函数 组件的自定义事件 事件绑定的第一种写法 @或v-on 事件绑定的第二种写法:使用ref ...

  4. ESP32 单片机学习笔记 - 05 - AP/Smart Config

    ESP32 单片机学习笔记 - 05 - AP/Smart Config 终于把感觉必要的基础外设学完了,开始学esp32的主要特色功能--物联网~~?(大概) 一.WIFI热点 AP模式 编程指南: ...

  5. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  6. JavaWeb黑马旅游网-学习笔记05【分类数据展示功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  7. JavaWeb-综合案例(用户信息)-学习笔记05【分页查询功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb-综合案例(用户信息)-学习笔记01[列表查询] JavaWeb-综合案例(用户信息)-学习笔记02[登录功能] JavaWeb-综合案 ...

  8. JavaScript学习笔记05【高级——DOM对象】

    w3school 在线教程:https://www.w3school.com.cn JavaScript学习笔记01[基础--简介.基础语法.运算符.特殊语法.流程控制语句][day01] JavaS ...

  9. MySQL学习笔记05【多表操作、三大范式、数据库的备份和还原】

    MySQL 文档-黑马程序员(腾讯微云):https://share.weiyun.com/RaCdIwas 1-MySQL基础.pdf.2-MySQL约束与设计.pdf.3-MySQL多表查询与事务 ...

  10. Python学习笔记:函数(Function)

    Python学习笔记:函数(Function) 一.函数基本概念 函数是Python里组织与重用代码最重要的方法.一般来说,如果你期望多次重复相同或相似的代码,写一个可重用的函数可能是值得的.函数通过 ...

最新文章

  1. 【优秀作业】粒子群算法(Python)
  2. IT十八掌作业_java基础第十一天_集合
  3. JavaScript学习笔记(6)BOM(浏览器对象模型)pc网页特效(停止学习)
  4. CentOS 6.5 升级 PHP 到5.6
  5. Python类的实例属性详解
  6. js 错误/异常处理
  7. python有趣的面试题_python面试题目
  8. 常见位操作:获取,设置,清零
  9. php smtp邮件类,php利用smtp类发送邮件
  10. 你是怎么看待那些拿几千块钱炒股的人?
  11. 补发《超级迷宫》站立会议八
  12. 如何手动优化机器学习模型超参数
  13. [导入]一个Form验证的方案
  14. 喷墨打印机一体机清零软件使用图解
  15. 3dmax快捷键大全
  16. 宇视服务器硬件如何安装,宇视科技无需后端平台与服务器支撑 即可形成小型人脸识别方案...
  17. Scan Context回环检测解读和使用
  18. 2021年电子竞赛四天三夜征程—-信号失真度测量装置(A题)
  19. Event Bubbling Exampl
  20. 未来计算机的图片大全集,小学生未来的机器人儿童画图片大全

热门文章

  1. ImageMagick--介绍
  2. 2022-2028全球与中国金属加工液市场现状及未来发展趋势
  3. PAT (Advanced Level) Practice - 1127 ZigZagging on a Tree(30 分)
  4. 英语六级638分经验总结
  5. 火车z开头硬卧_Z的玩具火车
  6. 影响我国金融市场发展的因素
  7. 新版的友盟统计的坑(9.1.0版)
  8. 解决Module not specified问题
  9. 软考 信息系统项目管理师考试论文分析和应对技巧
  10. MD5退出历史舞台你知道吗?