6. 全局变量默认是外部的(讨厌)
你会说“用全局变量可不是个好习惯!”。但在嵌入式系统中不同。举个例子,你有一个名为timer.c
的文件,其中有个全局变量int counter
,在另一个文件state_machine.c
中,有另一个counter
。如果你碰巧忘记了在它们之前加上’static’,它们就是同一个变量,你根本察觉不到,没有Warning,没有任何提示……
这种行为看起来十分奇怪,尤其是当关键字extern
就在手边的时候。不过当你熟悉static
的两种不同的意义后,就可以轻易避免这种情况了。不过这依然十分令人讨厌。
7. static
的两种不同的意义(讨厌)
有人能解释一下为什么static
在函数体中和函数体外有着两种完全不同的意义吗?在函数体中,他表示“静态”——“在函数调用过程中保持这个变量唯一”。但是在函数体外,它的意义完全改变,成了“该变量为该文件私有的”。为什么后者不用private
或者intern
呢?
8. & 优先级低于 ==(讨厌)
在嵌入式编程中,我们总是喜欢用if ((x&MASK) == 0)
这样的语句。但你可能常忘记写里面那对括号,因为感觉上,&的优先级应该比==高。但是事实并非如此,因此必须使用这对多出来的括号。
不过,这个情况有个不错的历史原因。C语言诞生自B语言,而在B语言中只有&而没有&&运算符。当Ritchie引入&&运算符时,他们希望原有的B语言端的代码能够正常运行,因此使&的优先级低于==。
9. 宏的功能并没有那么强(讨厌)
虽然递归的#include
是非常棒的点子,但是,要怎么做才能不诉诸一些费脑子的方法,轻易地做预处理循环呢?同样的,有些我常遇到的情况,比如怎么才能给程序int和string两种格式的版本号,而同时只需要修改一个变量呢?
1 2 | #define VERSION_INT 209
#define VERSION_STR "2.09"
|
用上面的代码,你更新版本号的时候总是需要修改两个地方。而且,特殊的#
和##
并不能帮上什么忙。我找到的唯一的解决则涉及了一些运行时修改。
10. 它不支持反射(讨厌)
好吧,可能这只是重申了一下第9点——如果宏系统再稍微强大一点,就不需要反射机制了。说不定我还会滥用它。不过我真正想说的是,用C语言,你不能写出生成代码的代码。
为什么不用C语言本身来写预处理器呢?这会给循环展开,更强大的宏机制,甚至更多IOCCC的怪点子提供无穷无尽的可能性。:-)
我认为,C语言之父能够坦然承认C的不足之处是非常可贵的。就像Dennis Ritchie说的一样:
“C语言行为古怪,瑕疵遍布,但却是一个巨大的成功。”同意的举手~~~