C++PrimerPlus6-07-函数——C++的编程模块

第7章 函数——C++的编程模块

  • 函数的基本知识
  • 函数原型
  • 按值传递函数参数
  • 设计处理数组的函数
  • 使用const指针参数
  • 设计处理文本字符串的函数
  • 设计处理结构的函数
  • 设计处理string对象的函数
  • 调用自身的函数(递归)
  • 指向函数的指针

7.1 复习函数的基本知识

7.1.1 定义函数

C++函数返回值不能是数组(但可以将数组作为结构或对象的组成部分来返回)。

7.1.2 函数原型和函数调用

原型描述了函数到编译器的接口。
C++的编程风格是将main()放在最前面,因为它通常提供了程序的整体结构。
在编译阶段进行的原型化被称为静态类型检查(static type checking),它可捕获许多在运行阶段难以捕获的错误。

7.2 函数参数和按值传递

实参argument,形参parameter,按值传递会创建变量的副本。

7.2.1 多个参数

7.2.2 另一个接受两个参数的函数

7.3 函数和数组

int sum_arr(int arr[], int n); // arr = array name, n = size

7.3.1 函数如何使用指针来处理数组

在这里数组名视为指针,它和int* arr的含义相同。但这里数组声明使用数组名来标记存储位置,对数组名使用sizeof将得到整个数组的长度。

arr[i] == *(arr + i); // values in two notations
&arr[i] == arr + i;   // addresses in two notations

7.3.2 将数组作为参数意味着什么

并没有将数组内容传递给函数,而是将数组的位置(地址)、包含的元素种类(类型)、元素数目(n变量)提交给函数。

传递常规变量时,函数将使用该变量的拷贝;但传递数组时,函数将使用原来的数组。
其实也是按值传递,但传的是地址,而不是内容。

将数组类型和元素数量都告诉数组处理函数,请通过两个不同的参数来传递他们:

void fillArray(int arr[], int size); // prototype
void fillArray(int arr[size]); // No -- bad prototype, 不要这样

因为传的是个指针,指针本身并没有指出数组的长度。 sizeoff arr结果是4,而不数组长度。

7.3.3 更多数组函数示例

为防止函数无意中修改数组的内容,可在声明形参时使用关键字const:

void show_array(const double ar[], int n);

7.3.4 使用数组区间的函数

元素区间(range)也是一种给函数提供数组信息的方法:

  • 提供数组种类、数组的起始位置、数组中元素数量
  • 或者使用元素区间:提供两个指针,一个标识开头,一个标识尾部。

ar, ar + ArSize。ar + ArSize指向数组结尾元素后面的一个位置。end - begin = ArSize。

int sum_arr(const int* begin, const int* end);
int main()
{
  int cookies[8] = {1};
  sum_arr(cookies, cookies + 8);
}

7.3.5 指针和const

const用于指针的两种用途:

  1. 让指针指向常量对象,防止使用该指针来修改所指向的值。
  2. 将指针本身声明为常量,防止改变指针指向的位置。

几种情况

// 可以用const指针指向非const量,但*pt的值为const,不能通过指针来修改指向的量
int age = 39;
const int*  pt = &age;
*pt += 1; // 不能,INVALID because pt points to a const int
cin >> *pt; // 不能,INVALID for the same reson
*pt = 20; // 不能,INVALID because pt points to a const int
age = 20; // 可以,VALID because age is not declared to be const

// 可以用const指针指向const量
const float g_earth = 9.80;
const float* pe = &g_earth; // VALID

// 不能用常规指针指向const量。因为不应该用指针来修改const量的值
const float g_moon = 1.63;
float* pm = &g_moon; // INVALID

// 

结论是(函数形参)尽可能使用const

  • 这样可以避免由于无意间修改数据
  • 使用const使函数能够处理const和非const实参,否则将只能接受非const数据。
  • 指向指针的指针不能用const(雾?)

指向const的指针和本身是const的指针:

int sloth = 3;

// 指向const的指针
// 不能修改sloth的值,但能指向别的地方
const int* ps = &sloth; // a pointer to const int
ing sage = 80;
pt = &sage; // okay to point to another location

// const指针
// 能修改sloth的值,但不能指向别的地方
int* const finger = &sloth;
*finger = 99;

7.4 函数和二维数组

int data[3][4] = {{1,2,3,4}, {9,8,7,6}, {2,4,6,8}};
int total = sum(data, 3);

// 函数原型可以是以下两种
// ar2是指针而不是数组,它指向由4个int组成的数组
int sum(int(*ar2)[4], int size);
int sum(int ar2[][4], int size);

int sum(int ar2[][4], int size)
{
  int total = 0;
  for (int r = 0; r < size; r++) // 行数由变量来确定
  {
    for (int c = 0; c < 4; c++) // 列数是确定的
    {
      total += ar2[r][c];
    }
  }
  return total;
}

// 对指针ar2执行两次解除引用,才能得到数据
ar2[r][c] == *(*(ar2 + r) + c) // same thing

// 每层的含义
ar2                 // pointer to first row of an array of 4 int,4int数组组成数组的第一行
ar2 + r             // pointer to row r (an array of 4 int),4int数组组成数组的第r行
*(ar2 + r)          // row r (an array of 4 int, hence the name of an array,
                    // thus a pointer to the first int in the row, i.e., ar2[r])
                    // 4int数组组成数组的第r行的值,是一个4int数组(的首元素地址)
*(ar2 + r) + c      // pointer int number c in row r, i.e., ar2[r] + c,第r行的4int数组的第c个元素的地址
*(*(ar2 + r) + c)   // value of int number c in row r, i.e., ar2[r][c],第r行的4int数组的第c个元素的值

7.5 函数和C风格字符串

7.5.1 将C风格字符串作为参数的函数

将字符串作为参数传递给函数,表示字符串的3种方式(他们都是char指针,也就是char*):

  • char数组
  • 用引号括起的字符串常量(也称字符串字面值)
  • 被设置为字符串的地址的char指针
char ghost[15] = "galloping";
char* str = "galumphing";
int n1 = strlen(ghost);         // ghost is &ghost[0]
int n2 = strlen("gamboling");   // address of string
int n3 = strlen(str);           // pointer to char

C风格字符串与常规char数组的重要区别是,结束字符。所以不需要将字符串长度作为参数传递给函数,可以在函数内检查每个字符,找到空值字符。

while (*str) // 处理字符串中字符的标准方式
{
  ...
  str++;
}

7.5.2 返回C风格字符串的函数

从后向前填充字符串:

while (n-- > 0)
  pstr[n] = c;

7.6 函数和结构

可以当作普通变量来使用,结构名不是结构的地址,要获得地址使用&。
将结构作为参数传递,有三种方式:

  1. 直接作为参数。缺点是创建了原始结构的副本,增加了内存和速度开销。
  2. 传结构的地址,然后使用指针来访问结构的内容
  3. 按引用传递(第8章)

7.6.1 传递和返回结构

当结构比较小时,按值传递。

7.6.2 另一个处理结构的函数示例

数学库cmath,sqrt, atan2, atan(不能区分180之内和之外的角度)

7.6.3 传递结构的地址

void show_polar(const polar* pda);

7.7 函数和string对象

比起数组,string对象和结构更相似。

7.8 函数和array对象

void show(std::array<double, 4> da); // da an object
void fill(std::array<double, 4>* pa); // pa a pointer to an object

7.9 递归

C++不允许main()调用自己。

7.9.1 包含一个递归调用的递归

void recurs(argumentlist)
{
  statement1;
  if (test)
    recurs(arguments);
  statements;
}

7.9.2 包含多个递归调用的递归

递归方法有时被称为分而治之策略(divide-and-conquer strategy)

7.10 函数指针

与数据项相似,函数也有地址。

7.10.1 函数指针的基础知识

  • 获取函数的地址:函数名(后面不跟参数)
  • 声明函数指针:指定函数的返回类型 和 函数的特征标(参数列表)
    • double (*pf)(int);
  • 使用指针来调用函数:(*pf)(4) 或者 pf(4)

7.10.2 函数指针示例

7.10.3 深入探讨函数指针

略!(P201)

7.10.4 使用typedef进行简化

7.11 总结

  • 函数:定义+原型,调用
  • 按值传递,使用拷贝
  • 数组形参,指针
  • C风格字符串char*,string类字符串
  • 处理结构与基本类型相同
  • 递归,函数指针

7.12 复习题

7.13 编程练习

1 略
2 略
3 略
4 略
5 使用递归

6 数组参数

#include <iostream>

using namespace std;

int Fill_array(double ar[], int size);            // 填充数组
void Show_array(const double ar[], int size);    // 显示数组
void Reverse_array(double ar[], int size);        // 翻转数组

int main()
{
    double ar[8];
    int numCount = Fill_array(ar, 8);
    Show_array(ar, numCount);
    Reverse_array(ar, numCount);
    Show_array(ar, numCount);
    Reverse_array(ar + 1, numCount - 2);
    Show_array(ar, numCount);

    return 0;
}

int Fill_array(double ar[], int size)
{
    printf("Input %d number for make a array:\n", size);
    int count = 0;
    while (count < size)
    {
        if (!(cin >> ar[count])) break;
        count++;
    }

    cout << "Done! you input " << count << " number.\n";
    return count;
}

void Show_array(const double ar[], int size)
{
    cout << "Show_array: ";
    for (int i = 0; i < size; i++)
    {
        cout << ar[i] << " ";
    }
    cout << endl;
}

void Reverse_array(double ar[], int size)
{
    for (int i = 0; i < size / 2; i++)
    {
        double temp = ar[i];
        ar[i] = ar[size - i - 1];
        ar[size - i - 1] = temp;
    }
}

789略
10 函数指针

#include <iostream>
using namespace std;

double add(double x, double y);
double sub(double x, double y);
double multiply(double x, double y);
double calculate(double x, double y, double (*pf)(double, double));

int main()
{
    double (*pf[3])(double, double) = { add, sub, multiply };

    cout << "Please input a pair of numbers:";
    double x, y;
    int count = 0;
    while (cin >> x >> y)
    {
        cout << "res#" << count << ": " << calculate(x, y, pf[count % 3]) << endl;
        cout << "Please input a pair of numbers:";
        count++;
    }

    cout << "Done\n";
    return 0;
}

double add(double x, double y)
{
    return x + y;
}

double sub(double x, double y)
{
    return x - y;
}

double multiply(double x, double y)
{
    return x * y;
}

double calculate(double x, double y, double (*pf)(double, double))
{
    return pf(x, y);
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 cdd@ahucd.cn

×

喜欢就点赞,疼爱就打赏

B站 cdd的庇护之地 github itch