1.函数
1.1 函数定义
函数:定义好的、可重用的功能模块
定义函数:将一个模块的算法用C++描述出来
函数名:功能模块的名字
函数的参数:计算所需要的数据和条件
函数的返回值:需要返回的计算结果

函数定义的语法形式
类型标识符 函数名 形式参数表
{
语句序列
}
1.2 函数调用
调用函数需要先声明函数原型
若函数定义在调用点之前,可以不另外声明;
若函数定义在调用点之后,必须要在调用函数前声明函数原型;
函数原型:类型标识符 被调用函数名(含类型说明的形参表)

函数调用形式:函数名(实参列表)

嵌套调用:在一个函数的函数体中,调用另一函数。
图片说明

/*求x的n次方函数*/
double power(double x, int n) {
double val = 1.0;
while (n--) val *= x;
return val;
}

例:数制转换,将一个八位二进制数转换为十进制数

#include <iostream>
using namespace std;
double power (double x, int n); //计算x的n次方
int main() {
    int  value = 0;
    cout << "Enter an 8 bit binary number  ";
    for (int i = 7; i >= 0; i--) {
      char ch;
      cin >> ch;
      if (ch == '1')//一位一位输入,输入一次计算一次
        value += static_cast<int>(power(2, i));//强制转换为int类型
    }
    cout << "Decimal value is  " << value << endl;
    return 0;
}

例:编写arctan函数,求π值。

double arctan(double x) {
//arctanx=x-x^3/3+x^5/5-...
          double sqr = x * x;
          double e = x;
          double r = 0;
          int i = 1;
          while (e / i > 1e-15) {
                   double f = e / i;
                   r = (i % 4 == 1) ? r + f : r - f;
                   e = e * sqr;
                   i += 2;
          }
          return r;
}
int main() {
          double a = 16.0 * arctan(1/5.0);
          double b = 4.0 * arctan(1/239.0);
          **//注意:因为整数相除结果取整,如果参数写1/5,1/239,结果就都是0**
          cout << "PI = " << a - b << endl;
          return 0;
}

例:判断是否是回文

bool isSym(int n)
{
    if (n < 0) return 0;
    int i = n;
    int m = 0;
    while (i != 0)
    {
        m = m * 10 + i % 10;//记录颠倒过来的数
        i = i / 10;//去掉最低位
    }
    return m == n;//若相等,输出1,不等输出0
}

例:每个骰子有六面,点数分别为1、2、3、4、5、6。游戏者在程序开始时输入一个无符号整数,作为产生随机数的种子。每轮投两次骰子,第一轮如果和数为7或11则为胜,游戏结束;和数为2、3或12则为负,游戏结束;和数为其它值则将此值作为自己的点数,继续第二轮、第三轮...直到某轮的和数等于点数则取胜,若在此前出现和数为7则为负。

int shai()//掷骰子
{
    int a = rand() % 6 + 1;
    int b = rand() % 6 + 1;
    int sum = a + b;
    cout << "player rooled " << a << "+" << b << "=" << sum << "\n";
    return sum;

}
int main
{
    enum Gamb{WIN,LOSE,PLAYING};
    int sum, cur;
    Gamb status;
    srand(time(NULL));
    sum = shai();
    switch (sum)
    {
    case 7:
    case 11:
        status = WIN;//第一次点数为7或11,获胜
    case 2:
    case 3:
    case 12:
        status = LOSE;//第一次点数为2,3或12,失败
    default:
        status = PLAYING;//其他继续摇
        cur = sum;//记录第一次的值
        break;
    }
    while (status == PLAYING)
    {
        sum = shai();
        if (sum == cur)
        {
            status = WIN;
            break;
        }//和第一次相等时,跳出,获胜            
        else if (sum == 7)
            status = LOSE;
    }
    if (status == WIN)
        cout << "YOU WIN!";
    else
        cout << "YOU LOSE!";
}

rand函数
函数原型:int rand(void);
所需头文件:<cstdlib>
功能和返回值:求出并返回一个伪随机数</cstdlib>

srand函数
void srand(unsigned int seed);
参数:seed产生随机数的种子
所需头文件:<cstdlib>
功能:为使rand()产生一序列伪随机整数而设置起始点。使用1作为seed参数,可以重新初化rand()</cstdlib>

1.3 嵌套与递归
函数的嵌套调用
注:函数的嵌套调用是利用栈来实现的

函数的递归调用
函数直接或间接地调用自身,称为递归调用。
例如求n的阶乘,两个要点,一个是递归公式,一个是终止条件
例:用递归法计算从n个人中选选k个人组成一个委员会的不同组合数。
分析:
由n个人里选k个人的组合数= 由n-1个人里选k个人的组合数+由n-1个人里选k-1个人的组合数;当n = k或k = 0时,组合数为1。

int comm(int n, int k)//由n个人里选k个人的组合数= 由n-1个人里选k个人的组合数+由n-1个人里选k-1个人的组合数
{
    if (k < 0||n < k) return 0;//去除不合理情况
    if (n == k || k == 0)
        return 1;
    else
        return comm(n - 1, k) + comm(n - 1, k - 1);
}

例:汉诺塔

void move(char A, char C)
{
    cout << A << "->" << C<<"\n";
}
void hanoi(int m, char A,char B, char C)
{
    if (m == 1)
        move(A, C);
    else
    {
        hanoi(m-1,A, C, B);//借助C把A上m-1片移到B
        move(A, C);//把剩下一片由A移到C
        hanoi(m - 1, B, A, C);//借助A把B上m-1片移到C
    }
}

1.4 函数的参数传递
注意:在函数调用时才分配形参的存储单元;实参可以是常量、变量或者表达式,且类型一定要与形参相同;值传递是单向传递,形参无法改变实参,引用传递可以实现双向传递,常引用做参数可以保障实参数据的安全。

引用类型
引用(&)是标识符的别名,定义一个引用时,必须对它初始化,使他指向一个已存在的对象。

 int i,j;
   &ri=i;//定义int引用ri,并初始化为i的引用
j=10;
ri=j;//相当于i等于j

注:一旦一个引用被初始化后,就不能改为指向其他对象。引用可以作为形参。不可以定义指向引用的指针,因为引用不是一个对象

void swap(int &a, int &b)
{
    int t = a;
    a = b;
    b = t;
}

1.5 含有可变参数的函数
如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型;如果实参的类型不同,可以编写可变参数的模板。

initializer_list:是一种标准库类型,用于表示某种特定类型的值的数组,该类型定义在同名的头文件中。
使用方法
图片说明
应用举例
图片说明

1.6 内联函数
内联函数
声明时使用关键字 inline。
编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销。
注意:
内联函数体内不能有循环语句和switch语句;
内联函数的定义必须出现在内联函数第一次被调用之前;
对内联函数不能进行异常接口声明。

#include <iostream>
using namespace std;
const double PI = 3.14159265358979;
inline double calArea(double radius) {
          return PI * radius * radius;
}

int main() {
          double r = 3.0;
          double area = calArea(r);//若编译器认定此函数为内联,则直接代入函数体
          cout << area << endl;
          return 0;
}

10.7 constexpr函数
constexpr函数语法规定
constexpr修饰的函数在其所有参数都是constexpr时,一定返回constexpr;
函数体中必须有且仅有一条return语句。

constexpr函数举例
constexpr int get_size() { return 20; }
constexpr int foo = get_size(); //正确:foo是一个常量表达式

10.8 带默认参数值的函数
可以预先设置默认的参数值,调用时如给出实参,则采用实参值,否则采用预先设置的默认参数值。
例:

int add(int x = 5,int y = 6) {
     return x + y;
}
int main() {
     add(10,20);  //10+20
     add(10);     //10+6
     add();       //5+6
}

默认参数值的说明次序
有默认参数的形参必须列在形参列表的最右,即默认参数值的右面不能有无默认值的参数;
调用时实参与形参的结合次序是从左向右
例:
int add(int x, int y = 5, int z = 6);//正确
int add(int x = 1, int y = 5, int z);//错误
int add(int x = 1, int y, int z = 6);//错误

默认参数值与函数的调用位置
如果一个函数有原型声明,且原型声明在定义之前,则默认参数值应在函数原型声明中给出;如果只有函数的定义,或函数定义在前,则默认参数值可以函数定义中给出。
例:
图片说明
例:

#include <iostream>
#include <iomanip>
using namespace std;
int getVolume(int length, int width = 2, int height = 3);//声明在定义前,要给出默认参数值
int main() {
const int X = 10, Y = 12, Z = 15;
cout << "Some box data is " ;
cout << getVolume(X, Y, Z) << endl;
cout << "Some box data is " ;
cout << getVolume(X, Y) << endl;
cout << "Some box data is " ;
cout << getVolume(X) << endl;
return 0;
}
int getVolume(int length, int width, int height) {
cout << setw(5) << length << setw(5) << width << setw(5)
<< height << '\t';
return length * width * height;
}

10.8 函数重载
C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。
例:
图片说明
图片说明
注意:重载函数的形参必须不同:个数不同或类型不同。
编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数。

10.9 C++系统函数
https://www.cnblogs.com/MarkKobs-blog/p/10456759.html