C 编程/结构和风格
本文是对 C 编程语言中良好编码风格的基本介绍。它旨在提供有关如何有效地使用缩进、注释和其他元素的信息,这些元素将使您的 C 代码更具可读性。它不是关于实际 C 编程的教程。
作为一名初学者,在程序代码中创建结构的意义可能并不明显,因为编译器并不关心其中的差异。但是,随着程序变得复杂,编写程序很可能已经成为一项联合工作。(或者其他人可能想看看它是如何完成的。或者您可能需要在几年后再次阅读它。)编写良好的代码还有助于您概述代码的功能。
在以下部分中,我们将尝试解释良好的编程实践,这些实践将使您的程序更清晰。
在 C 语言中,程序由语句组成。语句以分号结尾,并收集在称为函数的部分中。按照惯例,语句应保留在自己的行上,如下例所示
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}
以下代码块本质上是相同的。虽然它包含完全相同的代码,并且将以相同的结果进行编译和执行,但删除空格会导致一个本质的区别:它更难阅读。
#include <stdio.h>
int main(void) {printf("Hello, World!\n");return 0;}
简单地使用缩进和换行可以极大地提高代码的可读性,而不会影响代码性能。可读的代码使更容易查看函数和过程的结束位置,以及哪些行属于哪些循环和过程。
本课将重点介绍如何改进一个示例代码段的编码风格,该代码段应用公式并打印结果。稍后,您将看到如何更详细地编写此类任务的代码。现在,重点关注代码的外观,而不是它的功能。
在代码中添加空白无疑是良好代码结构中最重要的部分。有效地使用空白可以创建代码流的视觉尺度,这在您想要维护代码时返回到代码时非常重要。
请注意行号的使用。它们不是实际代码的一部分。它们仅用于参考。 |
如果换行最少,代码几乎无法让人阅读,并且可能难以调试或理解
#include <stdio.h>
int main(void) { int revenue = 80; int cost = 50; int roi; roi = (100 * (revenue - cost)) / cost; if (roi >= 0) { printf ("%d\n", roi); } return 0; }
与其将所有内容放在一行上,不如将长行分解,以便每个语句和声明都放在自己的行上。插入换行符后,代码将如下所示
#include <stdio.h>
int main(void) {
int revenue = 80;
int cost = 50;
int roi;
roi = (100 * (revenue - cost)) / cost;
if (roi >= 0) {
printf ("%d\n", roi);
}
return 0;
}
空行应用于偏移代码的主要组件。始终使用它们
- 在预处理器指令之后。
- 在声明新变量之后。
- 根据您自己的判断,查找其他需要分隔组件的位置。
根据这两条规则,现在应该至少添加了两次换行。
- 在第 1 行之后,因为第 1 行包含预处理器指令。
- 在第 5 行之后,因为第 5 行包含变量声明。
这将使代码比以前更具可读性
以下代码行在函数之间有换行符,但没有缩进。
#include <stdio.h>
int main(void) {
int revenue = 80;
int cost = 50;
int roi;
roi = (100 * (revenue - cost)) / cost;
if (roi >= 0) {
printf ("%d\n", roi);
}
return 0;
}
但它仍然不如它可以的那样易读。
虽然在代码的关键块之间添加简单的换行符可以使代码更易于阅读,但它没有提供有关程序块结构的信息。使用 Tab 键可能非常有用。缩进通过将执行路径的起始点移动到新列来视觉上分隔它们。这种简单的做法将使代码更容易阅读和理解。缩进遵循一个相当简单的规则
- 新块内的所有代码都应比前一条路径中的代码缩进一个 Tab[1]。
基于上一节中的代码,有两个块需要缩进
- 第 4 行到第 16 行
- 第 13 行
#include <stdio.h>
int main(void) {
int revenue = 80;
int cost = 50;
int roi;
roi = (100 * (revenue - cost)) / cost;
if (roi >= 0) {
printf ("%d\n", roi);
}
return 0;
}
现在很明显程序的哪些部分适合哪些块。您可以分辨出程序员打算哪些部分是条件性的,哪些部分不是。虽然可能不会立即注意到,但一旦许多嵌套路径添加到程序的结构中,缩进的使用就变得非常重要。因此,缩进使程序的结构清晰。
据称,研究表明,2 到 4 个字符的缩进大小比 8 个字符的缩进更容易阅读[2]。但是,某些系统可能仍在使用 8 个字符的缩进[3]。
代码中的注释可用于各种目的。它们提供了最简单的方法来设置代码的特定部分(及其用途);以及在代码的不同部分之间提供视觉“分割”。在整个代码中使用良好的注释将使您更容易记住代码的特定部分的功能。
现代版本的 C(以及许多其他语言)中的注释可以有两种形式
// Single Line Comments (added by C99 standard, famously known as c++ style of comments)
和
/* Multi-Line
Comments
(only form of comments supported by C89 standard)*/
请注意,单行注释是 C 语言中较新的添加功能,因此某些编译器可能不支持它们。最新版本的 GCC 将能够毫无问题地支持它们。
本节将重点介绍每种注释形式的各种用途。
单行注释最适合用于简单的“旁注”,解释代码的某些部分的功能。放置这些注释的最佳位置是在变量声明旁边,以及在可能需要解释的代码段旁边。注释应明确对应代码背后的意图和想法。从阅读代码中立即显而易见的内容不应放在注释中。
根据我们之前的程序,有一些很好的位置可以放置注释
- 第 5 行和/或第 6 行,解释“int revenue”和“int cost”分别代表什么,
- 第 8 行,解释变量“roi”将用于什么,
- 第 10 行,解释计算的想法,
- 第 12 行,解释“if”的目的。
这将使我们的程序看起来像
#include <stdio.h>
int main(void) {
int revenue = 80; // as of 2016
int cost = 50;
int roi; // return on investment in percent
roi = (100 * (revenue - cost)) / cost; // formula from accounting book
if (roi >= 0) { // we don't care about negative roi
printf ("%d\n", roi);
}
return 0;
}
多行注释最适合用于对代码进行长篇解释。它们可用作版权/许可声明,也可用于解释代码块的目的。这有两个好处:它们使您的函数更容易理解,并且使更容易发现代码中的错误。如果您知道某个代码块的预期功能,那么如果发生错误,就更容易找到负责的代码段。
例如,假设我们有一个程序,其设计目的是打印“Hello, World!” 一定数量的行,并且重复指定的次数。该程序中将包含许多 for 循环。在本例中,我们将行数称为 i,每行字符串的数量称为 j。
描述 'for' 循环 i 目的的多行注释的一个很好的例子是
/* For Loop (int i)
Loops the following procedure i times (for number of lines). Performs 'for' loop j on each loop,
and prints a new line at end of each loop.
*/
这很好地解释了 i 的用途,同时没有详细说明 j 的作用。通过详细说明特定路径的作用(而不是其内部的路径),将更容易对路径进行故障排除。
类似地,您应该始终在每个函数之前包含多行注释,以解释每个函数的作用、先决条件和后置条件。始终将技术细节留给程序内部的各个代码块 - 这使得故障排除更容易。
函数描述符应该类似于
/* Function : int hworld (int i,int j)
Input : int i (Number of lines), int j (Number of instances per line)
Output : 0 (on success)
Procedure: Prints "Hello, World!" j times, and a new line to standard output over i lines.
*/
此系统允许快速了解函数应该做什么。然后,您可以在程序的后面详细说明程序每个方面的实现方式。
最后,如果您喜欢美观的源代码,多行注释系统可以轻松添加注释框。与其他方式相比,这使得注释更加突出。它们看起来像这样
/***************************************
* This is a multi line comment
* That is nearly surrounded by a
* Cool, starry border!
***************************************/
应用于我们的原始程序,我们现在可以包含更具描述性和可读性的源代码
#include <stdio.h>
int main(void) {
/************************************************************************************
* Function: int main(void)
* Input : none
* Output : Returns 0 on success
* Procedure: Prints 2016's return on investment in percent if it is not negative.
************************************************************************************/
int revenue = 80; // as of 2016
int cost = 50;
int roi; // return on investment in percent
roi = (100 * (revenue - cost)) / cost; // formula from accounting book
if (roi >= 0) { // we don't care about negative roi
printf ("%d\n", roi);
}
return 0;
}
这将允许程序的任何外部用户轻松理解代码的功能及其操作方式。它还可以避免与其他同名函数产生混淆。
一些程序员在代码块注释的右侧添加一列星号
/***************************************
* This is a multi line comment *
* that is completely surrounded by a *
* cool, starry border! *
***************************************/
但大多数程序员不会在代码块注释的右侧添加任何星号。他们认为对齐右侧是浪费时间。
源文件中的注释可以通过使用 Doxygen 等流行工具自动用于记录源代码。[4][5]
- ↑ 一些程序员建议“使用空格进行缩进。不要在代码中使用制表符。您应该将编辑器设置为在您按下 Tab 键时输出空格。” [1] [2] 其他程序员则不同意。 [3] [4] 无论您是偏好空格还是制表符,请确保在您正在处理的项目中保持一致。混合使用制表符和空格会导致代码难以阅读。
- ↑ http://www.oualline.com/vim/vim-cook.html#drawing Vim 食谱
- ↑ https://www.kernel.org/doc/html/latest/process/coding-style.html Linux 内核编码风格
- ↑ "C++ 和 Java 的编码规范"“本文档中说明的所有代码块注释在代码块注释的右侧都没有漂亮的星号。做出这个有意的选择是因为对齐这些漂亮的星号会浪费大量时间,并且会阻碍内联注释的维护。”
- ↑ c2:BigBlocksOfAsterisks,"代码工艺" Pete Goodliffe 著,第 82 页,Falvotech“C 编程风格指南”,Fedora 目录服务器编码风格
- 阿拉丁的 C 编码指南 - 一个更权威的 C 编码指南。
- C/C++ 编程风格 GNU 编码风格和 Linux 内核编码风格