目录
9.1 复习函数
函数(function)是完成特定任务的独立代码单元。
函数让程序更加模块化,提高代码可读性,方便修改。
9.1.1 创建并使用简单函数
函数原型指明了函数的返回值类型和函数接受的参数类型。这些信息称为该函数的签名(signature)
/*letheadl.c*/
#include <stdio.h>
#define NAME "GIGATHINK,INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis,CA 94904"
#define WIDTH 40
void starbar(void);
int main(void){
starbar();
printf("%s\n",NAME);
printf("%s\n",ADDRESS);
printf("%s\n",PLACE);
starbar();
return 0;
}
void starbar(void){
int count;
for(count= 1;count<=WIDTH;count++)
putchar('*');
putchar('\n');
}
9.1.2 分析程序
- 程序在3处使用了starbar标识符:函数原型(是函数的声明,它告诉编译器函数的名称、参数类型和返回值类型),函数调用(是在程序中使用函数的过程。当程序需要执行某个特定的任务时,可以调用相应的函数。),函数定义(是实现函数功能的具体代码。它包含函数的名称、参数列表和函数体。)
- 函数的原型指明了函数的返回值和函数接受的参数类型,这些信息被称为该函数的签名
- 程序中的starbar()和main()的定义形式相同,首先包含了函数头包括函数类型和函数名和圆括号
9.1.3 函数参数
9.1.4 定义带形式参数的函数
ANSI C风格的函数头:
void show_n_char(char ch, int num)
改行告知编译器本函数具有两个参数ch和num,类型分别是char和int。这两个参数被称为形式参数(formal parameter),简称形参。形式参数是局部变量。
注意每个变量前都要声明其类型,不能写成这样:void dibs(int x, y, z)
9.1.5 声明带形式参数函数的原型
函数原型一般是下面这种形式:
void show_n_char(char ch, int num)
也可以省略变量名:
void show_n_char(char, num)
PS:在原型中使用变量名并没有实际创建变量,char仅代表一个char类型的变量
9.1.6 调用带实际参数的函数
函数调用中,实际参数(actual argument,简称实参)提供了ch和num的值。
show_n_char(SPACE,12)
调用时实参的值(SPACE,12)赋值给函数中相应的形式参数(ch,num)。
实参和形参运用过程
实参是出现在函数调用圆括号中的表达式,形参是函数定义的函数头中声明的变量,调用函数时,创建了声明为形参的变量并初始化为实参的求值结果
9.1.7 黑盒视角
基本内容概括为形参和实参的局部和全局区别(局部变量和全局变量的说明,简单略)
9.1.8 使用return从函数中返回值
这里告诉我们一个定义,对于被设计用于测试函数的程序有时被称为驱动程序
return 的用法除了返回值外,还可用与终止函数
return;
这条语句会导致终止函数,并把控制权还给主调函数,因为return后面没有任何表达式,所以没有返回值,只有在void函数中才会用到这种形式
9.1.9 函数类型
声明函数时必须声明函数的类型,带返回值的函数类型和其返回值类型相同,
不带返回值的函数应该声明为void类型。
9.2 ANSI C函数原型
int imin();
之前的声明会出现问题没有给出imin()函数的参数个数和类型,当调用它时使用参数个数不对或类型不匹配,编译器根本不会察觉出来
9.2.1 问题所在
//misuse.c--错误地使用函数
#include <stdio.h>
int imax();
int main(void){
printf("The maximum of %d and %d is %d\n",3,5,imax(3));
printf("The maximum of %d and %d is %d\n",3,5,imax(3.0,5.0));
}
int imax(n,m)
int n,m;{
return (n>m?n:m);
}
主调函数把它的参数存储在被称为栈的临时存储区,被调函数从栈中读取这些参数
9.2.2ANSI的解决方案
针对参数不匹配的问题,ANSI C要求函数声明时还要声明变量的类型,即 使用函数原型来声明函数的返回类型、参数的数量和每个参数的类型。有了这些信息,编译器可以检查函数调用是否和函数原型匹配。
9.2.3 无参数和未指定参数
为了表明函数没有参数,应该在圆括号内使用void
void print_name(void);
一些函数接受许多参数,ANSI C允许使用部分原型:
int printf(const char *, ...)
表明第一个参数是字符串,可能还有其他参数。
9.2.4 函数原型的优点
让编译器在第1次执行到该函数前就知道如何使用它。
将整个函数定义放在第1次调用该函数之前,也有同样的效果。
此时,函数定义也相当于函数原型。
9.3 递归(略)
C允许函数调用自己,这种调用称为递归(recursion)。
9.3.1 演示递归
PS:每次递归的变量n都属于本级递归私有
9.3.2 递归的基本原理
-
每级的函数调用都有自己的变量
-
每次函数调用都会返回一次
-
递归函数中位于递归调用之前的语句,均按被调用函数的顺序执行
-
递归函数中位于递归调用之后的语句,均按被调函数相反的顺序执行
-
虽然每级递归都有自己的变量,但是并没有拷贝函数的代码1
-
递归函数必须包含能让递归调用停止的语句
9.3.3 尾递归
最简单的递归形式是把递归调用置于函数的末尾,即return之前。这种形式的递归称为尾递归。
相对于循环递归的缺点
- 每次递归都会创建一组变量,所以递归使用的内存更多,而且每次递归调用都会把创建的一组新变量放在栈中。递归调用的数量受限于内存空间。
- 由于每次函数调用要花费一定的时间,所以递归的执行速度较慢
9.3.4 递归和倒序计算
9.3.5 递归的优缺点
优点:
递归为某些编程问题提供了最简单的解决方案
缺点:
是一些递归算法会快速消耗计算机的内存资源
9.4 编译多源代码文件的程序(略)(程序待复现)
在这个代码演示中,scanf("%*s")跳至下一个空白字符处(在本段代码里表示跳过第一个字符串)
9.5 查找地址:&运算符
指针(pointer)是C语言最重要的概念之一,用于存储变量的地址。
&运算符给出变量的地址,pooh是变量,&pooh就是变量pooh的地址。
PS:PC地址通常用十六进制形式表示
%p是输出地址的转换说明
9.6更改主调函数中的变量
9.7 指针简介
指针(pointer)是一个值为内存地址的变量。
9.7.1 间接运算符:*
假设ptr指向bah:
ptr = &bah;
使用简介运算符* 可以找出bah中的值
val = *ptr;
9.7.2 声明指针
声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间。
下面是一些指针的声明:
int * pi; //pi 是指向int变量的指针
float * pf, *pg
9.7.3 使用指针在函数间通信
使用指针的函数
void interchange(int * u, int * v)
调用:interchange(&a,&b);
当被调用时;该函数传递的不是a b 的值,而是它们的地址,这意味着出现在interchange()原型和定义中的形参u v 将把地址作为它们的值