参考资料:高教版《全国计算机等级考试二级教程——C语言程序设计》、《21天学通C语言》

库函数

函数是什么

函数是已命名的、执行专项任务的独立的C代码段,可选择是否调用它的程序返回一个值。

  • 函数是已命名的。每个函数都有独一无二的函数名。在程序的其他部分使用函数名,可以执行该函数中的语句。这也称为调用函数,可以在函数中调用其他函数。
  • 函数是独立的。函数可以独立执行任务,无需程序其他部分干预。
  • 函数可以向调用它的程序返回一个值。程序调用函数时,会执行函数中的语句,如果需要的话,可以把特定信息传递给调用它们的程序。

调用C语言标准库函数时要求的include命令行

include命令行必须以#号开头,系统提供的文件以.h作为文件的后缀,文件名用一对双引号“ “或一对尖括号<>括起来。

注意:include命令行不是C语句,因此不能在最后加分号。

标准库函数的调用

对库函数的一般调用形式为:

函数名(参数表)

在C语言中,库函数的调用可以以两种形式出现:

  1. 出现在表达式中。
  2. 作为独立的语句完成某种操作。

函数的定义和返回值

函数定义的语法

C语言函数定义的一般形式如下:

函数返回值的类型名 函数名 (类型名 形式参数1,类型名 形式参数2……)        //函数的首部,也称函数头
{
    说明部分        //函数体
    语句部分
}
  • 函数名和形式参数都是由用户命名的标识符。在同一程序中,函数名必须唯一,形式参数名只要在同一函数中唯一即可,可以与其他函数中的变量同名。
  • C语言规定,不能在函数的内部定义函数
  • 若在函数的首部省略了函数返回值的类型名,则默认函数返回值为int类型
  • 除了返回值类型为int类型的函数外,函数必须先定义(或说明)后调用
  • 若函数只是用于完成某些操作,没有函数值返回,则必须把函数定义成void类型。

函数定义的例子:

1
2
3
4
5
6
double add (double a, double b)
{
double s;
s = a + b;
return s;
}

分析:在此程序中,第一行称为函数的首部,其中add是函数名,是由用户定义的标识符。在它前面的double是类型名,用来说明函数返回值的类型(通常称为函数返回值的类型),函数值的类型可以是整型、实型、字符型、指针和结构类型。这个例子中的add是双精度型。

函数名后一对圆括号中是形式参数(简称形参)和类型说明表。在每个形参之前都要有类型名,各形参的定义之间用逗号隔开。

以上add函数首部之后的一对花括号之间的是函数体,在函数体中的语句用来完成函数的功能。这个例子的功能是求a和b的值。

函数体可以是空的,例如:

1
2
3
void dummy ()
{
}

定义的函数可以没有形参,函数体内也可以没有任何操作,但函数名后的一对圆括号不能省略

main()也是一个函数。比较add函数和main函数,会发现它们的结构相同。printf()和scanf()等库函数和用户自定义的函数一样,也是有参数和返回值的函数。

函数的返回值

函数的返回值通过return语句返回,return语句的形式如下:

1
return 表达式;		或		return (表达式);

也就是说,return语句中的表达式的值就是所求的函数值,此表达式的值的类型必须与函数首部所说明的类型一致。若不一致,则转换为函数值的类型。

当程序执行到return语句时,程序的流程就返回到调用该函数的地方(通常称为退出调用函数),并带回函数值。在同一个函数内,可以在多处出现return语句,但只有第一个被执行的return语句有效,也就是说return语句只可能执行一次

return语句中也可能不含表达式,这时必须定义函数为void类型,它的作用只是使流程返回到调用函数,并没有确定的函数值。

函数体内也可以没有return语句,这时也必须定义函数为void类型,程序的流程就一直执行到函数末尾的“}”,然后返回调用函数,也没有确定的函数值带回。

这个程序中使用了多条return语句,并返回不同的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
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.

函数的调用

函数的两种调用方式

函数的一般调用形式为:

1
函数名 (实际参数名)

实际参数(简称实参)的个数多于一个时,各实参之间用逗号隔开。

实参的个数必须与所调用函数中的形参相同,类型一一匹配。

若函数无实参,调用形式为:

1
函数名 ()

函数名后的圆括号不可少。

一般情况下,可用两种方式调用函数:

  1. 当所调用的函数用于求出某个值时,函数的调用可作为表达式出现在允许表达式出现的任何地方。例如对于前面举例的add函数,可以用以下语句求出3.0和4.0的值,然后赋给y(也可以出现在if语句中作为进行判断的表达式):

    1
    y = add (3.0, 4.0);
  2. C语言中的函数可以仅进行某些操作而不返回函数值,这时函数的调用可作为一条独立的语句,如:

    1
    dummy();

第一种方法仅适用于有返回值的函数。由于可以对这些函数求值(得到返回值),因此只要是可以使用C表达式的地方都可以使用这些函数。

例如,下面这条语句中,half_of()是printf()函数的实参:

1
printf("Half of %d is %d.", x, half_of(x));

下面这个例子在一个表达式中使用了多个函数:

1
y = half_of(x) + half_of(z);

函数调用时的语法要求

函数调用时有以下语法要求:

  1. 调用函数时,函数名必须与所调用的函数名完全一致
  2. 实参的个数必须与形参的个数一致,且实参可以是表达式。如果类型不匹配,按照赋值兼容的规则进行转换。
  3. C语言规定,函数必须先定义后调用。如果想在main函数中调用前面提到的add函数,在源程序中应把add函数放在main函数的前面。如果被调用的函数返回值为intchar类型,则被调用函数的定义也可以放在调用的位置之后。
  4. C程序中,函数可以自己直接或间接调用自己,称为递归调用

函数的递归调用

递归指的是在一个函数中直接或间接地调用自己。如果一个函数调用另一个函数,而后者又调用前者,将发生间接递归。C语言允许递归函数。

例如,递归可用于计算数的阶乘,计算方法如下:

1
x! = x * (x - 1) * (x - 2) * (x - 3) * …… * 2 * 1

还可以这样写:

1
x! = x * (x - 1)!

以下这个例子使用递归计算阶乘(注:程序中使用的是unsigned整型,因此输入的值最大为8,9的阶乘超过了unsigned的取值范围):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//函数的递归示例
//计算数的阶乘

#include <stdio.h>
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,参数类型2,……)
类型名 函数名 (参数类型1 参数名1, 参数类型2 参数名2,……)

例如:

1
2
double add (double, double)
double add (double p, double q)

注意,第二种类型的参数名完全是虚设的,可以是任意的用户标识符。

函数说明语句中的类型名必须与函数返回值的类型一致。

函数说明可以是一条独立的语句,如:

1
double add (double, double);

也可以与普通变量一起出现在同一个类型定义语句中,如:

1
double x, y, add (double, double);

函数说明的位置

当在所说明函数的外部、被调用之前说明函数时,在对函数进行说明的语句后面所有位置上都可以对该函数进行调用。

函数说明也可以放在调用函数内的说明部分,如在main函数内部进行说明,则只能在main函数内部才能识别该函数。

调用函数和被调用函数之间的数据传递

C语言中,调用函数和被调用函数之间可以通过三种方式进行传递:

  1. 实参和形参之间进行数据传递。
  2. 通过return语句把函数值返回调用函数。
  3. 通过全局变量。

在C语言中,数据只能从实参单向传递给形参,称为“按值”传递。也就是说,当简单变量作为实参时,用户不可能在函数中改变对应实参的值。在调用函数时,形参值的变化不会影响对应的实参。

例如,以下程序师徒通过调用swap函数,把主函数中变量x和y中的数据进行交换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
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():

1
2
3
4
inline int toInches (int Feet)
{
return (Feet * 12);
}

程序举例

例1

编写函数isprime(int a),用来判断自变量a是否为素数。若是素数,函数返回整数1,否则返回0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
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类型,程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <ctype.h>
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;
}

最后更新: 2019年03月07日 21:46

原始链接: qnkcdz0.xyz/2019/02/26/计算机二级C语言之函数/