参考资料:
《全国计算机等级考试二级教程——C语言程序设计》
《21天学通C语言》
库函数
函数是什么
函数是已命名的、执行专项任务的独立的C代码段,可选择是否调用它的程序返回一个值。
函数是已命名的。每个函数都有独一无二的函数名。在程序的其他部分使用函数名,可以执行该函数中的语句。这也称为调用函数,可以在函数中调用其他函数。
函数是独立的。函数可以独立执行任务,无需程序其他部分干预。
函数可以向调用它的程序返回一个值。程序调用函数时,会执行函数中的语句,如果需要的话,可以把特定信息传递给调用它们的程序。
调用C语言标准库函数时要求的include命令行
include命令行必须以#号开头,系统提供的文件以.h作为文件的后缀,文件名用一对双引号" "或一对尖括号<>括起来。
注意:include命令行不是C语句,因此不能在最后加分号。
标准库函数的调用
对库函数的一般调用形式为:
函数名(参数表)
在C语言中,库函数的调用可以以两种形式出现:
出现在表达式中。
作为独立的语句完成某种操作。
函数的定义和返回值
函数定义的语法
C语言函数定义的一般形式如下:
函数返回值的类型名 函数名 (类型名 形式参数1,类型名 形式参数2……) //函数的首部,也称函数头
{
说明部分 //函数体
语句部分
}
函数名和形式参数都是由用户命名的标识符。在同一程序中,函数名必须唯一,形式参数名只要在同一函数中唯一即可,可以与其他函数中的变量同名。
C语言规定,不能在函数的内部定义函数。
若在函数的首部省略了函数返回值的类型名,则默认函数返回值为int类型。
除了返回值类型为int类型的函数外,函数必须先定义(或说明)后调用。
若函数只是用于完成某些操作,没有函数值返回,则必须把函数定义成void类型。
函数定义的例子:
double add (double a, double b)
{
double s;
s = a + b;
return s;
}
分析:在此程序中,第一行称为函数的首部,其中add是函数名,是由用户定义的标识符。在它前面的double是类型名,用来说明函数返回值的类型(通常称为函数返回值的类型),函数值的类型可以是整型、实型、字符型、指针和结构类型。这个例子中的add是双精度型。
函数名后一对圆括号中是形式参数(简称形参)和类型说明表。在每个形参之前都要有类型名,各形参的定义之间用逗号隔开。
以上add函数首部之后的一对花括号之间的是函数体,在函数体中的语句用来完成函数的功能。这个例子的功能是求a和b的值。
函数体可以是空的,例如:
void dummy ()
{
}
定义的函数可以没有形参,函数体内也可以没有任何操作,但函数名后的一对圆括号不能省略。
main()也是一个函数。比较add函数和main函数,会发现它们的结构相同。printf()和scanf()等库函数和用户自定义的函数一样,也是有参数和返回值的函数。
函数的返回值
函数的返回值通过return语句返回,return语句的形式如下:
return 表达式; 或 return (表达式);
也就是说,return语句中的表达式的值就是所求的函数值,此表达式的值的类型必须与函数首部所说明的类型一致。若不一致,则转换为函数值的类型。
当程序执行到return语句时,程序的流程就返回到调用该函数的地方(通常称为退出调用函数),并带回函数值。在同一个函数内,可以在多处出现return语句,但只有第一个被执行的return语句有效,也就是说return语句只可能执行一次。
return语句中也可能不含表达式,这时必须定义函数为void类型,它的作用只是使流程返回到调用函数,并没有确定的函数值。
函数体内也可以没有return语句,这时也必须定义函数为void类型,程序的流程就一直执行到函数末尾的“}”,然后返回调用函数,也没有确定的函数值带回。
这个程序中使用了多条return语句,并返回不同的值:
#include char last_init;
int room;
int room_assign(char last_init);
int main (void)
{
puts("Enter the first initial of your last name: ");
scanf("%c", &last_init);
room = room_assign(last_init);
printf("\nYou need to report to room %d.", room);
return 0;
}
int room_assign(char li)
{
//该if语句测试首字母是在A~M之间还是在N~Z之间
//如果在A~M之间,则分配在1045房间,其余的分配在1055房间
//||用于检查首字母的大小写
if ((li >= 'a' && li <= 'm') || (li >= 'A' && li <= 'M'))
return 1045;
else
return 1055;
}
输出1:
Enter the first initial of your last name:
d
You need to report to room 1045.
输出2:
Enter the first initial of your last name:
R
You need to report to room 1055.
函数的调用
函数的两种调用方式
函数的一般调用形式为:
函数名 (实际参数名)
实际参数(简称实参)的个数多于一个时,各实参之间用逗号隔开。
实参的个数必须与所调用函数中的形参相同,类型一一匹配。
若函数无实参,调用形式为:
函数名 ()
函数名后的圆括号不可少。
一般情况下,可用两种方式调用函数:
当所调用的函数用于求出某个值时,函数的调用可作为表达式出现在允许表达式出现的任何地方。例如对于前面举例的add函数,可以用以下语句求出3.0和4.0的值,然后赋给y(也可以出现在if语句中作为进行判断的表达式): y = add (3.0, 4.0);
C语言中的函数可以仅进行某些操作而不返回函数值,这时函数的调用可作为一条独立的语句,如: dummy();
第一种方法仅适用于有返回值的函数。由于可以对这些函数求值(得到返回值),因此只要是可以使用C表达式的地方都可以使用这些函数。
例如,下面这条语句中,half_of()是printf()函数的实参:
printf("Half of %d is %d.", x, half_of(x));
下面这个例子在一个表达式中使用了多个函数:
y = half_of(x) + half_of(z);
函数调用时的语法要求
函数调用时有以下语法要求:
调用函数时,函数名必须与所调用的函数名完全一致。
实参的个数必须与形参的个数一致,且实参可以是表达式。如果类型不匹配,按照赋值兼容的规则进行转换。
C语言规定,函数必须先定义后调用。如果想在main函数中调用前面提到的add函数,在源程序中应把add函数放在main函数的前面。如果被调用的函数返回值为int或char类型,则被调用函数的定义也可以放在调用的位置之后。
C程序中,函数可以自己直接或间接调用自己,称为递归调用。
函数的递归调用
递归指的是在一个函数中直接或间接地调用自己。如果一个函数调用另一个函数,而后者又调用前者,将发生间接递归。C语言允许递归函数。
例如,递归可用于计算数的阶乘,计算方法如下:
x! = x * (x - 1) * (x - 2) * (x - 3) * …… * 2 * 1
还可以这样写:
x! = x * (x - 1)!
以下这个例子使用递归计算阶乘(注:程序中使用的是unsigned整型,因此输入的值最大为8,9的阶乘超过了unsigned的取值范围):
//函数的递归示例
//计算数的阶乘
#include unsigned int f, x;
unsigned int factorial(unsigned int a);
int main (void)
{
puts("Enter an integer value between 1 and 8: ");
scanf("%d", &x);
if (x > 8 || x < 1)
{
puts("Only values from 1 to 8 are acceptable!");
}
else
{
f = factorial(x);
printf("%u factorial equals %u\n", x, f);
}
return 0;
}
unsigned int factorial (unsigned int a)
{
if (a == 1)
return 1;
else
{
a *= factorial(a - 1);
return a;
}
}
输出:
Enter an integer value between 1 and 8:
6
6 factorial equals 720
函数的说明
函数说明的形式
对于返回值不是int类型的函数,若把定义放在调用之后,应该在调用之前对函数进行说明(或称为函数原型说明、函数原型),函数说明的一般形式如下:
类型名 函数名 (参数类型1,参数类型2,……)
类型名 函数名 (参数类型1 参数名1, 参数类型2 参数名2,……)
例如:
double add (double, double)
double add (double p, double q)
注意,第二种类型的参数名完全是虚设的,可以是任意的用户标识符。
函数说明语句中的类型名必须与函数返回值的类型一致。
函数说明可以是一条独立的语句,如:
double add (double, double);
也可以与普通变量一起出现在同一个类型定义语句中,如:
double x, y, add (double, double);
函数说明的位置
当在所说明函数的外部、被调用之前说明函数时,在对函数进行说明的语句后面所有位置上都可以对该函数进行调用。
函数说明也可以放在调用函数内的说明部分,如在main函数内部进行说明,则只能在main函数内部才能识别该函数。
调用函数和被调用函数之间的数据传递
C语言中,调用函数和被调用函数之间可以通过三种方式进行传递:
实参和形参之间进行数据传递。
通过return语句把函数值返回调用函数。
通过全局变量。
在C语言中,数据只能从实参单向传递给形参,称为“按值”传递。也就是说,当简单变量作为实参时,用户不可能在函数中改变对应实参的值。在调用函数时,形参值的变化不会影响对应的实参。
例如,以下程序师徒通过调用swap函数,把主函数中变量x和y中的数据进行交换。
#include void swap (int, int); //函数说明语句
int main (void)
{
int x = 10, y = 20;
printf("(1) x = %d, y = %d\n", x, y);
swap (x, y);
printf("(4) x = %d, y = %d\n", x, y);
return 0;
}
void swap (int a, int b)
{
int t;
printf("(2) a = %d, b = %d\n", a, b);
t = a;
a = b;
b = t;
printf("(3) a = %d, b = %d\n", a, b);
}
输出:
(1) x = 10, y = 20
(2) a = 10, b = 20
(3) a = 20, b = 10
(4) x = 10, y = 20
由于数据只能从实参单向传递给形参,形参数据变化不会影响对应实参,所以在本程序中,不能通过调用swap函数来交换主函数中的x和y的值。
函数的位置
就现在而言,应该把函数放在main()所在的源文件中,并位于main()的后面。
可以把用户自定义函数放在一个独立的源代码文件中,与main分离。在大型程序中或者要在多个程序中使用同一组函数时,经常会这样做。
内联函数
在C语言中可以创建一种特殊类型的函数——内联函数。内联函数通常都很短小。编译器会将函数代码拷贝进主函数里执行内联函数。待执行的代码段将会被放入主函数中,故称之为内联。
使用inline关键字可以将函数设置为内联。例如,下面的代码段声明了一个内联函数toInches():
inline int toInches (int Feet)
{
return (Feet * 12);
}
程序举例
例1
编写函数isprime(int a),用来判断自变量a是否为素数。若是素数,函数返回整数1,否则返回0。
#include int isprime (int); //isprime函数的说明语句
int main (void)
{
int x;
printf("Enter a integer number:");
scanf("%d", &x); //从键盘输入一个整数
if (isprime (x))
printf("%d is prime\n", x); //当返回值为1时,输出“是素数”
else
printf("%d is not prime", x); //当返回值为0时,输出“不是素数”
}
int isprime (int a) //定义isprime函数
{
int i;
for (i = 2; i <= a/2; i++)
if (a % i == 0) //a一旦能被某个数整除,即不是素数,返回0
return 0;
return 1; //a不能被2到a/2之间任何一个数整除,返回1
}
例2
编写函数myupper(ch),把ch中的小写字母转换成大写字母作为函数值返回,其他字符不变。主函数中不断输入字符,用字符@结束输入,同时不断输出结果。
按要求myupper函数的返回值应该是char类型,程序如下:
#include #include char myupper (char ch)
{
if (ch >= 'a' && ch <= 'z')
ch = ch - 32;
return ch;
}
int main (void)
{
char c;
while ((c = getchar()) != '@')
{
c = myupper (c);
putchar(c);
}
return 0;
}