xref: /DragonOS/docs/community/code_contribution/c-coding-style.md (revision 597ecc08c2444dcc8f527eb021932718b69c9cc5)
1# C语言代码风格
2
3  这份文档将会简要的介绍DragonOS的C语言代码风格。每个人的代码风格都各不相同,这是一件非常正常的事情。但是,对于一个开源项目的可维护性而言,我们希望制定一些代码规范,以便包括您在内的每个开发者都能在看代码的时候更加舒服。一个充斥着各种不同代码风格的项目,是难以维护的。
4
5  我们在这里提出一些建议,希望您能够尽量遵循这些建议。这些建议与Linux的代码规范相似,但又略有不同。在变量命名上,DragonOS采用Linux的风格;对于缩进,DragonOS采用Microsoft风格。
6
7
8
9## 0. 代码格式化工具
10
11  在提出下面的建议之前,我们建议您在开发的时候使用Visual Studio Code的`C/C++ Extension Pack`插件作为代码格式化工具。这些插件能为您提供较好自动格式化功能,使得您的代码的基本格式符合DragonOS的要求。
12
13  当您在编码时,经常性的按下`Ctrl+shift+I`或您设置的代码格式化快捷键,能帮助您始终保持良好的代码格式。
14
15## 1. 缩进
16
17  一个制表符的宽度等于4个空格。代码的缩进是按照制表符宽度(在多数编辑器上为4个字符)进行缩进的。
18
19  这样能够使得您的代码变得更加容易阅读,也能更好的看出代码的控制结构。这样能避免很多不必要的麻烦!
20
21举个例子:在switch语句中,将switch和case放置在同一缩进级别。并且将每个case的代码往右推进一个tab。这样能让代码可读性变得更好。
22
23```c
24switch (cmd)
25{
26case AHCI_CMD_READ_DMA_EXT:
27    pack->blk_pak.end_handler = NULL;
28    pack->blk_pak.cmd = AHCI_CMD_READ_DMA_EXT;
29    break;
30case AHCI_CMD_WRITE_DMA_EXT:
31    pack->blk_pak.end_handler = NULL;
32    pack->blk_pak.cmd = AHCI_CMD_WRITE_DMA_EXT;
33    break;
34default:
35    pack->blk_pak.end_handler = NULL;
36    pack->blk_pak.cmd = cmd;
37    break;
38}
39```
40
41## 2. 分行
42
43  我们建议,每行不要超过120个字符。如果超过了,除非有必要的理由,否则应当将其分为两行。
44
45  在分行时,我们需要从被分出来的第二行开始,比第一行的起始部分向右进行一个缩进,以表明这是一个子行。使用代码格式化的快捷键能让你快速完成这件事。
46
47  对于一些日志字符串而言,为了能方便的检索到他们,我们不建议对其进行分行。
48
49  对于代码的分行,请不要试图通过以下的方式将几个语句放置在同一行中,这样对于代码可读性没有任何好处:
50
51```c
52// 错误示范(1)
53if(a) return 1;
54
55// 错误示范(2)
56if(b)
57    do_a(),do_b();
58```
59
60## 3. 大括号和空格
61
62### 3.1 大括号
63
64  大括号的放置位置的选择是因人而异的,主要是习惯原因,而不是技术原因。我们推荐将开始括号和结束括号都放置在一个新的行首。如下所示:
65
66
67```c
68while(i<10)
69{
70    ++i;
71}
72```
73
74&emsp;&emsp;这种规定适用于所有的代码块。
75
76&emsp;&emsp;这么选择的原因是,在一些编辑器上,这样放置括号,**编辑器上将会出现辅助的半透明竖线,且竖线两端均为括号**。这样能帮助开发者更好的定位代码块的层次关系。
77
78下面通过一些例子来演示:
79
80&emsp;&emsp;在下面这个代码块中,我们需要注意的是,`else if`语句需要另起一行,而不是跟在上一个`}`后方。这是因为我们规定`{`必须在每行的起始位置,并且还要保持缩进级别的缘故。
81
82```c
83if (*fmt == '*')
84{
85    ++fmt;
86}
87else if (is_digit(*fmt))
88{
89    field_width = skip_and_atoi(&fmt);
90}
91```
92
93&emsp;&emsp;当循环中有多个简单的语句的时候,需要使用大括号。
94
95```c
96while (condition)
97{
98    if (test)
99        do_something();
100}
101```
102
103&emsp;&emsp;当语句只有1个简单的子句时,我们不必使用大括号。
104
105```c
106if(a)
107    return 1;
108```
109
110### 3.2 空格
111
112&emsp;&emsp;对于大部分关键字,我们需要在其后添加空格,以提高代码的可读性。
113
114&emsp;&emsp;请您在所有这些关键字后面输入一个空格:
115
116```c
117if, switch, case, for, do, while
118```
119
120&emsp;&emsp;关键字sizeof、typeof、alignof、__atrribute__的后面则不需要添加空格,因为使用他们的时候,就像是使用函数一样。
121
122
123&emsp;&emsp;对于指针类型的变量,`*`号要贴近变量名而不是贴近类型名。如下所示:
124```c
125char *a;
126void *func(char* s, int **p);
127```
128
129&emsp;&emsp;在大多数二元和三元运算符周围(在每一侧)使用一个空格,如下所示:
130
131```c
132=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :
133```
134
135&emsp;&emsp;这些一元运算符后方没有空格
136
137```c
138&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined
139```
140
141&emsp;&emsp;特殊的例子,以下运算符的前后都不需要空格:
142```c
143++  -- . ->
144```
145## 4. 命名
146
147&emsp;&emsp;DragonOS中的命名规范不使用诸如`TempValue`这样的驼峰命名法的函数名,而是使用`tmp`这样言简意赅的命名。
148
149&emsp;&emsp;注意,这里指的是我们在整个项目内都不希望使用驼峰命名法。并不意味着程序员可以随便的使用一些难以理解的缩写来作为变量名。
150
151&emsp;&emsp;对于全局变量或者全局可见的函数、结构体而言,我们需要遵循以下的命名规定:
152- 名称需要易于理解,且不具有歧义。如:对于一个计算文件夹大小的函数而言,我们建议使用`count_folder_size()`来命名,而不是`cntfs()`这样令其他人头大的命名。
153- 全局的,非static的名称,除非有特别的必要,命名时需要遵循以下格式:`模块名缩写前缀_函数/变量名`。这样的命名能便于别人区分这个名称位于哪个模块内,也减少了由于命名冲突所导致的麻烦。
154- 不需要让其他代码文件可见的全局名称,必须添加`static`修饰符。
155
156&emsp;&emsp;对于函数内的局部变量而言,命名规范则是需要言简意赅。过长的名称在局部变量中没有太大的意义。
157
158【文档未完成,待继续完善】