前言

  这篇文章可以看作对我之前写的《浅读C语言代码规范》的一些补充,但是本篇内容仅个人看法,具体是否采用还需读者自己评估。

  该文内容不定时补充,为了看过旧版内容的读者更方便,新的内容会放在前面。


优先使用库函数

  当一个功能库函数提供了实现时应当优先使用库中的实现,比如求字符串长度时应当优先使用strlen函数而非自行编写。

  例如下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <bits/stdc++.h>

using namespace std;

int main() {
const int N = INT_MAX >> 2;
char* str = new char[N];
// 填充一个足够长的字符串
memset(str, 5, sizeof(char) * N);
str[N - 1] = '\0';
auto t1 = clock();
// 自己编写计算字符串长度的代码 代码段 1
int len0 = 0;
while (str[len0] != '\0') ++len0;
auto t2 = clock();
// 调用库计算字符串长度 代码段 2
auto len1 = strlen(str);
auto t3 = clock();
cout << (t2 - t1) << endl;
cout << (t3 - t2) << endl;
cout << len0 << endl << len1;
return 0;
}

  在开启O2优化后代码段1的耗时稳定在150ms附近,代码段2的时间耗时则稳定在38ms,可见调用库比自行编写快了将近4倍。

  看不懂代码没关系,我主要是想告诉读者,自己编写的代码经常不如库中提供的实现效率更高(除非数据比较特殊)。

不通过末尾循环体更新外界变量

  for循环的格式为:for (单次表达式; 条件表达式; 末尾循环体)

  尽量不要通过末尾循环体更新外界变量,例如:

1
2
3
4
5
6
7
int main() {
int length = 0;
char str[1000] = ...;
for (; str[length] != '\0'; ++length) {}
printf("%d", length);
return 0;
}

  请不要使用这种方式计算运算结果,尽量把更新语句放置在循环体中,例如:

1
2
3
4
5
6
7
8
9
int main() {
int length = 0;
char str[1000] = ...;
while (str[length] != '\0') {
++length;
}
printf("%d", length);
return 0;
}

  因为一个循环的主要代码一般都放置在循环体中,放置在末尾循环体更不容易让他人理解你的意图。

不要在控制语句中赋值

  C语言是支持在控制语句中赋值的,例如:

1
2
3
4
5
6
7
8
int main() {
int a;
int b = ...;
if (a = b) {
...
}
return 0;
}

  虽然可以这样写,但是请不要这么干,这会让别人无法准确区分你到底是写错了还是故意要赋值的,自己以后再看自己的代码也容易混淆。

使用大括号表明空语句

  如果你需要编写一个空的whileif一类的语句,请使用大括号而非分号。例如:

1
2
3
4
5
6
7
8
int main() {
if (...) {
...
} else if (...); // 请不要这样写
else if (...) { // 这样写
} else {} // 放在一行内也可以,大括号中间有无空格均可
return 0;
}

  这样做的目的是为了避免自己手误多写了一个分号上去导致程序运行结果错误,便于后续检查以及他人阅读。

变量集中声明的注意事项

  如果使用变量集中声明的声明方式,请一定注意以下内容:

  1. 严格遵守命名规则,否则变量会一团糟
  2. 要么统一都赋初值,要么统一不赋初值,避免阅读代码时还需要来回折跃查看变量是否初始化
  3. 推荐使用就近原则(说到底还是觉得这方法不好用)

避免goto语句

  gotoC/C++中一个强大的功能,但是由于其功能过于强大,容易引发各种各样的问题,且会导致程序的控制流难以追踪,并不推荐使用。绝大多数情况下,任何能用其它语句代替的goto都不应当使用goto编写,除非是使用goto来跳出外部循环。

善用快捷键

  善用系统和IDE的快捷键可以让我们的编码速度更快,也可以更便捷地进行调试等操作。如果是JBS平台的用户,可以安装Key Promoter X这个插件,在你用鼠标进行操作时会提醒你对应的快捷键是什么,如果你频繁进行某项操作但其没有快捷键,它也会提醒你可以自己设置一个快捷键。

变量声明位置

  在C99(不了解C90、C99、C11是什么的话自行百度一下)之前,C语言要求函数内的变量必须在函数头声明。这一方面虽然可以通过函数头的变量说明函数的大致内容,但是也造成了一些麻烦:

  • 编码时经常需要从当前编辑的地方回到函数头补上需要用到的变量声明
  • 容易忘记赋初值
  • 某些变量只需用到一次,写在开头反而更乱
  • 提前声明变量可能带来额外的性能损耗
  • 提前声明变量无法控制变量的作用域

  自C99开始,C语言取消了这一限制,变量的声明可以写在函数中的任意位置(前提是符合语法)。个人更倾向于就近原则,即使用时再声明。

  如果读者想要使用前一种方式的话,我也推荐将临时用到的变量声明(比如for循环中用到的控制变量)在使用的位置,这样子可以让代码更直观。

使用布尔类型

  C语言中经常使用int来表示真假。比如下面的代码:

1
2
3
4
5
6
7
8
9
int main() {
int k = 0;
if (k) {
printf("true");
} else {
printf("false");
}
return 0;
}

  上面的代码完全可以正常运行,但是这实际上是一种非常麻烦并且容易出错的写法。首先,编码的人和阅读的人都需要思考,到底01哪一个代表true。其次,在复杂一些的环境中,这种写法会令人难以快速排出错误。

  如何避免上述的问题?其实很简单,使用C语言的库中提供的_Bool即可,该类型只有truefalse两个值,并且英文单词比单个数字更加直观易懂。如果嫌_Bool打起来麻烦的话,可以使用stdbool.h头文件中定义的bool

统一代码风格

  如果你的代码中使用了某一种代码风格,那么,请在程序中保持这个风格,不同风格的代码混杂在一起相当于没有规范的代码。

读懂编译报错

  如果代码存在编译错误,编译器会给出错误信息,这些信息通常是英文,如果你看不懂的话不妨把信息复制到网络上搜索一下,很容易就能找到问题原因和解决方法。

  这里强调一点,编译错误是编码过程中最容易解决的错误(除了IDE判断错误导致报错位置或信息不对的情况),遇到编译错误一定要自己想办法解决。