仅作为本人学习《深入 C 语言和程序运行原理》的学习笔记,原课程链接:极客时间《深入 C 语言和程序运行原理》——于航
代码规范的意义:
和一个人参与项目、写代码时的 “单打独斗” 相比,多人协作从理论上来看可以大幅提高生产效率。但现实情况却可能是,效率在提升的同时,代码质量下降、沟通成本变高等一些列问题也随之而来。甚至在某些情况下,团队人数的增加反而会导致项目推进效率的降低。这是因为工程师没有代码编写规范,这就会导致频繁返工甚至妥协,以及协作流程上的不明确与延期,使得项目的迭代周期变长,进而生产效率下降。因此,如何为团队制定统一的编码规范,并明确项目开发生命周期的整体流程以及其中各节点的重点注意事项,就成为了决定团队协作效率的一个重要因素。
——原文
GNU 与 GUN?傻傻分不清楚
大家对 GNU 这个标识应该不陌生,千万不要将它与 “文明用语” GUN 搞混。
GNU是一个自由的操作系统,其内容软件完全以GPL方式发布。这个操作系统是GNU计划的主要目标,名称来自GNU’s Not Unix!的递归缩写,因为GNU的设计类似Unix,但它不包含具著作权的Unix代码。GNU的创始人,理查德·马修·斯托曼,将GNU视为“达成社会目的技术方法”。 作为操作系统,GNU的发展仍未完成,其中最大的问题是具有完备功能的内核尚未被开发成功。GNU的内核,称为Hurd,是自由软件基金会发展的重点,但是其发展尚未成熟。在实际使用上,多半使用Linux内核、FreeBSD等替代方案,作为系统核心,其中主要的操作系统是Linux的发行版。Linux操作系统包涵了Linux内核与其他自由软件项目中的GNU组件和软件,可以被称为GNU/Linux(见GNU/Linux命名争议)。
——百度百科
目前(2022年1月),一共有 384 个 GNU 软件包被托管到 GNU 的官方开发站点上。其中,GCC 和 GDB 的第十二个大版本也在处于紧张的开发过程中。
GCC 作为 GNU 旗下使用人数最多的开源项目之一,它能够一直以来被社区广泛采纳,除了源于自身极高的项目质量外,也离不开贡献者之间默契的合作,其中就包括在编码规范上的统一。
GNU C 编码规范
GNU 编码规范将 C 语言项目需要考虑的编码细则分为了多个不同类别,下面是常用的 9 个方面。
格式
该类别中约束了 C 代码的具体编写格式。比如
- GNU 规范建议将每一行代码的字符数量保持在 79 个以内,以便在大多数情况下获得最好的代码可读性;
- C 函数定义时使用的开始花括号 “{” 建议作为每行的第一个字符,这样有助于兼容大多数代码分析工具;
- 为了更好地识别函数定义,函数名称同样需要放置于每行的起始位置。
- 函数定义或声明中,若函数参数过长,则需要将超过行长度限制的参数放到函数名所在的下一行,并与上一行中第一个参数的开头对齐。
- 对于结构体或枚举类型,可以选择在符合长度限制的情况下,将它们在一行内完整定义,或采用类似函数的定义方式,将包裹定义的开始与结束括号放置在单独一行,两行之间为具体的定义内容。
比如下面这个例子(摘抄自原文)
int lot_of_args(int an_interger, long a_long, short a_short,
double a_double, float a_float)
{
}
struct foo
{
int a, b;
};
struct bar { int x, y;};
上面只提到了一些重要的代码格式化规则,此外还有很多其他 GNU 代码格式的规范,这些内容可以参考 GNU 旗下的 indent 工具。
注释
在文件方面,GNU 规范建议程序 main 函数所在源文件应以描述程序基本用途的注释作为开头,而其他源文件则以文件名和描述该源文件基本功能的注释作为开头。 对于函数,则需要为其添加用于描述函数基本功能、参数类型、参数用途、参数可能取值,以及返回值含义(如果有)等内容的注释信息。对于每一个全局和静态变量,也应该为其添加相应的注释,来描述它们的基本用途和可能取值。除基本 C 语法代码外,诸如预处理指令 #else 与 #endif 也需跟随有相应的注释,以标注相应分支的位置和含义。 一般情况下,为保证国际化和通用性,应以英文形式撰写注释。若代码和注释在同一行,则注释应起始于该行代码最后一个字符后间隔两个空格的文章。注释应使用完整的句子,并在一般情况下保持首字母大写。最后需要注意的是,若在注释中谈论到了变量的值,则应该将该变量对应标识符完全大写。比如在注释文本 “the inode number NODE_NUM” 中提到的 NODE_NUM,便代表 C 代码中变量 node_num 对应的具体值。
语法约定
下面是原文列举的一些基本习惯的例子:
- 显式地为所有使用到的值标注类型,尤其是直接使用在表达式中的数字字面值;
- 选择性地为编译器添加 “-Wall” 参数,并根据警告信息来优化代码;
- 选择性地看待静态代码分析工具给出的建议,并在不影响程序正常功能的情况下,不要为了满足它们的要求而牺牲代码可读性;
- 外部函数和后续才会使用函数的声明,应该被放置在当前源文件开头处统一处理的地方,或选择放到单独的头文件中;
- 使用名字具有一定意义的不同变量而完成不同任务,且尽量使用变量的声明处在恰好可以完成其 “任务” 的最小作用域中。除此以外,也不要声明会覆盖全局变量的局部变量。
代码写法上,GNU 代码规范也给出了规定,比如:(来自原文)
- 不要将通过一个类型关键字声明的多个变量分散在多行;
- 可以将同类型的多个变量放在同一行声明,或选择在不同行使用独立的声明语句来声明多个变量;
- 当使用嵌套的流程控制语句(比如 if 语句)时,总是将内部的嵌套逻辑包裹在大括号中,以保证代码清晰可读;
- 尽量避免在 if 语句的条件判断处,也就是小括号内部作赋值操作(而在 while 语句中是可以的)。
命名
C 语言项目中,程序内使用到的全局变量和函数,它们的名称有时可以直接替代注释来说明它们的功能。因此,需要确保对于它们的命名是具有一定意义的。相对的,局部变量由于只作用在一个较短的上下文中,它的名字可以更短,只要能大概描述它的作用即可。 变量名一般采用下划线命名法,即通过下划线将其中不同的单词进行分割。变量名中可以使用缩写,但需要确保这些缩写是已经被人们熟知的,而不会产生任何歧义。并且,尽量仅使用小写字母、数字和下划线来组成变量名,而把大写字母留给对宏变量以及枚举常量命名。 另外,当需要定义数字常量值时,请优先使用枚举常量而非宏常量,这将有利于调试器对程序执行逻辑的分析。比如下面这段代码(摘自原文):
enum Setting {
LIMITATION = 1000
};
const char * author_name = "Jason";
const int author_age = 28;
系统可移植性
GNU 规范中的 “系统可移植性” 主要指将 C 应用移植到不同 Unix 版本系统上的能力。这种可移植性是可取但并非非常重要的。
CPU 可移植性
不同 CPU 之间可能存在字节对齐、字节序(大小端)等差异,所以在编码时不能假设一个 int 类型变量的起始地址为最低有效位(LSB)。同样地,使用 char 类型时,应显式指出其符号性。
对于基本类型大小,GNU 标准不会处理 int 类型小于 32 位的情况。同样,也不会考虑 long 类型小于指针类型和 size_t 类型的情况。
系统函数
C 程序可以通过 C 标准库或者 POSIX 库提供的函数,来使用相应的系统功能,但这些库函数在其他系统上可能存在可移植性问题。但是借助 Gnulib,我们可以在一定程度上解决这个问题。 Gnulib 是 GNU 旗下的可移植性库,它为许多缺乏标准接口的系统提供了对标准接口的实现,其中包括对增强型 GNU 接口的可移植性实现等。
国际化
GNU 规范考虑到了 C 程序国际化问题,GNU 提供了名为 GNU gettext 的库,可以轻松地将程序中的消息翻译成各种语言。
字符集
在 GNU 源代码注释、文本文档,以及其他相关上下文中,请首选使用 ASCII 字符集中的字符。如果需要使用非 ASCII 字符,我们要确保文档的编码方式保持统一。通常来说,UTF-8 应是首选的编码方式。
|