第一章 函数定义与使用
函数定义
类型名 函数名(参数表列)
{
语句序列
}
- 返回值:
※函数返回值由return语句给出,return语句还有一个作用是结束当前函数执行。
- 形式参数:
※函数在没有调用时候是静止的,此时形参只是一个符号,它标志这在形参出现的位置应该有个什么类型的数据。在函数被调用时才由主调函数将实参赋予形参。
函数调用
- 函数的声明和函数原型声明:
※函数调用前需要声明。函数的定义就属于声明,所以定义函数之后可以直接调用。如果要在函数定义前调用,需要添加函数原型声明。
※函数原型声明格式:类型名 函数名(参数表列);
- 嵌套调用:
※函数允许嵌套调用。当被调函数调用了其他的函数时,称为嵌套调用。
- 递归调用:
※函数可以直接或间接地调用自身,称为递归调用。递归有两个过程:踢腿和回归。
※递推是指将原问题不断分解成新的子问题当子问题已知时,递推结束。
※回归是从已知条件出发,按照递推的逆过程逐一求值回归。
参数的传递
概述
※函数参数传递就是形参和实参的结合(形实结合),传参有值传递和引用传递两种。
值传递
- 函数声明:
DoSomething(int a, int b){…;}
- 值传递过程:
发生了参数值的单向传递。函数调用时给形参分配内存空间,用实参来初始化形参(直接传值)。形参获得值之后便与实参脱离关系。
引用传递
- 什么是引用:
引用是一种特殊的变量,可以认为是另一个变量的别名。引用声明时必须初始化赋值。一旦一个引用被初始化后,就不能改为指向其他对象。
- 函数声明:
DoSomething(int &a, int &b){…;}
- 引用传递过程:
执行主调函数的调用表达式时,才为形参分配内存空间,并初始化形参。形参成为实参的别名。
CSDN: C++里不像C#进行了值类型和引用类型的区分。对于C++来说,如果在函数中不标明&引用,一般就是按值传递,但貌似数组除外。C++的类对象有两种声明方式,分别放在栈和堆中。 CRE:C#中的变量分为了值类型和引用类型。值类型要实现引用传值可以用
ref
关键字声明形参和实参。字符串虽然是引用类型,但是它有不可变性,所以它不用ref
关键字就不能实现引用传递。
默认参数
默认参数有两个规则:
- 默认值的解析(resolve)操作由最右边开始进行。如果我们为某个参数提供了默认值,那么这一参数右侧的所有参数都必须具有默认值才行。
- 默认值只能指定一次,可以在函数声明处,也可以在函数定义处,但不能在两个地方都指定。(头文件可为函数带来更高的可见性,为了更高的可见性,我们将默认值放在函数声明处而非定义处)
内联(inline)函数
将函数声明为inline,表示要求编译器在每个函数调用点上,将函数的内容展开。面对一个inline函数,编译器可将该函数的调用操作改为以一份函数代码副本代替。
inline函数编译时是将函数体嵌入每一个调用处。他严格讲不算函数调用,不发生控制转移。
inline函数的定义,常常被放在头文件中。由于编译器必须在它被调用的时候加以展开,所以这个时候其定义必须是有效的。
- 声明格式:
inline void DoSomething(){…;}
- 注意:
inline
关键字只是一个要求,编译器不承诺将inline修饰的函数作为内联。太复杂的内联函数会被编译器自动转换为普通函数。
- 成员函数:
C++中,如果成员函数直接在类定义中进行定义,它被默认视为inline
函数。
函数重载
两个以上的函数,具有相同的函数名,但是形参类型或个数不同。编译器根据实参形参类型、个数的最佳匹配,自动确定调用哪一个函数。这就是函数重载。
编译器无法根据函数的返回值类型来区分两个具有相同名称的函数。(CRE:函数可以按参数重载但不可以按返回值类型重载)
函数指针
所谓函数指针(pointer to function),其形式很复杂。它必须指明其所指函数的返回类型以及参数列表。此外函数指针的定义必须将*
放在某个位置,表示这是一个指针,最后还要给予一个名称。
声明格式:
returntype (*ptrname) (paramtype);
(指针名称所在的小括号是用来强调运算优先级的)
示例:
const vector<int>* (*sep_ptr) ( int );
(返回类型是一个const vector<int>*
)
有函数指针指向的函数,其调用方式和一般函数相同:
auto result = func_ptr();
校验是否真的指向某函数:
if(!func_ptr) printf("null function ptr!");
使用typedef简化代码:
typedef int (*func_ptr_type) (int, int);
func_ptr_type fptr = add;//add的定义为:int add(int a, int b){ return a + b;}
cout << fptr(1, 1) << endl;//输出:2
设定头文件
我们把函数声明放在头文件中,并在每个程序代码文件include这些函数声明。使用这种编程习惯,我们只需要为函数维护一份声明即可。
“只定义一份”规则有一个例外,那就是inline
函数的定义。为了能展开inline函数的内容,在每个调用点上,编译器都必须取得其定义。这意味着我们必须将inline函数定义在头文件中,而不是把他们放在源代码文件中。
文件作用域的对象如果要跨文件访问,应该在头文件使用extern
声明,例如extern return_type (*ptrname) (paramtype);
。如果不加extern
就成了定义。
C++系统函数
C++的系统库提供了几百个函数可以供程序员使用。要使用数学函数,只要嵌入头文件cmath。
系统函数又分为标准C++函数和非标准C++函数。编程时优先使用标准C++函数,有好的可移植性。
补充:可变参数
void PrintStudents(const char* title, ...)
{
std::va_list args;
va_start(args, title); //找到第一个参数title的堆栈地址?
for (int i = 0; i < 999; i++)
{
auto arg = va_arg(args, const char*); //从参数列表XXX获取参数值,。并让指针上移一个_INTSIZEOF(YYY),以指向下一个可变参数地址。
if (arg == NULL) break;//CRE:末尾标记?
std::cout << "姓名:" << arg << std::endl;
}
va_end(args); //arglist置空
}
int main()
{
PrintStudents("学生列表", "张三", "李四", "王五", NULL);
system("pause");
}
(END)