函数

随着程序规模的扩大,你的 main() 函数可能变得越来越臃肿了。这时候使用函数将不同的模块分开书写,可以让程序变得条理清晰、简明易懂。例如下面是 OJ 习题 “环形矩阵” 的一段代码:

int n;
int a[100][100];

int main ()
{
    scanf("%d", &n);
    for (int k = 1; k <= (n + 1) / 2; k++)
        for (int i = k; i <= n + 1 - k; i++)
            for (int j = k; j <= n + 1 - k; j++)
                a[i][j] = k;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
            printf("%4d", a[i][j]);
        puts("");
    }
    return 0;
}

读入、矩阵制作、输出三个步骤全部放在 main() 函数中显得有些冗长。我们可以根据功能将上述代码分成三个模块,每个模块写在一个单独的子函数里:

int n;
int a[100][100];

void input()
{
    scanf("%d", &n);
}

void matrix_generation()
{
    for (int k = 1; k <= (n + 1) / 2; k++)
        for (int i = k; i <= n + 1 - k; i++)
            for (int j = k; j <= n + 1 - k; j++)
                a[i][j] = k;
}

void print()
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
            printf("%4d", a[i][j]);
        puts("");
    }
}

int main ()
{
    input();
    matrix_generation();
    print();
    return 0;
}

由于这个程序比较简单,你可能会觉得这样写代码非常拖沓。但随着程序结构的进一步复杂化,你一定会发现将程序分功能分模块书写能帮助你理清思路,也方便 debug。

代码风格

对于初学者来说,养成良好的代码风格极其重要。一方面,条理清晰的代码看起来赏心悦目,方便自己 debug;另一方面,如果你将来与别人合作开发项目,良好的代码风格可以让他人快速理解你书写的模块,方便沟通交流。大家或许听说过一些类似于 “老程序员一走,整个项目就玩不转了,因为没人知道他写了什么” 的笑话,撇开工程复杂性的客观原因,这样的现象也说明很大一部分程序员的代码书写习惯极其糟糕。

所以无论你是否有编程基础,请一定重视自己的代码风格。我们建议你至少在以下几个方面注意:

  • 合理的变量命名:不要使用无意义的 a b c _ __,而应该使用 arrayLength stuCount 等。有意义的变量名可以让你/他人立刻理解这个变量的用途。至于变量的命名格式,我们建议你搜索驼峰命名法。
  • 合理的函数使用:不要将一大堆代码塞在一起。
  • 合理书写注释,对程序的功能做一点解释。
  • 合理的符号使用 (括号,换行符 etc.)
  • 合理的缩进
  • ……

如果你感兴趣,你可以上网搜索 Google 的代码风格规范。

最后是一个小笑话:

世界上有两件最讨厌的事情,一个是写注释,一个是别人的代码不写注释。

定义

函数的基本语法如下:

int func(int a, float b, char c, ...)
{
}

其中

  • 第一个 int 指明了函数的返回值类型为 int。你当然可以选择别的类型,如果你的函数不需要返回值,可以用 void
  • func 是函数的名称。这是你可以自由指定的部分。
  • (int a, float b, ...) 是函数的参数。你可以指定任意个数的参数,但每个参数都要有类型。

下面是一个简明的例子:

int abs(int x) {
    int res = x >= 0 ? x : -x;
    return res;
}

int main ()
{
    int x = -1;
    x_abs = abs(x); // x_abs = 1
    return 0;
}

注意所有的子函数要写在 main 函数的上方 (这是 C/C++ 的要求)。如果你想要将子函数写在 main 函数的下方,需要在 main 函数之前先声明该子函数。声明简单来说就是将函数的定义抄一遍:

int abs(int x);

int main ()
{
    int x = -1;
    x_abs = abs(x);
    return 0;
}

int abs(int x) {
    int res = x >= 0 ? x : -x;
    return res;
}

变量的作用域

有了函数的概念后我们就必须要区分“全局变量“和“局部变量”。顾名思义在函数内部定义的变量是局部变量,该变量只能在当前函数内使用。在所有函数外定义的变量是全局变量,该变量可以在任何函数中使用 (注:一个函数的参数也是该函数的局部变量)。下面是一个例子:


int n = 0;

void func()
{
    printf("%d\n", n); // 输出结果为 0。
    printf("%d\n", m); // 编译错误:m 是 main() 的局部变量,在 func() 中不可用。
}

int main ()
{
    int n = 1, m = 2;
    printf("%d\n", n); // 输出结果为 1,全局变量和局部变量重名优先使用局部变量。
}

按值传递和按引用传递

按值传递和按引用传递是函数中必须提及的另一个重要概念。很多初学者会疑惑:如果我在子函数中对参数的值做了改动,主函数里相应的变量会变吗?你可以做一个小实验探索这一点:

void func(int x) { x = 2; }
int main ()
{
    int x = 1;
    func(x);
    printf("%d\n", x); // 输出结果为 1
}

事实证明子函数的改动不会影响主函数。这是因为 C/C++ 中函数变量参数的传递默认使用按值传递,即子函数的参数变量 x 和主函数的变量 x 是两个独立的主体,在函数调用时主函数的 x 的值会被复制到子函数的 x 中,在子函数中对 x 的所有改动都是针对这个“复印件”的,对原件没有影响。

那么有没有办法可以让子函数直接修改主函数中的变量的值呢? C++ 提供了引用变量的机制,一个例子如下:

void func(int x, int &y) { x++; y++; }
int main ()
{
    int x = 1, y = 1;
    func(x, y);
    printf("%d %d\n", x, y); // 输出结果为 1 2
}

在这个例子中,x 是按值传递的,y按引用传递的 (注意函数参数前的 & 符号)。按引用传递可以理解为直接将原件交给了子函数,子函数的改动会影响主函数的值。

Previous
Next