自打开始学习编程,许多前辈都不止一次的郑重的提醒我代码规范的重要性。而在这近百来天的学习中,我也竭力模仿优秀规范的代码,刻意在我的代码中融入其的影子,可以说我的以我的代码风格为傲。但是依着书中前言的指引,先做了附录B中的试题,近乎及格一半的35分,让我不得端正心态,认真地学习、笔记正文,以求窥探编程的深渊
读书是由浅入深而后入浅的过程,期待我的二刷
文件结构
声明版权和版本
其中,
©(也可以写成(c))是版权的意思。
2001表示代码完成时间。如是时间段,如(2011——2018),表示代码完成时间为2011、最近一次修订在2018年
公司如果没有也可以写个人
头文件的结构
头文件有三部分内容
头文件开头处的版权、版本声明
预处理块
函数和类结构声明等
用ifndef/define/endif结构产生预处理块,防止重复定义
工程量不大是,重复引用或许只是效率变低的问题。工程量较大时,编译量的过大会很麻烦
如果在a.h中定义了全局变量(不建议),会报错(重复定义的错误)
#ifndef GRAPHICS_H//如果没有包含
#define GRAPHICS_H//那么就包含
#include<stdio.h>//引用标准库的头文件
#include"game.h"//引用非标准库的头文件
void Function(……)//全局函数声明
class Box//类结构的声明
{
……
}
#endif GRAPHICS_H//否则,不需要包含
定义文件的结构
定义文件有三部分内容
定义文件开头处的版权和版本声明
对头文件的引用
程序的实现体
#include<stdio.h>//引用头文件
void Function(……)//全局函数的实现体
头文件的作用
通过头文件调用库功能
头文件能加强类型安全检查
目录结构
补
声明与定义
定义:表示创建变量或者分配内存单元
声明:说明变量的性质,但不分配内存单元 extern也是声明(引用性声明)
声明可以多次但定义只能有一次
关键字extern
一般用于变量名前或函数名前,以此说明“此变量/函数在别处定义,于此处引用”
C语言提供了一下不同的存储类型
自动变量(auto)
静态变量(static)
外部变量(extern)
寄存器变量(register)
extern标准定义格式: extern 类型名 变量名
如果在所有函数之外定义的变量没有指定其储存类别,那么它就是一个外部变量,它的作用域是从它的定义点到本文件末尾(如果有多个源文件,全局变量的作用范围不是从变量定义处到该文件结尾。而是在其他文件中也有效)。如果要在定义点之前或者其他文件中使用它,那么就需要关键字extern对其声明(不是定义,不分配内存)
extern int i;//是声明不是定义,没有分配内存
int i;//是定义
//如果在声明的时候给变量赋值,那么就和去掉extern直接定义变量赋值是等价的
extern int a = 10;//尽量不要写这种定义方式
int a = 10;//上述两条语句等价
以上的不同语句对声明和定义的区分只适用于源文件中的局部变量。
注:
对于int a来说,在源文件中,如果是全局变量就是声明,如果是局部变量的话就是定义
#include<stdio.h>
int a;//全局变量
int main()
{
return 0;
}
#include<stdio.h>
int main()
{
int a;//局部变量
return 0;
}
程序的版式
版式虽不会影响程序的功能,但会影响可读性
空行
空行起着分隔程序段落的作用
在每个类声明后、每个函数定义结束之后都要加空行
在一个函数体内,逻辑上密切相关的语句之间不加空行,其他地方应加空行分隔
代码行
一行代码只做一件事情,易于阅读、便于写注释
if、while、for、do等语句独占一行。执行语句不得紧随其后。不论执行语句有多少,都要加{}
尽可能在定义变量的同时初始化该变量(就近原则)
关键字之后要留空格。
函数名之后不要留空格,以与关键字区分
前括号 向后紧跟 后括号、逗号、分号、向前紧跟,紧跟处不留空格
逗号之后要留空格。如果分号不是一行的结束符号,其后要留空格
赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符等二元操作符的前后应当加空格
一元操作符前后不加空格
中括号、点(.)、“->”这类操作符前后不加空格
对于表达式比较长的 for 语句和 if 语句,为了紧凑起见可以适当地去
掉一些空格,如 for (i=0; i<10; i++)和 if ((a<=b) && (c<=d))
对齐
程序的分界符“{” 和 “}”应该独占一行并且一对{}应该位于同一列。同时与引用他们的语句左对齐
长行拆分
代码行最大长度宜控制在 70 至 80 个字符以内
长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以
便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。
修饰符的位置
应当将修饰符 * 和 & 紧靠变量名
注释
注释是对代码的“提示”,而不是文档。不可喧宾夺主
代码本身就是清楚的,不必加注释
边写代码边写注释,修改代码的同时修改注释,确保代码与注释的一致性
注释应当准确、易懂,不存在多义
尽量避免在注释中使用缩写
注释的位置应与代码相邻
当代码较长,特别是有多重嵌套时,应当在一些段落结束处加注释
类的注释
命名的规则
标识符应当直观且可以拼读,可望文知意,不必进行“解码”。标识符最好采用英文单词或其组合,便于记忆和阅读。切忌使用汉语拼音来命名。
标识符的长度应当符合“min-length && max-information”原则
命名规则尽量与所采用的操作系统或开发工具的风格保持一致
程序中不要出现仅靠大小写区分的相似的标识符
int x , X //变量x 与 X 容易混淆
void foo(int x) // 函数foo、Foo容易混淆
void Foo(float x)
程序中不要出现标识符完全相同的局部变量和全局变量 变量的名字应当使用“名词”或者“形容词+名词”
float value
float oldValue
float newValue
全局函数的名字应当使用“动词”或者“动词+名词
DrawBox//全局函数
用正确的反义词组命名具有互斥意义的变量或相反动作的函数
int minValue;
int maxValue
尽量避免名字中出现数字编号
类名和函数名用大写字母开头的单词组合而成
class Node; // 类名
class LeafNode; // 类名
void Draw(void); // 函数名
void SetValue(int value); // 函数名
变量和参数用小写字母开头的单词组合而成
BOOL flag;
int drawMode;
常量全用大写的字母,用下划线分割单词
const int MAX = 100;
const int MAX_LENGTH = 100;
静态变量加前缀 s_(表示 static)
void Init(…)
{
static int s_initValue; // 静态变量
…
}
如果不得已需要全局变量,则使全局变量加前缀 g_(表示 global)
int g_howManyPeople; // 全局变量
int g_howMuchMoney; // 全局变量
表达式和基本语句
如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免
使用默认的优先级
if ((a | b) && (a & c))
不要编写太复杂的复合表达式
i = a >= b && c < d && c + f <= g + h ; // 复合表达式过于复杂
不要有多用途的复合表达式
d = (a = b + c) + r ;
//应拆分为
a = b + c;
d = a + r;
不要把程序中的复合表达式与“真正的数学表达式”混淆
if (a < b < c)//a < b < c 是数学表达式而非程序表达式
if ((a<b) && (b<c))
布尔变量:
布尔变量只能有两个可能值的变量:真(1)、假(0)
声明一个布尔变量,用关键字bool
不可将布尔变量直接与 TRUE、FALSE 或者 1、0 进行比较
if (flag) // 表示 flag 为真
if (!flag) // 表示 flag 为假
整型变量用“==”或“!=”直接与 0 比较
if (value == 0)
if (value != 0)
不可将浮点变量用“==”或“!=”与任何数字比较。应该设法转化成“>=”或“<=”形式
if ((x>=-EPSINON) && (x<=EPSINON))//EPSINON 是允许的误差(即精度)
在C语言中,float 和double 的EPSINON值在float.h头文件中以宏的方式给出
FLT_EPSILON
DBL_EPSILON
应当将指针变量用“==”或“!=”与 NULL 比较
有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了程序员为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把 p 和 NULL 颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为 NULL不能被赋值。
如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面
不可在 for 循环体内修改循环变量,防止 for 循环失去控制
建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法
每个 case 语句的结尾不要忘了加 break,否则将导致多个分支重叠
(除非有意使多个分支重叠)。
不要忘记最后那个 default 分支
少用 慎用
尽量使用含义直观的常量来表示那些将在程序中多次出现的数字或字符串
如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值
const float RADIUS = 100;
const float DIAMETER = RADIUS * 2;
函数设计
参数的书写要完整
void SetValue(int width, int height); // 良好的风格
void SetValue(int, int); // 不良的风格
float GetValue(void); // 良好的风格
float GetValue(); // 不良的风格
参数命名要恰当,顺序要合理
如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该指针在函数体内被意外修改。
尽量不要使用类型和数目不确定的参数
避免函数有太多的参数
不要省略返回值的类型
char c;
c = getchar();
if (c == EOF)
函数名字与返回值类型在语义上不可冲突
有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值(strcpy)
return 语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁
函数的功能要单一
函数体的规模要小
少用static局部变量
使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的
在函数的入口处,使用断言检查参数的有效性