注:部分来源于网络相关资料,用于个人总结学习记录,如有侵权,请联删。
1,函数声明
函数原型的作用是提供函数调用所必须的接口信息,使得编译器能够检查函数调用中可能存在的问题。一段执行程序函数调用在函数定义的前面时需要函数声明。
第一种形式:
返回类型 函数名(类型1 参数一, 类型二 参数二,,,,);
第二种形式:
返回类型 函数名(类型1,类型2,,,,);
2,内联函数
c++提供一种提高函数效率的方法,即在编译时将被调函数的代码直接嵌入到主调函数中,取消调用这个环节。这种嵌入到主调函数中的函数称之为内联函数(Inline function)。
内联函数的声明是在函数定义的类型前加上inline修饰符,定义形式为:
Inline 返回类型 函数名(形式参数列表)
{
函数体
}
内联函数中不允许用循环语句和switch语句
内联函数的声明必须出现在内联函数第一次被调用之前。
#include <iostream>
using namespace std;
inline int fun(int a, int b)
{
return a*a + b*b;
}
int main()
{
int n = 5, m = 8, k;
k = fun(n, m);
cout << "k = " << k << endl;
return 0;
}
//执行结果
k = 89
3,默认参数和函数重载
c++允许在函数定义或函数声明时,为形参指定默认值,这样的参数称之为默认参数,一般形式为:
返回类型 函数名(...,类型 默认参数名=默认值)
{
函数体
}
注意:
1,如果在函数定义时已经设置了默认参数,那么就不能在函数声明中再次设置
2,可以设置多个默认参数,设置的顺序为自右向左,换言之,要为某个参数设置默认值,则它右边的所有参数必须都是默认参数。
3,默认值可以是常量,全局变量,甚至是一个函数调用(调用实参必须是常量或全局变量的表达式),不可以是局部变量。
4,函数重载
函数重载是在同一个域中用同一个函数名来定义多个函数,但函数参数列表应彼此有不同,或者参数个数不同,或者是参数类型不同,或者两者均有不同。
函数重载说明:
1,重载函数的形参必须不同(个数不同或类型不同)
2,编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用那个函数
3,不要将不同功能的函数声明为重载函数,以免出现调用结果的误解和混淆
5,函数模板
函数模板(function template)是一个独立于类型的函数,可作为一种模式,产生函数的特定类型版本
使用函数模板可以设计通用型的函数,这些函数与类型无关并且只在需要时自动实例化,从而形成“批量型”的编程方式。
函数模板定义的语法规则为:
模板形参表是一对尖括号<>括起来的一个或多个模板形参的列表,不允许为空,形参之间以逗号分隔,其形式有两种。
第一种形式:
Typename 类型参数名1,typename 类型参数名2,,,,
第二种形式:
Class 类型参数名1,class 类型参数名2,,,,
6,函数的嵌套调用
形参只在这个函数执行的时候才会被分配内存,当函数执行完毕后分配的内存会被释放。至于理由……函数的参数属于局部变量,如果一开始就分配的话,别的地方也能访问修改,数据就不确定了。
另外,函数的形参实际是:调用此函数前,将实参压入堆栈,然后跳转到函数的地址执行。函数执行完跳回去时,堆栈弹出,内存释放。
7,函数的递归调用
函数直接或者间接调用自己成为递归调用。c++允许函数递归调用,如图a所示为直接递归调用,图b表示间接递归调用。
阶乘问题:
#include <iostream>
using namespace std;
int f(int n)
{
if (n > 1) return f(n - 1)*n;
return 1;
}
int main()
{
cout << f(5) << endl;
return 0;
}
8,作用域和变量生命期
在函数内部或复合语句中定义的变量,称为局部变量
下列变量是局部变量:
1,在一个函数内部定义的变量
2,函数的形式参数
3,在某个复合语句中出现的变量
在源文件中,但在函数外部定义的变量,成为全局变量,全局变量的有效区域是从定义变量的位置开始到源文件结束。
函数之间数据传递尽管可以利用全局变量,但这样一来也导致两个函数彼此分不开,违背了模块化的原则,所以结构化程序设计提倡少用或者不用全局变量。
使用全局变量编写一个函数swap用于交换两个整数的值:
#include <iostream>
using namespace std;
int a, b;
void swap()
{
int t = a;
a = b;
b = t;
}
int main()
{
cin >> a >> b;
swap();
cout << "a = " << a << " b = " << b << endl;
return 0;
}
c++实体通常有三类:
变量或者对象,例如变量数组。
函数
类型。包含结构体类型,共用体类型,类类型
作用域是程序中的一段区域。在同一个作用域上,c++程序中每一个名字都与唯一的实体对应;如果在不同的作用域上,程序中可以多次使用同一个名字,对应不同作用域中的不同实体。
作用域分有:
文件作用域
函数作用域
块作用域
类型声明作用域
函数原型作用域
实体在作用域内可以使用称为可见,又称之为有效。可见的含义是指实体在作用域上可以使用
下面给出c++实体可见规则:
(1)同一个作用域内不允许有相同名字的实体,不同作用域的实体可以有相同的名字
(2)实体在包含它的作用域内,从定义或者声明的位置开始,按文件行的顺序往后直到作用域结束均是可见的,包含作用域内的所有子区域及其嵌套
(3)若实体a在包含它的作用域内的子区域中出现了相同名字的实体b,则实体a被屏蔽
(4)可以使用extern声明将变量或者函数实体的有效区域往前延伸,称之为前置声明
extern声明变量实体的形式为:
Extern 类型 变量名,.....
extern声明函数原型的形式为:
Extern 返回类型 函数名(类型1 参数名1,......)
Extern 返回类型 函数名(类型1 参数名1,......)
(5)在全局作用域中,变量或者函数实体若使用static修饰,则该实体对于其他源文件是屏蔽的,称之为私有的。在linux kernel里面常用。
Static修饰变量实体的形式为:
Static 类型 变量名[= 初值],... ...
Static修饰函数原型的形式为:
Static 返回类型 函数名(类型1 参数名1,... ...)
Static 返回类型 函数名(类型1,......)
使用举例:
全局变量是作用于整个工程程序的,extern int h , k 是将FILE1.CPP中的h和k变量扩展到FILE2.CPP中
优先调用本文件的f2
对象生命周期:在程序执行过程中对象存在的时间。
动态存储:在程序运行期间,系统为对象动态地分配存储空间。动态存储地特点是存储空间地分配和释放是动态的,要么函数调用(形参)来自动分配释放,要么程序指令来进行人工分配释放,这个生命周期是整个程序运行期的一部分。
动态存储的优点是对象不持久地占有存储空间,释放后让出空闲空间给其他对象地分配。
动态存储在分配和释放有两种形式,一种是由函数调用来自动完成地,称为自动存储,一种是由程序员通过指令来人工完成地,称为自由存储。
静态存储:静态存储是指对象在整个程序运行期间持久占有存储空间,其生命周期与程序运行周期相同。静态存储地特点是对象地数据可以在程序运行期始终保持直到修改为止,或者程序结束为止,静态存储地分配和释放在编译完成时就决定好了,所以除非有必要尽量少地使用静态存储。
自动对象
默认情况下,函数或复合语句中地对象(包含形参)称为自动对象,其存储方式为自动存储(存储空间是系统自动进行地),程序中大多数对象时自动存储。
auto 类型 变量名[];
寄存器变量:
c++语言允许用CPU的寄存器来存放局部变量,称之为寄存器变量。在局部变量前加上register存储类别修饰来定义的,其形式为:
Register 类型 变量名[=初值],... ...
静态局部对象:
在局部对象的前面加上static存储类别修饰用来指明对象是静态局部对象,一般形式为:
Static 类型 变量名[=初值],... ...
C = 10
修饰局部变量
static修饰局部变量时,使得被修饰的变量成为静态变量,存储在静态区。存储在静态区的数据生命周期与程序相同,在main函数之前初始化,在程序退出时销毁。(无论是局部静态还是全局静态)
9,内部函数与外部函数
内部函数:函数本质是全局的,在多文件的程序中,在链接时会检查函数在全局作用域内是否名字唯一,如果不是则会出现链接错误
在函数定义前加上static修饰,则函数称为内部函数,即只能本文件使用,其他文件是没有办法使用的。定义形式为:
Static 返回类型 函数名(形参列表)
{
函数体
}
外部函数
在函数定义前加上extern声明,则函数称为外部函数,定义形式为:
Extern 返回类型 函数名(形式参数列表)
{
函数体
}
c++所有的函数本质上都是外部函数。因此,上面的extern都可以省略
10,头文件与工程文件
如果是多文件结构程序,欲在文件中调用别的文件中的函数,需要有函数的声明,而且每个函数均是如此。如果是函数声明比较多的情况下,在每个文件中都写上函数声明不是很好的办法很难管理。
只引用一个函数 Extern f1();
使用头文件可以解决该问题,其工作原理是通过将每个源文件中外部函数的函数声明等信息集中写到一个文件中,称为头文件(有别于源文件),而别的源文件只需要文件包含这个头文件,则编译时编译器自然就有了函数声明。
工程文件:
多文件结构程序在编译时需要工程文件来管理,不同编译器有不同的工程文件格式。