0
点赞
收藏
分享

微信扫一扫

《C++新经典》第7章 函数

数数扁桃 2022-04-29 阅读 66
c++

《C++新经典》第7章 函数


可复用代码,实现相对独立和短小的功能。

7.1 函数的基本概念和定义

7.1.1 函数的基本概念

常用功能模块编写成函数,减少重复代码工作量。
C程序,由一个主函数(main函数,函数入口)和若个其他函数构成,主函数调用其他函数,其他函数互相调用。

#include <stio.h>

void printhello() {
	print("hello\n");
}

int main() {
	printhello();
	printhello();
	return 0;
}
  • 源代码文件包含一到多个函数;
  • 诸多函数分别放到多个源代码文件中;
  • C程序从main函数开始,main函数结束。
  • 函数不能嵌套定义;
  • 函数分为库函数和自定义函数。

7.1.2 函数的定义和返回值

调用函数时,接收传递进来数据的变量,叫做函数参数。

  • 函数定义:
返回类型 函数名(形式参数列表) { //{}内为函数体
	一或多条语句;
	return 返回值;
}
//调用函数时,函数形参会分配内存,调用结束时,形参内存会被释放,形参只在函数内部使用
//函数调用时,传递给函数的参数是实参,可以是变量、常量、表达式
//实参,形参,单向值传递
  • 无返回类型无形参
void printhello() {
	print("hello\n");
	return;//可有可无
}
  • 有返回类型有形参
int add(int a, int b) {
	int sum = a+b;
	return sum;
}

7.2 函数调用方式和嵌套调用

7.2.1 函数调用的一般形式

函数名(实参列表);

7.2.2 函数调用的方式

  • 函数调用作为语句
printhello();
  • 函数表达式
int result = add(1, 2);
  • 函数调用作为另一个函数调用的参数
resultt = add(add(1, 2), 3);

调用函数前先声明该函数

  • 函数声明的一般形式:
返回类型 函数名(形参列表);
  • 函数声明:
void printhello();
int add(int, int);
  • 函数定义:
void printhello() {
	printf("hello\n");
}
int add(int a, int b) {
	return a+b;
}
  • 函数调用:
printhello();
int result = add(1, 2);

7.2.3 函数的嵌套调用

c语言函数平行独立,不允许内部嵌套定义,但可以嵌套调用。

  • 错误定义
void outFunc() {
	printf("outFunc\n");
	void inFunc() {
		printf("inFunc\n");
	}
}
  • 正确定义
void outFunc() {
	printf("outFunc\n");
}
void inFunc() {
	printf("inFunc\n");
}

7.3 函数递归调用

7.3.1 函数递归调用的定义

函数递归调用,是函数自己调用自己的嵌套调用,必须有递归结束条件(递归调用的出口)。
函数调用时,分配固定有限内存保存信息(局部变量,函数参数,函数调用关系等),超过内存大小,程序执行崩溃或异常退出。

7.3.2 函数递归调用的出口

无出口,则递归调用死循环,程序资源耗尽,运行崩溃或异常退出。

int func(int n) {
	if(n <= 1)
		return 1;
	else 
		return func(n-1) * n;
}

7.3.3 递归优缺点及必要性

优点:代码少,简洁,精妙。
缺点:

  • 理解有难度。
  • 若调用层次太深,调用栈(保存函数调用关系等用到的内存)可能会溢出,程序运行不正常。
  • 效率和性能不高,深层次调用,调用中间保存内容多。

必要性:不常用,但有些地方不得不用。
递归存在直接调用(只有一个函数)和间接调用(多函数间相互循环调用)。

7.3.3 递归的实际运用

计算本方人员能移动到的所有位置,上下左右格子方向,遇到敌方不能移动,有移动力限制。

//两个出口条件,是否存在可移动格子,是否有剩余移动力

int posx=6, posy=8; //当前位置
int value = 40;//剩余移动力

void allPos(int posx, int posy, int value) {
	if(上边存在格子) { //有格子且未到达过
		if(上边能走) {	//没敌人,有足够移动力
			记录能走位置;
			扣除适当移动力;
			记录当前位置;	//避免重复
			allPos(上边位置,扣除后的移动力); //递归调用
		}
	}
	if(下边存在格子) { //有格子且未到达过
		if(下边能走) {	//没敌人,有足够移动力
			记录能走位置;
			扣除适当移动力;
			记录当前位置;	//避免重复
			allPos(下边位置,扣除后的移动力); //递归调用
		}
	}
	if(左边存在格子) { //有格子且未到达过
		if(左边能走) {	//没敌人,有足够移动力
			记录能走位置;
			扣除适当移动力;
			记录当前位置;	//避免重复
			allPos(左边位置,扣除后的移动力); //递归调用
		}
	}
	if(右边存在格子) { //有格子且未到达过
		if(右边能走) {	//没敌人,有足够移动力
			记录能走位置;
			扣除适当移动力;
			记录当前位置;	//避免重复
			allPos(右边位置,扣除后的移动力); //递归调用
		}
	}
}

7.4 数组作为函数参数

7.4.1 数组元素作为函数实参

数组元素当作普通变量使用。

int max(int x, int y) {
	if(x > y)
		return x;
	return y;
}

int main() {
	int a[10];
	a[1] = 5;
	a[5] = 8;
	max(a[1], a[5]);
	return 0;
}

7.4.2 数组名作为函数实参

数组名(数组指针),代表数组首地址,函数传递数组名时传递的是地址,“地址传递”,能够修改元素值。

void changeValue(int b[8]) { //int b[],int *b,int b[22],大小无意义
	b[2] = 15;
}

int main() {
	int a[8];
	a[2] = 12;
	changeValue(a);
	return 0;
}

7.4.3 多维数组作为函数实参

可以省略第一维大小,不能省略第二维大小。(二维数组)

void changeValue(int b[5][8]) { //int b[][8]
	b[0][2] = 15;
}

int main() {
	int a[5][8];
	a[0][2] = 12;
	changeValue(a);
	return 0;
}

7.5 局部变量和全局变量

7.5.1 局部变量

函数内部定义的变量,只在本函数内使用。

void func(int value) {
	int x;
	x = value;
}

int main() {
	int x;
	int value =12;
	func(value);

	{ //程序块,复合语句;内部变量等价于局部变量
		int x = value;
	}
	return 0;
}

//不同函数内变量名可以相同,互不影响
//形参也是局部变量

7.5.2 全局变量

函数外定义的变量称为全局变量(外部变量)。
优点:增加函数间数据联系的渠道。增加函数间直接传递数据的通道,各个函数都能修改全局变量的值。
缺点:

  • 必要时才使用。全局变量在程序整个执行期间一直占据内存。
  • 降低函数通用性,依赖全局变量的函数迁移时,相关全局变量也要迁移,若遇到同名全局变量,比较麻烦。
  • 到处全局变量,降低程序清晰性和可读性。多个函数都能改变全局变量值,难以判断瞬间全局变量的值。
int c;
void changeValue() {
	c = 12;
}
int main() {
	c = 1;
	printf("c=%d\n", c);
	changeValue();
	printf("c=%d\n", c);
	return 0;
}
  • extern外部变量说明时,可以引用后面或其他文件定义的全局变量。全局变量定义时可初始化,全局变量说明(extern)时不可赋初值。
extern int c; //全部变量说明
void changeValue() {
	c = 12;
}
int main() {
	printf("c=%d\n", c);
	changeValue();
	printf("c=%d\n", c);
	return 0;
}
int c = 15; //全局变量定义时初始化
  • 全局变量(外部变量)定义与说明。定义一次,所有函数之外,定义时分配内存,可初始化。说明可多次(不分配内存),函数内部也可说明(一般极少),已在外部定义过该变量,仅仅是引用该变量的“声明”。
void changeValue1() {
	extern int c; //全部变量说明
	c = 12;
}
void changeValue2() {
	extern int c; //全部变量说明
	c = 10;
}
int c = 15; //全局变量定义时初始化
int main() {
	printf("c=%d\n", c);
	changeValue1();
	printf("c=%d\n", c);
	changeValue2();
	printf("c=%d\n", c);
	return 0;
}
  • 同一源文件中,局部变量会覆盖同名全局变量的作用范围。
extern int c; //全部变量说明
void changeValue() {
	int c = 12;
}
int main() {
	printf("c=%d\n", c);
	changeValue();
	printf("c=%d\n", c);
	return 0;
}
int c = 15; //全局变量定义时初始化
  • 一个源文件定义的全局变量,需要在其他源文件中使用,extern做外部变量说明即可。

func.c

int c = 11;

func2.c

extern int c;
void changeValue() {
	c = 22;
}

7.6 变量的存储引用和内外部变量

7.6.1 变量的存储类别

变量作用域划分局部变量和全局变量,变量存在的时间(生存期)划分静态存储变量和动态存储变量。

  • 静态存储方式,程序运行(编译?)期间分配固定存储空间的方式。
  • 动态存储方式,程序运行期间根据需要进行动态分配存储空间的方式。

存储空间主要分为三个部分:程序代码区、静态存储区、动态存储区。
全局变量和静态局部变量放在静态存储区,占据固定存储单元(静态),不是动态分配和释放。程序开始时分配存储区,完毕后释放。

动态存储区存储数据:

  • 函数形参,视作局部变量。
  • 局部变量,函数内定义变量(不包括静态局部变量)。
  • 函数调用时调用现场的一些数据和返回地址等。

这些数据在函数调用开始时分配空间,函数调用完毕后释放空间(动态)。

7.6.2 局部变量存储方式

  1. 一般局部变量,动态分配。
  2. 静态局部变量,编译时只赋初值一次(默认0),程序运行期间不释放,长期占用内存,降低程序可读性。

7.6.3 全局变量跨文件引用

extern可用于函数内或函数外声明全局变量。
static可用于定义静态全局变量,只用于本文件中。

7.6.4 函数跨文件调用

  1. 内部函数(静态函数)
    只能被本文件中其他函数调用。
static 返回类型 函数名(形惨列表) {}
  1. 外部函数
    能被本文件中和其他文件中函数调用。
返回类型 函数名(形惨列表) {}

7.6.5 static使用

  1. 静态局部变量,编译时默认初始化为0(可定义时初始化)。
void func() {
	static int value = 1;
}
  1. 静态全局变量,只能本文件中使用。
static int value = 1;
  1. 静态函数(内部函数),只能本文件中使用
static void func() {}
举报

相关推荐

0 条评论