文章内容由林锐编写的《高质量C/C++编程指南》结合自身在学习中遇到的困难整理出来的常见问题。希望帮助大家,快速了解一些编程规范。
每个C/C++程序通常分为两个文件。一个文件用于保存程序的声明(declaration),称为头文件。另一个文件用于保存程序的实现(implementation),称为定义(definition)文件。
C/C++程序的头文件以“.h”为后缀,C程序的定义文件以“.c”为后缀,C++程序的定义文件通常以“.cpp”为后缀。
#prama once //防止头文件被重复引用 #include <math.h> //引用标准库的头文件 #include "myheader.h" //引用非标准库的头文件 void Function1(…); //全局函数声明 class Box //类结构声明 { ... }
定义文件有两部分内容: 1. 对头文件的引用。 2. 程序的实现体(包括数据和代码)。
#include "myheader.h" //引用头文件 //全局函数的实现体 void Function1(...) { ... } //类成员函数的实现体 void Box::Draw(...) { ... }
(1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。
(2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
版式虽然不会影响程序的功能,但会影响可读性。程序的版式追求清晰、美观,是程序风格的重要构成因素。
空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。
注释通常用于: (1)函数借口的说明; (2)重要代码行或段落提示。
int x, X; //变量x与X容易混淆 void foo(int x); //函数foo与FOO容易混淆 void FOO(float x);
float value; float oldValue; float newValue;
DrawBox(); //全局函数 box->Draw(); //类的成员函数
int minValue; int maxValue; int setValue(...); int getValue(...);
class Node; //类名 class LeafNode; //类名 void Draw(...); //函数名 void SerValue(...); //函数名
int drawMode; float minValue;
const int MAX = 100; const int MAX_LENGTH = 100;
static int s_initValue; //静态变量
int g_howManyPeople; //全局变量 int g_howMuchMoney; //全局变量
void Object::SetValue(int width, int height) { m_width = width; m_height = height; }
word = (high << 8) | low if ((a | b) && (a & c))
如a = b = c = 0这样的表达式称为复合表达式。允许复合表达式存在的理由是: (1)书写简洁; (2)可以提高编译效率。 但要防止滥用复合表达式。
i = a >= b && c < d && c + f <= g + h; //复合表达式过于复杂
d = (a = b = c) + r; //该表达式既求a值又求d值。应当拆分为两个独立的语句: a = b + c; d = a + r;
if (a < b < c) //a < b < c是数学表达式而不是真正的程序表达式 //并不表示 if ((a < b) && (b < c)) //而是 if ((a < b) < c)
if (flag) //表示flag为真 if (!flag) //表示flag为假
if (value == 0) if (value != 0) //不可模仿布尔变量的风格而写成 if (value) //会让人误解 value 是布尔变量 if (!value)
if (x == 0.0) //隐含错误的比较 //转化为 if ((x >= -EPSINON) && (x <= EPSINON)) //其中EPSINON是允许的误差(即精度)
if (p == NULL) //p与NULL显式比较,强调p是指针变量 if (p != NULL) //不要写成 if (p == 0) //容易让人误解p是整型变量 if (p != 0) //或者 if (p) if (!p) //容易让人误解p是布尔变量
有时我们可能会看到 if (NULL == p) 这样奇怪的形式。这是为了防止将 if (p = = NULL) 误写成 if (p = NULL),而颠倒二者顺序,编译器会认为 if (NULL = p) 是错误的,而 if (p = NULL)是合法的。因为NULL不能被赋值。
for (int x = 0; x < N; x++) //起点到终点间隔为N,循环次数为N for (int x = 0; x <= N 1; x++) //起点到终点间隔为N-1,循环次数为N
如果不使用常量,直接在程序中填写数字或字符串,将会有什么麻烦?
(1)程序的可读性(可理解性)变差。程序员自己会忘记那些数字或字符串是什么意思,用户则更加不知它们从何处来、表示什么。
(2)在程序的很多地方输入同样的数字或字符串,难保不发生书写错误。
(3)如果要修改数字或字符串,则会在很多地方改动,既麻烦又容易出错。
#define MAX 100 //宏常量,结尾不能加分号 const int MAX = 100; //const常量
C/C++语言可以用const来定义常量,也可以用#define来定义常量。但是前者比后者有更多的优点:
(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。(因为宏替换发生在预处理阶段)