C++学习随笔5 基于过程编程(1)- 函数
基于函数的程序设计就称为基于过程的程序设计
1.函数机制
- 栈机制:
一个程序要运行,就要先将可执行程序文件装载到计算机的内存中。装载是操作系统掌控的,一般而言,操作系统将程序装入到内存后,将形成一个随时可以运行的进程空间。
一个运行的程序在内存中表示为四个空间区域:
代码区:存放程序的执行代码。所谓执行代码就是索引了的一个一个函数块代码,它由函数定义块的编译得到。
全局数据区:存放全局数据、常量、文字量、静态全局量和静态局部量。
堆区:存放动态内存,供程序随机申请使用。
栈区:存放函数数据区(即局部数据区),它动态地反应了程序运行中的函数状态,其运动轨迹正好用来观察函数的调用与返回。
调用一个函数,可以看作是一个栈中元素的压栈和退栈操作。然而,压栈之后的函数运行,可能导致其他函数被调用。因此,程序运行表现为栈中一系列元素的压栈和退栈。
最初,操作系统将main函数压入栈中,标志着程序运行的开始,等到最后main函数退栈,程序也就运行结束了。
- 函数指针:
数据指针(Pointer to Data):指向数据区域的指针。
函数指针(Function Pointer):指向代码区域的指针(也称为指向函数的指针(Pointer to Function))。
指针函数(Pointer Function):返回指针类型的函数。 例如:int* f(int a);
运行中的程序,其中的每个函数都存放在代码区,占据着一个区域。故每个函数都有起始地址,函数指针与数据指针不能相互转换,通过函数指针可以调用所指向的函数。
函数指针定义:
函数有多少种类型,函数指针就有多少种类型。函数的类型表示是函数声明去掉函数名。 例如: void f(); 类型就是 void ();
函数指针定义:
int (*gp) (int); //定义一个名为gp的函数指针,容纳函数指针名的括号不能省
函数指针初始化: int g(int); int (*gp)(int) = g; //其中g应该与指针gp所指向的函数类型相同 函数指针赋值: int g(int); int (*gp)(int); gp = g;
函数指针声明: typedef int (*Fun)(int a, int b); //是函数指针的声明不是定义 因此: int m(int, int); Fun func = m; //OK Fun = m; //错:不恰当地使用typedef 'Fun'
函数引用: void g(); typedef void Fun(); Fun &f=g; f(); //等价于g()
函数指针既可以作为参数传递,也可以作为数组的元素类型。
- 命令行与main参数
命令行重定向:
如果不专门指定设备,就是标准输入/输出,否则,“<”后面规定输入设备,“>”后面规定输出设备。
例如:
#include <iostream> using namespace std; int main(){ for(int a,b; cin>>a>>b; cout<<a+b<<"\n"); }
通过命令"ConsoleApplication1<abc.txt>>xyz.txt",便可以从abc.txt文件中读入数据,而将输出追加到文件xyz.txt中(>则是不追加,每次清空)。
命令"ConsoleApplication1"则是默认使用标准输入/输出。
main参数:
int main(int argc, char**argv){......}
第一项是表示传递的C-串有几个,第二项是表示具体的C-串数组
#include <iostream> using namespace std; int main(int argc, char **argv) { for(int i=0; i<argc; ++i) cout<<argv[i]<<endl; }
- 函数重载
只要参数个数不同,参数类型不同,参数顺序不同,函数就可以重载。然而,返回类型不同则不允许函数重载。
默认参数:
C++可以给函数声明中的参数使用默认参数。这样在函数调用时,对应的实参就可以省略。函数参数的默认值只能从后往前设置。
void func(int a=1, int b, int c=3, int d=4); //错,a和b的位置违规 void func(int a, int b=2, int c=3, int d=4); //ok 调用时,默认实参也只能从后往前逐个替换: func(10, 15, 20, 30); //ok func(); //错:a没有默认值,必须给出实参 func(12, 12); //ok: c和d默认 func(2, 15, , 20); //错:d不默认则c也无资格默认