C++系列文章目录
待学习\整理内容
英文术语整理
一、函数概述
C++不允许函数定义嵌套,即函数定义中再定义一个函数是不被允许的
1.函数原型
标准库函数的函数原型都在头文件中提供,程序可以用#include指令包含这些原文件。
用户自定义函数,程序员必须在源代码中说明函数原型。
函数原型(函数声明)是一条程序语句,由函数返回类型、函数名和参数构成,形式为:
返回类型 function(参数名)
函数声明和函数定义在返回类型、函数名和参数表上必须完全一致。如果不一致就会发生编译错误。
函数原型不必包含参数的名字,只要包含参数的类型。
int Area(int,int);
int Area(int length,int width);//二者等价
若函数要返回一个值,必须在函数名前规定返回值类型;如果函数没有返回值,则类型为void
2.全局变量、局部变量、静态局部变量
(1)程序的内存区域
区域名 | 程序内存空间 |
---|---|
代码区(code area) | 存放程序的代码,程序中各个函数的代码块 |
全局数据区(data area) | 存放程序的全局数据和静态数据 |
堆区 (heap area) | 存放程序的动态数据 |
栈区(stack area) | 存放程序的局部数据,即各个函数中的数据 |
(2)全局变量
定义:函数外边访问的变量,在程序的每个函数中是可见的。
存放位置:内存的全局数据区。
初始化:编译器可以默认初始化,默认为0。
全局变量定义之前的所有函数定义都不会知道该全局变量。
(3)局部变量
定义:在函数内部定义的变量仅在该函数内是可见的。类型修饰是auto
,表示该变量在栈里分配空间,但习惯上会省略auto。
存放位置:函数中的局部变量存放在栈区。
初始化:无默认初始化。如果不初始化,结果会不可预见。
(4)静态局部变量——static关键字
定义:函数结束时,静态局部变量不会消失,每次调用函数时,亦不会为其重新分配空间。直到程序运行结束都始终存放在全局数据区。
存放位置:全局数据区(与全局变量共享全局数据区)
初始化:编译器可以默认初始化,默认为0。
用处:①使用它确定某函数是否被调用过;②使用它保留多次调用的值。
- 与全局变量、局部变量的异同:
- ①与全局变量共享全局数据区
- ②函数中局部变量存放在栈区,静态局部变量存放在全局数据区;存放位置不同,存在时限不同,对二者操作的运行结果也不同。
- ②静态局部变量只在定义它的函数中可见
3.函数调用机制
(1)步骤
- ①建立被调函数的栈(stack)空间
- ②保护调用函数的运行状态和返回地址
- ③传递参数
- ④将控制转交给被调函数
(2)参数传值特性
函数之间的栈区是相互独立的,通过参数传递可以让嵌套函数中内层函数的形参赋有外层函数的实参值,修改内层函数的形参值不会影响外层函数的实参。
(3)栈空间释放
- ①栈资源有限,如果需要占用相当大的栈空间,可以在连接前通过设置栈空间大小来改善。
- ②函数返回时,将把返回值保存在临时变量空间中(如果有返回值的话)。
- ③恢复调用函数的运行状态,释放栈空间,使其属于调用函数栈空间的一部分,最后根据返回地址,回到调用函数代码执行处。
二、常用函数
1.递归函数(recursive function)
(1)定义
递归函数即自调用函数,在函数体内部直接或间接地自己调用自己,即函数的嵌套调用是函数本身。
(2)调用机制说明
- ①任何函数之间不能嵌套定义,调用函数和被调用函数之间相互独立,但彼此可以调用。
- ②被调函数运行时,调用点、调用时状态、返回点不同,可以看作是函数的一个副本,与调用函数的代码无关,所以代码是独立的。
- ③被调函数运行的栈空间独立于调用函数的栈空间,所以与调用函数之间的数据也是无关的。
- ④函数之间靠参数传递和返回值来联系,函数看作黑盒。
(3)函数调用的形式——直接调用和间接调用
①直接调用
定义:在函数中出现调用函数本身。
如求斐波拉契数列第n项:
long fib(int x)
{
if (x>2)
return (fib(x-1)+fib(x-2));//直接调用
else
return 1;
}
②间接调用
定义:函数中调用了其他函数,而该函数调用了本函数
间接递归要注意后提前调用的函数要提前声明,如以下的fn2()要在fn1()前预先声明。
如:
int fn1(int a)
{
int b;
b = fn2(a+1);//间接递归
//...
}
int fn2(int s)
{
int c;
c = fn1(s-1);//间接递归
//...
}
(4)递归的条件
- ①须有完成函数任务的语句(可以没有return)
- ②一个确定是否能避免递归函数调用的测试
- ③一个递归调用语句
- ④先测试,再递归调用;满足条件才递归。无限制递归会使栈空间溢出。
include<iostream>
using namespace std;
void count(int val)//①可以没有返回值
{
if(val > 1) //②
count(val - 1); //③
cout << "ok:" << val << endl; //①完成的任务
}
(5)消去递归
理论上可以证明,递归函数都能用非递归函数来代替。
待补充ing
(6)递归评价
1.增加系统开销,时间复杂度、空间复杂度高;
2.内联函数
(1)定义
内联函数也称内嵌函数,主要解决程序的运行效率,方便多次调用的可读性和调用开销。
(2)inline关键字
将函数声明为inline,即
inline int isnumber(char);//函数声明
inline int isnumber(char c)//签名声明了inline,这里的inline关键字可以省略
{
return (ch >= '0' && ch <= '9')?1:0;
}
当编译器看到inline后,为该函数创建一段代码,以便在以后每次遇到该函数的调用都用相应的一段代码来替换。
但内联函数必须先声明后调用。即声明必须有inline关键字,否则编译程序不认为其为内联函数。
(3)内联函数限制(重要)
- ①一定不能含有复杂的结构控制语句,如switch和while。如果含有则会视为普通函数。
- ②递归函数不能用作内联函数。
- ③只适合于只有1—5行的小函数
(4)对比C,内联函数与宏定义
克服了用#define宏定义带来的弊端。
3.重载函数
(1)定义
对于在不同类型上作不同运算而又同名的情况称为重载;
(2)匹配重载函数的顺序
靠将实参类型和所有被调用的重载函数的形参类型一一比较来判定的。
按以下三个步骤的先后顺序找到并调用那个函数:
- ①寻找一个严格的匹配
- ②通过内部转换寻求一个匹配
- ③通过用户定义的转换寻求一个匹配,若能找到唯一的一组转换
若能找到,则使用那个匹配。
(3)使用说明(重要)
- C++的函数如果在返回类型、参数类型、参数个数、参数顺序上被认为是不同的,则函数是不同的。但对于重载函数,如果只有返回类型不同,则是不够的,至少是要参数类型、参数个数、参数顺序上有所不同。
- typedef定义的类型只能使之相同与一个已存在的类型,而不能建立新的类型,所以不饿能用typedef定义的类型名来区分重载函数声明中的参数。即typedef INT int;void func(int x);void func(INT x);是同一个函数而不是重载函数。
- 重载函数应具有同样的功能。
(4)重载函数的内部实现(了解)
4.默认参数的函数
(1)默认参数的声明
C++允许给函数定义默认参数值。默认参数在函数声明中提供,当又有声明又有定义时,定义中不允许默认参数。如果函数只有定义,则默认参数才可以出现在函数定义中。
void point(int = 3,int = 4);//声明中给出默认值
void point(int x,int y);//error定义中不允许再给出默认值
{
cout << x << y << endl;
}
(2)默认参数的顺序规定
如果一个函数中有多个默认参数,则形参分布中,默认参数应该从右到左逐渐定义。当调用函数时,只能向左匹配参数。
(3)默认参数与函数重载
默认参数可以将一系列简单的重载函数合成为一个。但也可能有二义性。
(4)默认值的限定
可以是全局变量、全局常量,甚至是一个函数。
默认值不可以是局部变量,因为默认参数的函数调用是在编译时确定的,而局部变量的位置和值在编译时均无法确定。