这篇来说说函数的其他内容,如原型,递归等.
1.函数原型
这节内容在书9.2章.
1.1 简介与使用
函数原型相当于简单介绍了这个函数的名称,返回值类型和参数类型.比如下面这两个函数
double cube_volume(double l, double w, double h) { //1
double v = l * w *h;
return v;
}
int print_line(void) { //========================2
printf("-----------------\n");
return 0;
}
它们的函数原型是
double cube_volume(double, double, double); //1
double cube_volume(double l, double w, double h);
int print_line(void); //2
void表示这个函数确实没有参数,而这种情况则是"假定用户没有用原型来声明函数,它将不会检查参数"(255页).
int print_line(); //小括号中什么都不写
所以推荐在确实没有参数的情况下应该写void.
1.2 作用
编译器可根据函数原型来检查函数调用是否与函数原型匹配.比如参数的数量类型是否一致.如果不一致,编译器会发出警告提醒程序员注意.
而且原型可以让编译器知道在第一次执行前知道该如何使用这个函数.
2.递归
这节内容在书9.3章.
2.1 什么是递归
递归的意思就是自己调用自己.比如下面的函数就是一个递归.
int re(void) {
print("递归调用");
re(); //自己调用自己
return 0;
}
2.2 递归如何工作
看看下面的代码,直到传入的参数time大于等于4才会停止调用自己.
# include <stdio.h>
int print(int); //原型
int main(void) {
print(1); //传入1,需要调用4次.
return 0;
}
int print(int time) {
printf("[前]第[%d]级调用,地址: %u\n", time, &time);
if(time < 4)
print(time + 1);
printf("[后]第[%d]级调用,地址: %u\n", time, &time);
return 0;
}
运行结果
[前]第[1]级调用,地址: 2293296 -----1
[前]第[2]级调用,地址: 2293248 ----2
[前]第[3]级调用,地址: 2293200 ---3
[前]第[4]级调用,地址: 2293152 --4
[后]第[4]级调用,地址: 2293152 --4
[后]第[3]级调用,地址: 2293200 ---3
[后]第[2]级调用,地址: 2293248 ----2
[后]第[1]级调用,地址: 2293296 -----1
简单分析一下.在刚刚被调用,即time=1时,time是小于4的,于是在if中继续调用并自己+1且并没有执行if之后的输出语句,等于2,等于3时依旧是这样. 而在等于4是,由于4<4为假,所以跳出分支执行if后面的输出语句,4返回,之后3,2,1依次执行结束并返回.
所以这个程序实际的效果是这样的
-> : 调用
<- : 返回,控制权交给上一级.
main() -> print(1) -> print(2) -> print(3) -> print(4)
| 4 < 4 为假,不再递归调用自己
main() <- print(1) <- print(2) <- print(3) <- print(4)
2.3 注意事项
使用递归千万要注意的一点是,需要有让程序退出的条件,否则容易导致程序崩溃.
再有一点,需要考虑效率问题,比如下面这个求斐波那契数列的程序
long fbnq(long n) {
if(n > 2) {
printf("===\n");
return fbnq(n - 1) + fbnq(n - 2);
} else {
printf("---\n");
return 1;
}
}
看起来只是一个简单的加法,但是实际运行会发现,就算只是传入10,也会输出很多行,可以想象就算数字只有30,也是相当可怕的了.很容易迅速用掉电脑的内存,容易导致程序崩溃.
3.&运算符与指针
这节内容在书9.5,9.7章.
3.1 &运算符
运算符&负责给出变量的存储地址.比如time是一个变量名,则&time是变量的地址,"可以把地址看作是变量在内存的地址"(267页).
可以使用"%p"转换说明来输出变量的地址.也可以使用"%u".
int main(void) {
int time = 60;
printf("time的地址(p转换说明):%p\ntime的地址(u转换说明):%u\ntime的值为%d\n",&time, &time, time);
return 0;
}
运行结果
time的地址(p转换说明):000000000022FE4C
time的地址(u转换说明):2293324
time的值为60
输出的这个"000000000022FE4C"或"2293324"就是变量"time"在内存中的地址了.
int address(int time) {
int t = 60;
printf("t的地址(p转换说明):%p\nt的地址(u转换说明):%u\nt的值为%d\n",&t, &t, t);
printf("============================\n");
printf("time的地址(p转换说明):%p\ntime的地址(u转换说明):%u\ntime的值为%d\n", &time, &time, time);
return 0;
}
int main(void) {
int time = 60;
address(time);
return 0;
}
运行结果
t的地址(p转换说明):000000000022FE0C
t的地址(u转换说明):2293260
t的值为60
============================
time的地址(p转换说明):000000000022FE20
time的地址(u转换说明):2293280
time的值为60
再来看看下面的程序
int address(int time) {
int t = 60;
printf("address()函数\nt的地址(p转换说明):%p\nt的值为%d\n", &t, t);
printf("============================\n");
printf("time的地址:%p\ntime的值为%d\n", &time, time);
return 0;
}
int main(void) {
int time = 60;
int t = 60;
printf("main()函数\nt的地址(p转换说明):%p\nt的值为%d\n", &t, t);
printf("============================\n");
printf("time的地址:%p\ntime的值为%d\n", &time, time);
printf("============================\n");
address(time);
return 0;
}
运行结果
main()函数
t的地址(p转换说明):000000000022FE48
t的值为60
============================
time的地址:000000000022FE4C
time的值为60
============================
address()函数
t的地址(p转换说明):000000000022FE0C
t的值为60
============================
time的地址:000000000022FE20
time的值为60
两个"time",两个"t"的地址都不同.
3.2 指针简介
指针"是一个值为内存地址的变量(或数据对象)"(269页).
假如"pointer"是一个指针变量,那么下面的语句
pointer = &p;
的意思是将"p"的"地址"赋给"pointer".
与指针相关的运算符还有一个"*".注意,虽然与乘法的符号一样,但是意义完全不同.
下面的语句
val = *p; //p就是上面代码中的p.
的意思是找出"p"指向的值并赋值给val.下面两条语句
p = &pointer;
val = *p;
的意思是,"p"为"pointer"在内存中的地址,"val"将会被赋值为地址为"p"的变量的值. 所以"*"也可以称为"间接运算符".
可以简单理解为"将val赋值为pointer",所以可以简化为下面一条语句
val = pointer;