getchar()的副作用

老的C语言程序员中有一种倾向,就是把很短的执行频繁的计算写成宏,而不是定义为函数。完成I/O的getchar,做字符测试的isdigit都是得到官方认可的例子。人们这样做最根本的理由就是执行效率:宏可以避免函数调用的开销。实际上,即使是在C语言刚诞生时(那时的机器非常慢,函数调用的开销也特别大),这个论据也是很脆弱的,到今天它就更无足轻重了。有了新型的机器和编译程序,函数宏的缺点就远远超过它能带来的好处。

  避免函数宏。在C++里,在线函数更削减了函数宏的用武之地,在Java里根本就没有宏这种东西。即使是在C语言里,它们带来的麻烦也比解决的问题更多。

  函数宏最常见的一个严重问题是:如果一个参数在定义中出现多次,它就可能被多次求值。如果调用时的实际参数带有副作用,结果就会产生一个难以捉摸的错误。下面的代码段来自某个<ctype.h>,其意图是实现一种字符测试:

  #define isupper(c)((C)>=‘A’&& (C)<=‘Z)

  请注意,参数c在宏的体里出现了两次。如果i s u p p e r在下面的上下文中调用:

  While  (isupper(C=getchar()))

  那么,每当遇到一个大于等于A的字符,程序就会将它丢掉,而下一个字符将被读入并去与Z做比较。C语言标准是仔细写出的,它允许将isupper及类似函数定义为宏,但要求保证它们的参数只求值一次。因此,上面的实现是错误的。

  直接使用ctype提供的函数总比自己实现它们更好。如果希望更安全些,那么就一定不 要嵌套地使用像getchar这种带有副作用的函数。我们重写上面的测试,把一个表达式改成两个,这里还为捕捉文件结束留下机会:

  While ((C = getchar())!= EOF && isupper(C))

posted on 2013-03-13 15:13  tracymdy  阅读(266)  评论(0编辑  收藏  举报