信奥竞赛

系列

剑指信奥 | C 语言之一劳永逸的函数

逆序带来的思考

上次课,我们学习了字符数组,在最后,我们留下了一个思考题:

字符串的逆序

问题描述

完成字符串的逆序输出

输入格式

字符串 “Hello, String!”

输出格式

逆序结果 "!gnirtS ,olleH"

这道题是把字符串进行逆序 reverse处理,就是把原字符串反向输出。

代码片段是这样的:

#include <stdio.h>
int main() {
    // 原字符串
    char s[] = "Hello, World!";

   // TODO 实现原字符串的逆序,并输出

}
                

你做出来了吗?

我们一起来分析一下,要求得这个字符数组的逆序,只要能实现从后向前的循环输出就可以了。

但是既然要用到循环,我们需要知道这个字符数组元素的个数,如果知道了元素的个数,我们就可以用 for 循环的方式来实现逆序。

于是问题转化为怎样求得这个字符数组的元素个数。这个并不难,我们也可以在一个循环中用一个计数器来计算字符的个数。

但是,循环何时结束呢,不然就陷入无限循环了...

别忘了,上次课,我们提到了一个 C 语言中字符数组很特别的一点,就是每个数组的最后一个元素总是 \0,而这,就是我们循环结束的标志!

到此为止,我们有了解决这个问题的整体思路,那就一步一步的来实现吧。

代码 1:
#include <stdio.h>

int main() {
    char s[] = "Hello, World!";
    // 计数器,同时作为索引
    int count = 0;
    while (s[count] != '\0') {
        count++;
    }
    printf("%d", count); // 输出:13
    // TODO 实现原字符串的逆序,并输出
}
                

这样,我们求得了字符数组元素的个数 13。

代码 2:
#include <stdio.h>
int main() {
    char s[] = "Hello, World!";
    int count = 0;
    while (s[count] != '\0') {
        count++;
    }
    // TODO 实现原字符串的逆序,并输出
    // 定义逆序数组 r
    char r[count + 1];
    // 用 for 循环实现 s 的逆序,赋值给 r
    for (int i = 0; i < count; i++) {
        r[i] = s[count - i - 1];
    }
    printf("%s", r); // 输出 !dlroW ,olleH
}
                

这样,我们就求得了 s 数组的逆序数组 r,并实现了输出。

问题是,现在这个代码过程只是把 “Hello, World!” 实现了逆序,如果换一个字符串,代码还需要做更改才可以,能不能避免这种重复性的操作呢?

让我们来探索一下 C 语言的函数,它可以解决这个问题。

C 语言的函数

函数 function 是 C 语言中非常重要的一个内容,任何的高级计算机语言都有函数的存在。

那什么是函数呢?

函数就是可以执行一个特定任务的代码块:

函数在数学中为两个不为空集的集合间的一种对应关系:输入值集合中的每项元素皆能对应唯一一项输出值集合中的元素。

Wikipedia

函数就像机器,给予输入产生输出

我们可以把函数看作一个“黑箱”,不必关注里面做了什么,只要了解这个黑箱需要什么输入,能够产生什么输出就可以,这样,这个函数就可以为我们所用。

其实,我们第一天写代码时,就已经接触过 C 语言的函数了:

C语言的基本结构单位是函数。

系统首先调用 main 函数(主函数),通过函数的嵌套调用,再调用其他函数。

函数可以是系统自带的函数,也可以是用户定义的函数。

Wikipedia

看到了吗?我们每次写的 int main(){} 就是 C 语言的一个函数,它是一个程序执行任务的开始。

而我们更加频繁使用的 printf() 也就是一个 C 语言的标准库函数,它被定义在头文件 header file stdio.h 中,所以,我们要做输出,必须引入这个头文件:

#include <stdio.h>

是不是更加清楚了我们代码的主体结构?

函数的分类

在 C 语言中,函数分为两大类:

  1. 标准库函数
  2. 自定义函数

我们来学习一个跟数学运算有关的库函数:sqrt() 是定义在头文件 math.h 中的一个库函数,它的作用是求一个数的平方根。

好,我们来测试一下:

#include <stdio.h>
#include <math.h>
int main() {
    double d = 9.0;
    double r = sqrt(d);
    printf("%lf", r); // 输出 3.000000
}
                

很快,我们得到了 9 的平方根。

函数的作用

使用函数有什么好处呢?

作用体现在很多方面,我们来了解一下函数比较明显的几个优势:

  • 使用了函数的程序更容易理解、维护以及调试。
  • 函数可以在其他程序中重用,提高了编码效率。
  • 大的程序分解为小的模块,便于多人协同开发。

所以,重视函数,学好函数是开发大型程序的必要条件。

除了我们可以使用的大量的 C 语言标准库函数之外,我们在实际的编程过程中,更需要根据具体的需求编写属于我们自己的函数。

这意味着,我们不仅要会用函数,也需要会写函数,而这就是 C 语言的自定义函数。

那么,怎样来自己定义一个函数呢?

自定义函数

在 C 语言中,一个函数一般有以下几个部分组成:


返回类型 函数名(类型1 参数1, 类型2 参数2, ...) {
    // 函数体
    返回控制
}
                

看起来好像很复杂?我们先来定义一个函数:


int sum(int x, int y) {
    int z = x + y;
    return z;
}
                

这样我们就定义了一个计算两个整数之和的函数,从这个函数中,我们需要了解到:

  1. 这个函数的返回类型为 int
  2. 这个函数的名字是 sum
  3. 这个函数有两个参数 parameter x 和 y
  4. 这个函数的作用是求两个数的和
  5. 这个函数的返回值是两个数的和

这样,我们就完整的了解了一个 C 语言函数的定义过程。

在函数定义完成后,我们就可以使用这个函数了,这个过程叫做调用 calling:

#include <stdio.h>
int sum(int x, int y) {
    int z = x + y;
    return z;
}
int main() {
    printf("%d", sum(1, 2)); // 输出 3
}
                

我们在主函数 main() 中调用了函数 sum(),求得了 1 和 2 的和,得到了结果。

需要注意的是,在 C 语言中,函数的参数是可有可无的,函数的返回值也是可有可无的,因此,根据这一点,C 的函数又分为了这样几种类型:

  • 无参无返回值
  • 无参有返回值
  • 有参无返回值
  • 有参有返回值

那么,在实际编程过程中,我们到底要使用哪一种类型呢?

答案很简单:一切依据你的需要来,代码是死板的,编写代码的我们是灵活的~

最后一个思考题:实现用户输入的任意字符串的逆序,把这个逆序的过程编写为函数,试一下吧!

日积月累

  • function 函数
  • parameter 参数
  • calling 调用
  • header file 头文件