6. 函数上
2026/8/8 6:56:55 网站建设 项目流程

函数上

复习:使用函数需要提供函数定义,提供函数原型,调用函数。

函数定义

函数分为两类:有返回值的函数,无返回值的函数。

C++对于有返回值的函数有一定限制,不能是数组,但可以将数组作为结构体或是对象的组成部分返回。

返回类型若不匹配,编译器会报错,除非满足隐式类型转换规则。

函数原型和函数调用
  • 函数原型是一条语句,必须以分号结尾;

  • 函数原型不要求提供变量名,但需要提供类型;

  • C/C++都允许不写函数声明,但是必须是单文件且定义在调用之前。C99及以上和C++不允许使用隐式函数声明。

  • C 语言:老标准允许省略类型(默认 int),新标准已禁用,C++ 不兼容这个规则;

函数和数组

大多数情况下C++和C语言一样,也将数组名视为指针。

由于使用的是指针而不是原始数组的副本,所以被调函数是有能力修改原始数据的。如果想要保护数据,则可以用const保护数组;

使用数组区间的函数

传统C/C++的方法是,传入数组起始指针,再传入数组元素数量。

还有一种方法是指定元素区间,使用两个指针,一个指向头部,一个指向尾部。例如STL方法使用超尾概念来定义区间。

指针使用const

const在前*在后为常量指针,本质是指针,指向常量的指针,不可以修改指向的值,但可以修改指向

*在前const在后为指针常量,本质是常量,指针类型的常量,可以改变指向数据的值,不能改变指向。

C++禁止将const的地址赋给非const的指针。允许非const的指针赋给const指针。

不过两层间接关系混合const指针可以通过间接关系修改数据,不再安全。

const int a = 10; const int* p1 = &a; // 合法:const 数据地址 → const 一级指针 ​ // 错误:试图将 const int* 的地址,直接赋给 int**(二级指针无 const) int** p2 = &p1; // 编译器报错!错误信息类似:
const int a = 10; const int* p1 = &a; ​ // 显式强转:强制将 const int* 的地址,转为 int** 赋给 p2 int** p2 = (int**)&p1; // 编译器放行(强转压制了错误) ​ // 试图通过二级指针修改 const 数据 a **p2 = 30; // 未定义行为!可能出现的结果: // 1. 输出 10(编译器优化,const 数据被放在只读内存,修改无效); // 2. 输出 30(修改了内存,但破坏了 const 语义,后续代码可能出错); // 3. 程序崩溃(写入只读内存,触发内存保护错误)。
函数和二维数组

在C/C++中多维数组做参数的时候必须明确二维及以上的大小,第一位的大小可省略。例如:

void func(int arr[][4], int row) ;
void func(int (*arr)[4], int row) ;//注意[]优先级高于*

如果在形参中写上了行数,调用函数时没有强制的行数限制。编译器会无视它。

缺陷:

  • 列数编译期固定,函数无法通用处理不同列宽的二维数组;

  • 不方便使用 const 保护数组内容;

拓展:现代 C++ 解决方案:std::vector < std::vector< int > >、std::array,消除该限制。

函数和C风格字符串

由于C类型的字符串有既定的结束字符,所以设置形参的时候可以不设置代表字符串长度的形参。

三种C字符串:char数组、char指针、字符串字面量。传参时全部退化为char*

函数和结构体

结构体可直接作为函数的形参或实参,用法与普通变量一致 。

函数调用时会创建结构体的完整副本。若结构体体积较大,会导致程序效率下降,可以考虑使用结构体指针。

函数和string对象
  1. 形参使用const string& s

    • 传递的是原对象的引用,不允许修改。

  2. 形参使用string& s

    • 传递的是原数据的引用,并且可以修改。

  3. const string s/string s

    • 使用的原数据的副本。

函数与arrray对象(C++11)(定长数组)

优先使用「const 引用」尽量避免「按值传递」

  1. 按 const 引用传递(const array<T, N>& arr)(T是类型,N是数组长度)

    • 传递 array 对象的只读引用,无拷贝(仅传地址),禁止修改数组内部数据

  2. 按非 const 引用传递(array<T, N>& arr)

  3. 按值传递(array<T, N> arr)

组成部分作用
array容器模板名
<T, N>T为数值类型,N为固定大小
arr变量名

array并非只能存储基本类型,还可以存储对象。

vector对象(动态数组)
void display(const vector<string>& vec);
递归

和C\C++语法上允许main()函数调用自己,但极不推荐。

递归存在函数调用开销、栈空间开销;深度极大时优先改用循环迭代。

#include <iostream> const int Len = 66; const int Divs = 6; void subdivide(char ar[], int low, int high, int level); ​ int main() { char ruler[Len]; int i; for (i = 1; i < Len - 2; i++) ruler[i] = ' ';//把1~64标记为' ' ruler[Len - 1] = '\0'; int max = Len - 2; int min = 0; ruler[min] = ruler[max] = '|';//把边缘标记为'|',0和64 std::cout << ruler << std::endl;//打印数组,屏幕第一行 for (i = 1; i <= Divs; i++)// { subdivide(ruler, min, max, i); std::cout << ruler << std::endl; for (int j = 1; j < Len - 2; j++)//清空数组 ruler[j] = ' '; } ​ return 0; } ​ //当递归至level为0时,停止递归。会把中间,中间的中间……,标记为'|' void subdivide(char ar[], int low, int high, int level) { if (level == 0) return; int mid = (high + low) / 2; ar[mid] = '|'; subdivide(ar, low, mid, level - 1); subdivide(ar, mid, high, level - 1); }
函数指针

和数据类似,函数也有地址。

函数指针的语法是返回值类型 (*指针变量名)(参数类型列表),括号不能省略,否则为指针函数。

函数名(pt())和函数指针((*pt)())等价,和数组名与数组首地址类似。

//写法1:完整写法 void estimate(int lines, double (*pf)(int)); //写法2:数组形式(仅形参位置可用) void estimate(int lines, double pf(int));
函数指针数组

函数指针数组是「存储多个函数指针的数组」

语法:返回值类型 (*数组名[数组大小])(参数类型列表)

// pa是数组,数组内每个元素是:const double* (*)(const double*, int) 类型函数指针 const double *(*pa[3])(const double *, int) = {f1,f2,f3};

注:auto 不能直接定义数组类型。但可以简化复杂指针声明,极大减少手写复杂类型的出错概率

使用typedef

typedef可以用来创建类型别名,只能用来操作类型,不能操作实体/标识符。

int (*func_arr[4])(int, int);//等价于int (*(func_arr[4]))(int, int) // 简化写法1:typedef别名(C/C++通用) typedef int (*CalcFunc)(int, int);
//补充: 简化写法2:using别名(C++11+,更直观) using CalcFunc = int (*)(int, int);

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询