很多人写代码是照猫画虎,这些“猫”最终就变成了教条(注1)。一旦教条被人熟知,不同的变量,数值,功能就被按照教条使用,然后用一些“胶水”代码组合起来,实现需要的方案。通过对语法的深入了解,我们可以消除很多的“胶水”。这篇文章举了几个怪异的C语法的例子,以及如何在不导致歧义(注2)的情况下,利用(滥用?)他们实现更高效的代码。
记得返回值
我的第一个关于“教条编程”的例子将讨论格式化输出函数sprintf。下面这段代码的写法并不鲜见:
sprintf(str1, "Old v=%d\t",v);
/* Some code that plays with v */
sprintf(str2, "New v=%d",v);
strcat(str1, str2);
printf(str1);
大部分的sprintf实例使用一个临时的字符串变量作为它的第一个参数。这就是那个根深蒂固的教条:“sprintf需要一个临时字符串”。然而更好的教条是符合语法的,“sprintf需要一个指向字符数组的指针”。这提醒我们,可以用一个返回char *的函数替代它作为第一个参数,从而节省一个临时缓冲区的空间。例如:
sprintf(str, "Old v=%d\t",v);
/* Some code that plays with v */
sprintf(strchr(str, '\0'), "New=%d",v);
printf(str);
当直接使用指针的时候,你必须谨慎的避免NULL指针。在这个例子里,str(str,'\0')的有效得益于第一个sprintf。sprintf还隐藏了一个好处-它返回已经写入缓冲区的字符数量-这个数量可以节省一次对strlen的调用!检查你的代码吧。祝你好运!
另一个例子,如果得到一个字符串的长度,是为了在某些操作时判定它是否有效,你也许会这样写:
len = strlen(GetFileName());
if (len > 0)
; /* File name is not null */
然而,如果你只是打算用它和0比较的话,为什么不这样写:
if (GetFileName()[0] != '\0')
; /* File name is not null */
这次,我们不仅节省了一个缓冲区(这很普通,我们直接使用返回值(注3)-这也避免了一次额外的拷贝),而且还在正确的检查了返回值的同时,消除了前面strlen的调用开销。别忘了,相同的结果可以用等价的方法得到。
if (*GetFileName () != '\0')
; /* File name is not null */
因为字符串只是一个(可以用char * 指向的)结构,上面的方法同样可以用于返回结构体,C++类或其指针的函数,直接提取你需要的元素。例如:
struct POINT GetCurrentPos(void);
int y;
y = GetCurrentPos().y;
译者:
本文是Steve Goodwin的大作,译者水平有限,无法很严谨的翻译文中要表达的内容,只能就个人的理解,尽量在含义上贴近原文。尽管对这篇文章的某些观点并不十分理解和赞同,但是对于开发者来说,仍然是很有价值的参考。
原文链接:http://www.gamedev.net/reference/articles/article1239.asp
注1:原文:This example becomes a template,template本意是模板,但是这里理解为教条更能表达感情的好恶。
注2:原文:without qualifying for the IOCCC (International Obfuscated C Code Contest),意译,不是很准确。
注3:原文:seeing as we use the result directly ,这里的result指的应该是函数返回值。