登录社区:用户名: 密码: 忘记密码 网页功能:加入收藏 设为首页 网站搜索  

文档

下载

图书

论坛

安全

源码

硬件

游戏
首页 信息 空间 VB VC Delphi Java Flash 补丁 控件 安全 黑客 电子书 笔记本 手机 MP3 杀毒 QQ群 产品库 分类信息 编程网站
  立华软件园 - 安全技术中心 - 技术文档 - 入门基础 技术文章 | 相关下载 | 电子图书 | 攻防录像 | 安全网站 | 在线论坛 | QQ群组 | 搜索   
 安全技术技术文档
  · 安全配制
  · 工具介绍
  · 黑客教学
  · 防火墙
  · 漏洞分析
  · 破解专题
  · 黑客编程
  · 入侵检测
 安全技术工具下载
  · 扫描工具
  · 攻击程序
  · 后门木马
  · 拒绝服务
  · 口令破解
  · 代理程序
  · 防火墙
  · 加密解密
  · 入侵检测
  · 攻防演示
 安全技术论坛
  · 安全配制
  · 工具介绍
  · 防火墙
  · 黑客入侵
  · 漏洞检测
  · 破解方法
 其他安全技术资源
  · 攻防演示动画
  · 电子图书
  · QQ群组讨论区
  · 其他网站资源
最新招聘信息

撒旦的语法
发表日期:2007-03-28作者:[转贴] 出处:  

   很多人写代码是照猫画虎,这些“猫”最终就变成了教条(注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;


或者,

    printf(GetDevice()->pName);

编译器可能会把结构创建为临时对象,然后把他们的指针作为隐含参数,所以你不必顾虑把结构体拷贝进栈或者出栈的开销。

从另一个角度,你还记得“函数无法返回一个字符串”的课程吗?其实你可以。方法很多。例如,创建一个名为STRING的结构体,里面包含一个字符串数组,你可以在函数中返回这样一个STRING,然后像上面那样引用其中的字符串。
    struct STRING { char str[256]; };

    struct STRING GetName(void)
    {
        .
    }

    printf(GetName().str);


实际开发中,这和传递字符串指针相比,用处很少甚至完全无益,但是它的确减少了程序员创建临时变量的需求。

表达式

  函数调用只是表达式。如果他们返回一个类型(不包括void,那并不是一种真正的类型),那么你就可以把它作为一个普通的表达式用在任何需要的地方,例如一个while循环。这使得循环或者类似的流程被快速短路成为可能,从C的懒惰表达式计算中获益。(看补充)
    while (1 && GetNextLine(&str))
        ;

把表达式中的1改成0,导致整个表达式的结果变成0,GetNextLine函数不会被调用,循环永远都不会运行。我经常使用这种方法在测试代码中取消复杂的if语句块。
    if (1
        && ComplicatedExpression1
        && ComplicatedExpression2
        && ComplicatedExpression3
    )
        ;


通过把1改成全局,静态,变量,我可以在调试时,取消这些代码的运行,或者通过菜单选项改变它,从而在一次编译中测试多个项目。

结束

我希望还有一些新的想法告诉你。我忘了讲,代码也可以使用这种方式移除:
    /*
    . code here .
    //*/

然后用在首行添加一个单独的/字符来重新插入这段代码。

补充

C使用一个懒惰表达式计算器。这就是说,它只计算需要的表达式来推导最终的结果是TRUE还是FALSE,所以一行类似
    if (fn1() && fn2() && fn3())
.
的代码,会先计算fn1(),只有在必要时才继续计算fn2(),也就是说,当fn1()返回TRUE的时候。如果它返回FALSE,无论其他表达式如何,都无法使得最终结果是TRUE。大部分人从如下的代码段中凭直觉了解了这一点:
    if (ptr && ptr->Name)
        printf(ptr->Name);


这和Pascal是不同的。


译者:
  本文是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指的应该是函数返回值。
我来说两句】 【发送给朋友】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 撒旦的语法

 ■ [欢迎对本文发表评论]
用  户:  匿名发出:
您要为您所发的言论的后果负责,故请各位遵纪守法并注意语言文明。

最新招聘信息

关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放 / 友情链接  
Copyright ©2001-2006 Lihuasoft.net webmaster(at)lihuasoft.net
网站编程QQ群   京ICP备05001064号 页面生成时间:0.00213