• 2008-12-21

    回头望C

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/32787473.html

    周游了许多语言之后,重回C的怀抱,那些之前不曾注意的问题,浮出了水面。

    从头文件说起,第一个问题就是为什么要分头文件,也许最恰当的答案就是编译器技术受限C产生那个年代的资源。

    还记得刚开始学写C程序的时侯,第一次学会防止重编译的手段觉得自己很厉害,开始向专业人士迈进了:
    #ifndef _BLAH_H
    #define _BLAH_H

    ...

    #endif

    虽然这样的代码让自己看上去更像真正的C程序员,可除了C/C++,我再也没有用到过这种手法。原因很简单,其它语言都有自己的处理方法,确保消除这种重复。比如Ruby里面的require,会由运行时系统检查该文件是否已经包含其中(虽然做得并不完美)。仔细想想,其实,即便是C,如果改进一下编译器,比如加上一个包含文件的列表,就完全可以做到这一点。还是前面提到的那一点,那个年代的编译器技术遗留下的问题,把原本该由编译器做好的事情,留给了程序员。幸运的是,这种防止重编译的手法已经成了事实的标准。

    接下来的问题是头文件里面放什么?通常的说法是放声明。声明包括宏的声明、类型声明、函数声明,还有变量声明。是的,变量。从理论上说,头文件里面可以放任何东西,甚至是变量的定义(《大头随笔(一)》仔细讨论了声明和定义的差别)。

    从工程的角度出发,头文件扮演着接口的角色。也就是说,我们应该把一些一个模块允许其它模块调用的部份放在头文件里面。站在今天对软件开发的理解上,我们希望把数据隐藏起来,换句话说,变量不应该出现头文件。数据隐藏对于已经习惯面向对象的人来说,是一个基本常识,但是,很多还在编写C代码的人,他们总会告诉你,访问全局变量是一件多么惬意的事。看着说话时的幸福神情,我想他们多半是忘记了找bug的痛苦。

    当我们希望保护自己时,C语言却让我们显得是那么无能为力。尽管我们可以把头文件当作接口,但事实上,别人依然可以跨过不去理会头文件中的定义,直接访问模块内部定义的函数。很简单,声明一下就可以直接拿过来用,不仅仅是函数,变量也可以。而写出这种代码的原因通常是省事,而图一时之快的结果,对于工程来说,带来的只会是日后无尽的昏乱。也许只有混乱来临时,我们才会想起在C中还有一种叫做static的东西。

    由前面的讨论可以看出,C语言本身对于工程的支持还是很弱的,很多时侯,只能依靠约定,而虽然约定不是100%可靠,但聊胜于无。
    分享到:
    引用地址:

    评论

  • LInux内核源代码中,把static和inline发挥到了极致,头文件的使用也很多。也许,以你现在的功力,更能从美学的角度看内核代码了。
  • 难得原创的好文章、好网站。
    想跟贵站做个友情链接交换,贵站链接已做好,在我的网站首页下边,请核对。
    网站名称:衣儿叁IT笔记
    网址:www.note1234.com
    我的网站刚开通不久,没什么人看,但我会一直写下去,如果贵Blog愿意并做好本站链接,请告知一声,不胜感激。
  • 所以,C的世界是人品的世界,
    但是,后来发现人品难以控制了~因为IDE的出现,程序没有了历练的过程,
    所以,各种依赖的问题被语言或是IDE 隐藏了,只有真正成大问题时,大家才注意到,
    所以,C的直接将问题提前暴露的哲学也是很好的,问题都在明面儿上,你不注意,就是不靠谱,,代码是否对味,一眼看的出来,,,
  • 当我们希望保护自己时,C语言却让我们显得是那么无能为力。尽管我们可以把头文件当作接口,但事实上,别人依然可以跨过接口,直接访问我们原本希望只有实现文件看到的代码。很简单,声明一下就可以直接拿过来用,不仅仅是函数,变量也可以。
    -------------------------------------------
    “希望只有实现文件看到的代码”可以使用static修饰,这样相应的函数或全局变量只能在当前的.c文件(确切地说应该叫编译单元)内可见。这样一来即使在其它的编译单元内声明了该函数/变量,也无法链接到static修饰的相应函数/变量。
    回复kusk说:
    多谢指正,确实不够严谨。修改了一下。:)
    2008-12-21 23:57:34