乐趣区

C学习笔记5

函数

函数定义包括:返回类型、函数名字、由 0 个或多个形参组成的列表以及函数体

函数调用完成两项工作:
1、用实参初始化函数对应的形参
2、被控制权转移给被调用函数

函数的返回类型不能是数组类型或函数类型,但可以使指向数组或函数的指针

形参和函数内部定义的变量统称为局部变量,同时还会隐藏在外层作用域中同名的其他所有声明

局部静态对象

在程序的执行路径第一次经过对象定义语句时初始化,并且知道程序终止才被销毁,在此期间即使对象所在函数结束执行也不会对它有影响

函数声明(函数原型)

如果一个函数永远也不会被用到,那么它可以只有声明没有定义

函数声明与函数定义类似,区别是函数声明无须函数体,用一个分号代替即可

尽量把声明放到头文件中,把定义放到源文件中

参数传递

传值参数

初始化一个非引用类型的变量,初始值被拷贝给变量。对变量的变动不会影响初始值

指针形参

和其他非引用类型一样,执行指针拷贝操作时,拷贝的是指针的值,即指针的值不会改变,指针所指对象的值会改变

传引用参数

通过使用引用实参,允许函数改变一个或多个实参的值

拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型根本就不支持拷贝操作,当某种类型不支持拷贝操作时,函数只能通过引用型形参访问该类型的对象

引用类型的形参可以作为输出参数,也可作为输入参数,这样就可实现返回多个值的函数

const 形参和实参

当用实参初始化形参时会忽略掉顶层 const,所以在函数重载时,const int i = int i

指针或引用形参与 const

可以使用非常量初始化一个底层 const 对象,反过来不行,一个普通的引用必须用同类型对象初始化

尽量使用常量引用

数组形参

不允许拷贝数组、使用数组时通常会将其转换成指针

使用标记指定数组长度

类似字符串以 ’0’ 结束

使用标准库规范

传递指向数组首元素和尾后元素的指针

显式传递一个表示数组大小的形参

专门定义一个表示数组大小的形参

main:处理命令行选项

int main(int argc, char *argv[]) {. . .}
int main(int argc, char **argv[]) {. . .}

第二个形参 argv 是一个数组,它的元素是指向 C 风格字符串的指针,第一个形参 argc 表示数组中字符串的数量
允许 main 函数没有 return 语句直接结束,编译器会隐式插入一条返回 0 的 return 语句,返回 0 表示成功,返回其他值表示失败,非 0 值的具体含义依机器而定

含有可变形参的函数

如果所有的实参类型相同,可以传递一个名为 initializer_list 的标准库类型;
如果实参的类型不同,可以编写一种特殊的函数,也就是所谓的可变参数模板
C++ 还提供了一种特殊的形参类型(即省略符),可以用它传递可变数量的实参,这种功能一般只用于与 C 函数交互的接口程序

initializer_list 形参

提供的操作为:
initializer_list<T> lst; 默认初始化;T 类型元素的空列表
initializer_list<T> lst{a, b, c} lst 的元素数量和初始值一样多;lst 的元素是对应初试值的副本,列表中的元素是 const
lst2(lst) 拷贝或复制一个 initializer_list 对象不会拷贝列表中的元素,拷贝后,原始列表和副本共享元素
lst2 = lst
lst.size() 列表中的元素数量
lst.begin() 返回指向 lst 中首元素的指针
lst.end() 返回指向 lst 中尾元素下一个位置的指针
initializer_list 对象中的元素永远是常量值,无法改变元素的值
如果想向 initializer_list 形参中传递一个值的序列,必须把序列放在一对花括号内

省略符形参

只能出现在形参列表的最后一个位置,形式有以下两种:

void foo(parm_list, ...);
void foo(...);

返回类型和 return 语句

return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方
return 有两种形式:

return;
return expression;

不要返回局部对象的引用或指针
调用运算符的优先级与点运算符和箭头运算符相同,符合左结合律

列表初始化返回值

C++11 允许函数返回花括号包围的值的列表
如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间,如果函数返回的是类类型,由类本身定义初始值如何使用

递归

一个函数直接或间接调用自身,就称该函数为递归函数

返回数组指针

方法一:使用类型别名

typedef int arrT[10];
using arrT = int[10];
arrT *func(int i);

方法二:声明一个返回数组指针的函数
形式为:

Type(*function(parameter_list))[dimension]

Type 表示元素的类型,dimension 表示数组的大小
例如:int (*fun(int i))[10];
方法三:使用尾置返回类型
例如:auto func(int i) -> int(*)[10];
方法四:使用 decltype
例如:

int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
decltype(odd) *arrPtr(int i)
{}

函数重载

函数名字相同,形参数量或者形参类型不同,不允许两个函数除了返回类型外其他所有要素相同

重载和 const 形参

一个拥有顶层 const 的形参无法和另一个没有顶层 const 的形参区分开来
如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时 const 是底层的

调用重载的函数

当调用重载函数的时候有三种可能结果:
编译器找到一个实参最佳匹配的函数,并生成调用该函数的代码
找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配的错误信息
有多于一个函数可以匹配,但是每一个都不是明显的最佳选择,此时发生二义性调用的错误

默认实参

一旦某个形参被赋予了默认值,其后所有形参都必须有默认值,如果调用时想用默认实参,只要在调用时省略该实参
在给定的作用域中一个形参只能被赋予一次默认形参
局部变量不能作为默认实参

内联函数和 constexpr 函数

内联函数

将函数指定为内联函数,就是将它在每个调用点上内联地展开,消除运行时开销
内联机制用于优化规模较小,流程直接,频繁调用的函数

constexpr 函数

constexpr 函数指能用于常量表达式的函数
函数的返回类型及所有形参的类型都得是字面值类型,而且函数体重必须有且仅有一条 return 语句
constexpr 函数被隐式指定为内联函数
允许 constexpr 函数返回值并非一个常量

内联函数和 constexpr 函数可以在程序中多次定义,通常定义在头文件中

调试帮助

assert 预处理宏

是一个预处理变量,类似内联函数,使用一个表达式作为它的条件:

assert(expr);

对 expr 求值,表达式为假,输出信息并终止程序执行,表达式为真,什么也不做,定义在 cassert 头文件中,assert 宏用于检查“不能发生”的条件

NOEBUG 预处理变量

assert 行为依赖于一个名为 NOEBUG 的预处理变量的状态,如果定义了 NOEBUG,则 assert 什么也不做,默认状态下没有定义 NOEBUG,此时 assert 将执行运行时检查
可以使用 #define 语句定义 NOEBUG,关闭调试状态
定义 NOEBUG 避免检查各种条件所需的运行时开销,assert 应仅用于验证那些不可能发生的事情
可用 NOEBUG 编写自己的条件调试代码,如果 NOEBUG 未定义,将执行 #ifndef 和#endif 之间的代码,如果定义了 NOEBUG,这些代码将被忽略

void print(const int ia[], size_t size)
{
#ifndef NDEBUG
        //__func__是编译器定义的一个局部静态变量,用于存放函数的名字, 以一个 const char 静态数组
    cerr << __func__ << ": array size is" << size << endl;
#endif
//...

FILE 存放文件名的字符串字面值
LINE 存放当前行号的整型字面值
TIME 存放文件编译时间的字符串字面值
DATE 存放文件编译日期的字符串字面值

函数匹配

1、选定本次调用对应的重载函数集,集合中的函数称为候选函数集,集合中的函数称为候选函数
候选函数有两个特征:与被调用函数同名,声明在调用点可见
2、考察本次调用可提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数
可行函数有两个特征:形参数量与本次调用提供的实参数量相等,每个实参的类型与对应的形参类型相同
如果函数含有默认实参,则我们调用该函数时传入的实参数量可能少于它实际使用的实参数量
3、从可行函数中选择与本次调用最匹配的函数
如果有且仅有一个函数满足下列条件,则匹配成功:
· 该函数每个实参的匹配都不劣于其他可行函数需要的匹配
· 至少有一个实参的匹配优于其他可行函数提供的匹配

实参类型转换

1、精准匹配
· 实参类型和形参类型相同
· 实参从数组类型或函数类型转换成对应的指针类型
· 向实参添加顶层 const 或者从实参中删除顶层 const
2、通过 const 转换实现的匹配
3、通过类型提升实现的匹配
4、通过算术类型转换或指针转换实现的匹配
5、通过类类型转换实现的匹配

函数匹配和 const 实参

如果重载函数的区别在于它们的引用类型的形参是否引用了 const,或者指针类型的形参是否指向 const,则当调用发生时编译器通过实参是否是常量来决定选择哪个函数

函数指针

指向函数,类型由它的返回类型和形参类型共同决定,与函数名无关
函数名作为一个值使用时,函数自动转换成指针
可直接用指向函数的指针调用函数,无需提前解引用指针
函数指针可作为形参和实参
decltype 返回函数类型,不会将函数类型自动转换成指针类型
返回类型不会自动转换成指针,必须显式将返回类型指定为指针

退出移动版