读书笔记-《代码大全》

2 分钟阅读

强制类型转换的应用

C语言中void* 可以转换为任意指针

size_t 到 unsigned

变量初始化

  • 从未对变量赋值。它的值只是程序启动时变量所处内存区域的值
  • 变量值已经过期。变量在某个地方曾经被赋值,但该值已经不再有效
  • 变量的一部分被赋值,而另一部分没有

在声明变量的时候初始化

理想情况下,在靠近第一次使用变量的位置声明和定义该变量

  • 在有可能的情况下使用const, 定义常量,入参。
  • 特别注意计数器和累加器,在下一次使用时忘记重置其值。
  • 在类的构造函数中,初始化该类的数据成员
  • 检查是否需要重新初始化
int index = 0;
for(int i = 0; i < 10; ++i)
{
    /// do something with index
}
for(int i = 0; i < 10; ++i)
{
    int index = 0;
    /// do something with index
}

尽可能缩短变量存活时间

短的变量存活时间减少了初始化错误的可能。

变量存活时间短还会使代码具有可读性。阅读者同一时间内需要阅读的代码越少,越容易理解代码。

当需要把一个大的函数,拆分成几个小程序,短的存活时间方便拆分。

在循环开始之前再去初始化该循环里使用的变量,而不是在该循环所属的子程序的开始处初始化这些变量。

直到变量即将被使用时再为其赋值。

把相关语句放在一起。把相关语句提取成单独的子程序。

开始时采用最严格的可见性,然后根据扩展变量的作用域。比如,把一个循环内的变量挪到循环外的难度要比反过来难度低,或把一个private转变为public的难度远比反过来难度低。

避免采用硬编码,宏定义总是好于硬编码。

  • TITLE_BAR_COLOR0xFFFFFF 更能反应出所代表的信息
  • 同时,也方便修改宏定义时,同时改变所有的颜色的RGB值

为变量指定单一用途

temp = Sqrt(b*b - 4*a*c);
root[0] = (-b + temp) / (2*a);
root[1] = (-b - temp) / (2*a);

// swap the roots
temp = root[0];
root[0] = root[1];
root[1] = temp;
discriminant = Sqrt(b*b - 4*a*c);
root[0] = (-b + discriminant) / (2*a);
root[1] = (-b - discriminant) / (2*a);

// swap the roots
oldRoot = root[0];
root[0] = root[1];
root[1] = oldRoot;

避免让代码具有隐含含义,把同一变量用于多个多个用途的另外一种方式是当变量代表不同事务时让其具有不同的取值集合。

  • 变量count的取值可能表示某个计数,除非他等于-1,在这种情况下表明有错误发生
  • 变量customerId可能代表某个客户账号,除非他的取值大于50000,在这种情况下,你通过减去50000来得到过期账号。
  • 变量bytesWritten可能表示写入输出文件的字节数,除非它的取值为负,在这种情况下他表示的是用于输出磁盘驱动器的号码。

变量名的注意事项

糟糕的变量名

x = x - xx;
xxx = fido + SalesTax(fido);
x = x + LateFee(x1, x) + xxx;
x = x + Interest(x1, x);

良好的变量名

balance = balance - lastPayment;
monthlyTotal = newPurchases + SalesTax(newPurchases);
balance = balance + LateFee(customerID, balance) + monthlyTotal;
balance = balance + Interest(customerID, balance);

为变量命名时最重要的考虑事项时,改名字要完全、准确地描述该变量所代表的事物 不包含晦涩的缩写,同时也没有歧义。 对于一个表示中国奥林匹克代表团成员数量的变量,你可能会使用NumberOfPeopleOnTheChineseOlympicTeam 表示当前利率的变量最好为rate而不是r.

变量名太长: numberOfPeopleOnTheChineseOlympicTeam,numberOfSeatsInTheStadium, maximumNumberOfPointsInModernOlympics 变量名太短: n, np, ntm, ms, nsisd, m, max, min 变量名正好: numTeamMembers, teamMemberCount, numSeatsInStadium, seatCount, teamPointsMax, pointSRecord

很多程序有表示计算机结果的变量:总额、平均值、最大值,等等。如果你要用类似于TotalSumAverageMaxMinRecord这样的限定词,那么请一定记住把限定词加到名字最后。 变量名中最重要的部分应该被放置在最前面,限定词在最后。 这样做会避免,totalRevenuerevenueTotal异议词语

为状态变量起一个比flag更好的名字。最好把标记flag看作状态变量,标记的名字中不应该含有flag,因为你从中丝毫看不出该标记是做什么的。 含义模糊的标记

if (flag) ...
if (statusFlag & 0xF) ...
if (printFlag == 16) ...
if (computeFlag == 0) ...

更好的状态变量命名

if (dataReady) ...
if (characterType & PRINTABLE_CHAR) ...
if (reportType == ReportType_Annual) ...
if (recalcNeeded == false) ...

为布尔变量命名

  • donedone表示某件事情已经发生之前把变量值设为false, 在错误已经发生时把它设为true
  • errorerror表示有错误发生。在错误发生之前把变量值设为false, 在错误已经发生时把它设为true
  • foundfound来表明某个值已经找到了。在没有找到设为false, 找到后设为true.
  • successok, 操作失败时设为false, 操作成功后设为true

给布尔变量赋予隐含“真、假”含义的名字: statussourceFile是很糟糕的布尔变量名。 应该把status替换为类似error或者statusOK这样的名称,把sourceFile替换为sourceFileAvailablesourceFileFound

使用肯定的布尔变量名,否定的布尔名如notFoundnotDone以及notSuccessful比较难阅读。使用肯定的语义避免双重否定带来的阅读难度。

AnsiString strTmp;
strTmp = edtAccount->Text;
if(strTmp.IsInvalid())
{
    st.Account = strTmp;
}
strTmp = edtPassword->Text;
if(strTmp.IsInvalid())
{
    st.Password = strTmp;
}

缩写的一般指导原则:

  • 使用标准的缩写(列在字典中的那些常见缩写)
  • 去掉虚词and, or, the
  • 去掉无用的后缀ing, end
  • 确保不要改变变量的含义
  • 反复使用上述技术,知道你把每个变量名的长度缩减到了8到20个字符,或者达到你所用的编程语言对变量名的限制字符数。

不要用每个单词中删除一个字符的方式来缩写

键入一个字符算不上是什么额外工作,而节省一个字符带来的便利却很难抵消由此而造成的可读性的损失。

缩写要一致

应该一直使用相同的缩写。要么全用Num,要么全用No,也不要有些地方使用全写Number, 同时在其他地方使用缩写Num

创建你能读出来的名字

使用xPos而不是xPstn, 用needsCompu而不用ndsCmptg。这里可以使用电话沟通,如果你无法向他人读出你的代码,就请重新给变量起一个更清晰的名字。

名字对于代码的读者的意义要比对作者更重要

避免使用令人误解的名字或缩写

要确保名字的含义是明确的

避免使用具有相似含义的名字

如果你能够交换两个变量的名字而不会妨碍对程序的理解,那么你就需要为这两个变量重新命名了。

避免在名字中使用数字

如果名字中的数字真的非常重要,就请使用数组来代替一组单个的变量。如果数组不合适,那么数字就更不合适。

避免在名字中拼错单词

避免在名字中使用容易混淆的字符

  • 数字1和小写的l
  • 数字1和大写的L
  • 数字0和大写的O
  • 数字2和小写的z
  • 数字6和大写的G

避免浮点数的数量级相差巨大的数字之间的四则运算

#include <iostream>

int main()
{
    double d = 100000000.0 + 0.1;
    std::cout << d << std::endl;
    return 0;
}

避免浮点数的等量比较

#include <iostream>

int main()
{
    double tmp = 0.0;
    for(int i = 0; i < 10; ++i)
    {
        tmp += 0.1;
        std::cout << tmp << std::endl;
    }
    return 0;
}
int a = 0;
int b = 0;
bool IncreaseA()
{
    a++;
    return false;
}
bool IncreaseB()
{
    b++;
    return true;
}

int main(int argc, char* argv[])
{
    if(!IncreaseA() || !IncreaseB())
    {
    }
}

为空语句创建一个DoNothing()预处理宏或者内联函数

while(recordArray.Read(index++) != recordArray.EmptyRecord())
{
    ;
}

#define DoNothing()
while(...)
{
    DoNothing();
}

更新时间: