Chapter_02 C语言的语言特性
- 2.1
编程语言的缺陷可以分为三类:
- 不该做的做了。——多做之过。
- 该做的没做。——少做之过。
- 该做但做的不合适。——误做之过。
- 2.2 多做之过
- fall through特性给switch语句带来的的麻烦
由于C语言的设计理念(相信程序员,依靠程序员对其行为作出安全性的保证),几乎从来不进行运行时错误检查——对进行解除引用操作的指针进行有效性检查除外,MS-DOS甚至这点都无法保证。
一个遵循标准的C编译器至少允许一条switch语句有257个case标签(8bit字符+EOF)。
switch语句另一个问题:内部的任何语句都可以加上标签,并在执行时跳转到那里。(goto语句对程序流的结构化带来的破坏)
const在C语言中并不代表真正的常量。见图:
(说明:这是在Visual Studio 2008中的结果,要将项目的Compile As选项设置为Compile as C Code (/TC)选项。因为由于 c++ 中,const 变量的值是在编译时就计算出来的,因此,它可以用在 case 语句中,而 c 中,const值在编译时只是一个变量的地址,因此,它无法用在 case 语句中。)
fall through的含义:如果case语句后面不加break,程序将依次执行下去。作者在此处列举了数据说明此种特性更适用于写编译器,97%的情况下都是错误的,从而认为”fall through“作为switch的缺省行为是一个失误。
2. 相邻的字符串常量将被自动合并成一个字符串的约定以及画蛇添足的拖尾逗号
如下这种情况:
str将被自动合并成"This is a sentence"。
拖尾逗号:字符串末尾的逗号在不在都没有意义,使C语言的自动生成时更容易一些。
作者在此处展示了使一段代码第一次执行时的行为与以后要执行时不同的技巧——利用static静态变量。
如上图的函数,执行6次的结果如下:
作者此处的观点:拖尾逗号将会抑制正确的行为,对程序没有什么好处。
3. 太多的缺省可见性
C函数的缺省的情况下函数的名字都是全局可见的。要限制对这个函数的访问,必须加个static关键字。缺省的全局可见性被证明是错误的。
全局可见性带来的interpositioning,interpositioning就是用户编写的和库函数同名的函数并取而代之的行为。
all-or-nothing:一个符号要么全局可见,要么对其他文件都不可见。C语言中的信息可见性的选择很少。
- 2.3 误做之过
C语言的简洁导致了符号的过度复用。
“当按照常规方式使用时,可能引起误会的任何运算符”可以认为是运算符存在着“错误”的优先级。
逗号运算符在所有运算符中优先级最低。
如上的情况,变量i的值是1,而不是2。
总之,作者建议:牢记乘法和除法的优先级高于加法和减法,然后在涉及其他的操作符时一律加上括号。
操作符的结合性:在几个操作符具有相同的优先级时结合性决定了先执行哪一个。
所有的赋值符都具有右结合性。如下:
a的值将是2。
作者建议:如果在计算表达式值的时候,需要考虑结合性,那么最好将这个表达式一分为二或者使用括号。
C语言中有些和顺序有关的问题定义的很含糊——大部分表达式里各个操作数计算的顺序就是不确定的。
C语言的问题还包括了标准库中的有些程序具有不安全的语义——例如gets导致的缓冲区溢出(蠕虫病毒利用操作系统高危漏洞进行的破坏与大规模传播均是利用此技术)。
用fgets()彻底取代gets()。
- 2.4 少做之过
ANSI C规定的“maximal munch strategy”(最大一口策略):编译器选取能组成最长字符序列的方案。
关于返回局部变量
如上的情况,由于buffer是局部变量(自动变量),其在堆栈中分配内存,因此,当fun函数返回时,buffer的内存便会被回收,造成了pstr的内容微不可预期的(图中表现为乱码)。
解决这个问题五个的方案:
- 返回一个指向字符串常量的指针。——最简单的解决方案,不适用于变量。
- 使用全局声明的数组。——适用于自己创建字符串的情况。缺点是全局变量对全局可见,且函数的下一次调用也会覆盖该数组的内容。
- 使用静态数组。——比全局变量好处在于可以防止任何人修改这个数组,但函数的下一次调用也会覆盖这个数组的内容且如果闲置不用很浪费内存空间。
- 显式分配内存(malloc),保存返回的值。——好处在于函数的下一次调用不会覆盖以前的值,适用于多线程。缺点就是程序员必须自己管理内存,容易导致内存泄露。
- 内存的分配交给调用者,调用者应该同时指定缓冲区的大小。——也许目前是最好的解决方案。
执行情况:
lint程序会对诸如”函数返回了局部变量的地址“之类的情况作出警告,如下如分别是visual studio和gcc的警告信息: