首页 > 代码库 > C语言中容易被忽略的细节(第二篇)

C语言中容易被忽略的细节(第二篇)

前言:本文的目的是记录C语言中那些容易被忽略的细节。我打算每天抽出一点时间看书整理,坚持下去,今天是第一篇,也许下个月的今天是第二篇,明年的今天又是第几篇呢?……我坚信,好记性不如烂笔头。


第一篇链接:C语言中容易被忽略的细节(第一篇)


1、C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。C语言中数组元素可以是任何对象,也可以是另外一个数组,即数组的数组。


2、C语言允许初始化列表出现多余的逗号。例如:int days[] = {1,2,3,};作用:方便自动化生成代码。


3、因为a[i]与*(a + i)等价,且由于a+i与i+a的含义一样,因此a[i]与i[a]具有相同含义。


4、在使用运算符“&&”的表达式中,要尽量把最有可能为false的子表达式放在“&&”左边;在使用运算符“||”的表达式中,要尽量把最有可能为true的子表达式放在“||”左边。因为C/C++对逻辑表达式的判断采取“猝死法”:如果“&&”左边的子表达式计算结果为false,则整个表达式就为false,后面的子表达式没有必要再计算;如果“||”左边的子表达式计算结果为true,则整个表达式就为true,后面的子表达式没有必要再计算。这种方法可提高程序效率。


5、对于多维数组来说,正确的遍历方法要看语言以什么顺序来安排数组元素的存储空间。对于C/C++多维数组来说,“先行后列”遍历效率肯定好于“先列后行”遍历,不管其行数远大于列数还是情况相反甚至接近。影响效率的实际上主要是大型数组导致的内存页面交换次数及cache命中率的高低,而不是循环次数本身。


6、为什么fgetc()函数返回值为int而不是unsigned char?

函数调用出错或读到文件末尾时fgetc()会返回EOF,即-1。若EOF保存在unsigned char型中,结果为0xff,而0xff是ASCII码有意义的字节,因而将无法区分是字节0xff还是结束符EOF。若EOF保存在int型中,结果为0xffffffff,如果读到字节0xff,转换为int型为0x000000ff,能够正确区分EOF和字节0xff。


7、如果函数没有参数,应使用void而不要空着,因为标准C把空的参数列表解释为可以接受任何类型和个数的参数,而标准C++则把空的参数列表解释为不可接受任何参数。在移植C/C++程序时,尤其要注意这方面的不同。


8、在函数体的“出口处”,对return语句的正确性和效率进行检查。

(1)return语句不可返回“堆栈内存”的“指针”或“引用”,因为该内存单元在函数体结束时被自动释放。

//存在隐患的写法  
char *Func(void)  
{  
    char str[] = "hello world";  
    return str;     //该语句存在隐患,str指向的内存单元将被释放  
}  
  
//可行的写法  
const char *Func(void)  
{  
    const char *p = "hello world";  
    return p;       //返回字符串常量的地址  
}  

(2)如果函数返回值是一个对象,要考虑return语句的效率。

return string(s1 + s2);     //语句1

string result(s1 + s2);     //语句2 
return result; 

语句1与语句2并不等价。语句1表示创建一个临时对象并返回它。语句2执行了三件事:

1)result对象被创建,调用相应的构造函数初始化;

2)调用拷贝构造函数,把result复制到保存返回值的外部存储单元中。

3)result在函数结束时调用析构函数被销毁。

注意:创建临时对象并返回它,编译器可直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的开销,提高了效率。内部数据类型变量不存在构造函数和析构函数,采用创建临时变量并返回,不会提高多少效率,但会使程序更加简洁。例如:

int x, y;
return x + y;  


9、不要省略返回值的类型,如果函数没有返回值,应声明为void类型。在标准C语言中,凡是不加类型说明的函数,一律自动按int类型处理。这样做不会有什么好处,却容易被误解为返回void类型。C++语言有严格的静态类型安全检查,不允许上述情况发生。由于C++程序可以调用C函数,为了避免混乱,我们规定任何C/C++函数都必须有返回值类型。


10、如果参数是指针,且仅做输入用,则应在类型前加const,以防止该指针指向的内存单元在函数体内无意中被修改。如果输入参数以值传递的方式传递对象,改用“const &”方式来传递,因为引用的创建和销毁不会调用对象的构造和析构函数,从而可提高效率。

C语言中容易被忽略的细节(第二篇)