Coding Style Guide for C++ Beginners

担任程序设计基础课的助教,并帮助刚刚接触编程的同学解决入门问题令我回想起我入门编程的那段时光。虽然当时的我对程序语言还一知半解,但总对自己代码的美观程度要求颇高,往往会在完成功能之后花相当长的时间精简代码,用更简洁的语句表达原本的功能,并仔细的确认代码风格是否美观。担任助教以来,我发现许多同学对代码的美观程度并不是很重视,而混乱的代码也对他们的调试带来了困难。因此,我便简单写下这篇短文帮助刚入门的初学者快速对代码风格的概念有一个了解。

事实上,保持代码的“美观程度”有许多好处:

  1. 提高代码的可读性。这个读代码的人可能是与你合作开发的人,或者更有可能的是若干年以后的你自己。

  2. 在编写代码的过程中保持良好的代码风格,可以减少许多错误的风险。例如,保持大括号处在正确的位置上能够减少漏写大括号的情况。

  3. 便于调试。程序出错的时候整洁的代码使你更容易找到出错的位置。

  4. 助教批改你的编程作业时会更开心,并且更有可能一不小心给你打一个更高的分数。

几乎每个需要大规模编程开发的公司都会制定一套自己的编程规范,即对代码风格的标准化要求,如Google,Amazon等等。这些规范都十分详尽,对于入门者很不友好。本文则会站在入门的角度提供一点指导,其中一部分内容出自Google的C++编程规范。对于已经入门的读者,则可以深入现有的详尽的编程规范。

缩进

代码要根据层次结构缩进。例如,函数体中的语句要比函数定义多缩进一层。

#include <iostream>

using namespace std;

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    if (a == b + c) {
        cout << "a == b + c" << endl;
    } else {
        cout << "a != b + c" << endl;
    }
    return 0;
}

至于“一层缩进”具体应该是多少呢?比较常见是一个Tab,也可以是2个/4个空格。对于IDE或用于编程的文本编辑器,几乎都拥有设置Tab长度(记事本中一个Tab是8个字符长,编程时往往设置成2个/4个字符长)、软Tab(敲击Tab时输入相应个数的空格)、自动缩进、全文Tab/空格转换等等。无论你使用哪种,请注意在一个项目中不要混用。

如果你拿不定注意用哪种缩进,可以先尝试4个空格。

大括号

其中一种主张左大括号之前不换行,右大括号与左大括号所在的行的开头对齐。这种方式的好处是节省空间。Java的程序几乎都使用这种规则。

#include <iostream>

using namespace std;

int main() {
    int n, a, b;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> a >> b;
        if (a == b)
            cout "a and b are identical!" << endl;
        else
            cout "a and b are not identical!" << endl;
    }
    return 0;
}

另一种主张是左大括号也另起一行,即左右大括号各自单独占一行对齐。这种风格的好处是层次感比较清晰:

#include <iostream>

using namespace std;

int main()
{
    int a, b, c;
    cin >> a >> b >> c;
    if (a == b + c)
    {
        cout << "a == b + c" << endl;
    }
    else
    {
        cout << "a != b + c" << endl;
    }
    return 0;
}

在具体的代码规范中,可能还有处理不同情况的特例,但是对于初学者而言,从上述两种风格中选一种看的顺眼的并坚持使用就足够了。另外,一个建议是所有的if/else/for/while后面总是使用大括号,这样可以减少出错的风险。

变量名

函数命名,变量命名,文件命名要有描述性;少用缩写。 变量名建议使用小写,单词间用下划线分开。

int price_count_reader;    // 无缩写
int num_errors;            // “num” 本来就很常见
int num_dns_connections;   // 人人都知道 “DNS” 是啥

类名每个单词首字母大写,例如:

class BinarySearchTree;
class HashTable;
class Student;

函数名建议使用小写字母,单词间使用下划线分割。 对于小型程序,使用一个字母的变量名有时还可以接受,如果开发大型项目,规范使用变量名则会非常重要。另外,不同的编程语言有不同的命名习惯,如果要学习其他编程语言,建议也简单了解一下对应的命名规范。

空行与空格的使用

本节部分修改自这篇文章

空行可以在代码同一个函数中的语句有语义分别的时候插入,这样可以提高程序的可读性。 不同函数定义之间也应插入一个空行。

空格应只在恰当的时候使用,行尾不应出现空格。许多编辑器提供自动删除行尾空格的功能。

下面使用一段程序示范空格的使用。

int i = 0;  /* 在结尾分号前不要留空格 */
int a, b;   /* 逗号后面总是添加空格 */

void f(bool b) {        /* 在不换行的左大括号左侧,应该留有一个空格 */
    if (b) {            /* 在条件语句或循环语句关键字后要留有一个空格 */
        // do something.
    } else {            /* else关键字两侧要各留一个空格 */
        // do something.
    }
    while (test) {      /* 通常情况下,在条件语句或循环语句的条件体的括号内两侧,不留空格 */
        // do something.
    }
    for (int i = 0; i < 5; ++i) {
        // do something.
    }
    switch (i) {
        case 1:         /* 在case语句中,冒号前不要留空 */
            // do something.
        case 2: break;  /* case语句中,如果代码和case关键字在同一行,请在冒号后面留一个空格 */
    }

    ch = getchar();     /* 函数与后面的括号之间不应该有空格 */
    x = 0;              /* 赋值操作符(=)的两侧要各留一个空格 */
    x = -5;             /* 单目操作符要和其作用对象紧连在一起,不要留有空格 */
    ++x;

    v = w * x + y / z;  /* 双目操作符的两侧往往各留一个空格 */
    v = w*x + y/z;      /* 不留空格的情况很少见,不过如果你选择这种格式,要保持整个代码格式的一致 */
    v = w * (x + z);    /* 括号内两侧不要留有空格 */
    if (x && !y)
        // do something.
}

其他

编程中还有一些其他需要注意的地方,但是对于仅编写小程序的初学者,这些要求可以放松一些:

写在最后

恭喜你坚持读到了最后!标准的编码有助于编程的学习,不过代码风格与编程规范有许多种不同的要求,对于初学者不应该过多地被标准束缚。事实上,程序员之间有许多圣战,比如左大括号之前换不换行,用space还是Tab缩进等等,甚至还有关于这个现象的漫画哦(这里还有这里)。所以大家还是不要忘了学习编程的初衷是为了让计算机帮助我们工作,而不要被规范过分限制住手脚。希望这篇文章能对初学者有一些帮助,如果对文章有建议,可以联系我修改补充。

解铮 于 2017年3月22日