通用程序设计

来源:互联网 发布:马云淘宝提成怎么算 编辑:程序博客网 时间:2024/06/10 05:17
 

3.     通用程序设计

本章讨论一些您设计程序时需要考虑的问题。

3.1     使用哪种语言

如果你希望使用一种可以取得更快的编译和运行速度的语言的话,那么最好的是C。使用其他语言就像是使用了一种非标准化的特性:它将会对使用者引引起麻烦。即使GCC支持其他语言,但使用者会发现为了使用你的程序,不得不为这种语言安装编译器,很不方便。例如,如果你使用C++写你的程序使用者不得不安装CNU C++编译器来编译你的程序。

C相对于C++和其他编译型语言有一个优势:很多人知道C,所以人们会发现这些用C写的程序很容易阅读和修改。

所以通常情况下使用C是最好的。

但是有两个例外:

  • 为那种语言开发的专用工具. 这是由于那些要编译安装这个工具的人已经安装了那种语言
  •  如果一个应用只有社区中一小部分的人有兴趣,那么使用什么语言的问题对其他人来说没有什么影响,所以你大可以随你高兴。

许多程序设计为具有可扩展性:他们包含一种比C语言更高级的语言的解释器。一般来说,程序的很大一部分也是用那种语言写的。emacs就是这种技术的代表。

标准的可扩展性解释器是GNU软件Guile,他实现了一种语言方案(一种特别简洁的Lisp语言)。Guile还包括用于GTK+/GNOME,使得在Guile中编写现代GUI程序更具有实用性。我们不拒绝使用“脚本语言”编写软件,如Perl和Python,但是对于GNU系统的相容性是非常重要的。

3.2    与其他程序的兼容

除了极少数的例外,GNU的工具程序和库应该与Bakeley Unix向上兼容,与标准C向上兼容如果标准C指定了他们的行为,与POSIX向上兼容如果POSIX指定了他们的行为。

当这些标准冲突时,给每一种标准提供兼容模式。

标准C和POSIX禁止了很多扩展。可以放心使用这些扩展,只要包含能够关闭它们的选项 --ansi,--posix,--compatible。但是,如果这个扩展对于真正的程序或者脚本有显著的影响,那么他就不符合真正的向上兼容.因此你应该重新设计接口使得它能够向上兼容.
许多GNU程序都压缩那些与posix冲突的扩展,如果定义了环境变量POSIXLY_CORRECT. 请使你的程序在适当的时候也能识别这个变量.
当一个特色只有用户使用(不是程序或者命令文件),这种情况在Unix中很少见,可以放心用不同的或者更好的来替换它.(比如,vi被Emacs替换.) 但是能够提供兼容的特性会更好.(有一个免费的vi变种,因此我们提供了它).
欢迎新增的他有用特性,不关它们是否已经有先例.

3.3    使用非标准特性

与Unix工具比起来,许多GNU工具已经提供了许多方便的扩展。在设计你的程序时,是否使用这些扩展确实是一个困难的问题.。
一方面,使用这些扩展能创建更清晰的程序。另一方面,人们将不能编译这些除他们有这些GNU工具。这将使这些程序只能在较少的机器上运行.。
对于一些扩展,很容易提供用与不用的选择。比如,你可以使用"关键子"INLINE 定义函数,并且把它定义为宏,依据编译器的不同,可以扩展为inline或者空.
一般说来,如果没有这些扩展可以的话,最好就不要使用,除非这些扩展能够带来大的改进.。
这个规则对那些大的,固定的,可以在许多平台上运行的程序(比如Emacs)例外。在这些程序中使用GNU扩展将会使用户不高兴,因此我们不会那样做。

另外一种例外是设计作为编译的一部分的程序: 所有那些为了引导GNU编译工具而必须使用其他编译器的程序。如果他们需要GNU编译器,那么人们将不能编译这些程序除非他们已经安装了GNU编译器。在特定情况下,那将会是极其糟糕的.。

3.4    标准C和原始C

C89 现在已经广泛使用,因此在新的程序中使用它的特性将没有任何问题。但是有一个例外:不要使用标准C的“trigraph”特性。

C99还没有被广泛使用,所以不要在程序中使用它的特性。但是可以使用已经存在的特性。

然而,在大多数程序中,很容易支持原始的C编译器,如果你知道如何做,那么请随便。如果你在维护的程序有这种支持,你应该尽量让它继续运转。

为了支持原始C,必须使用标准原型格式来书写函数定义,

int

foo (int x,int y)

...

用原始C的风格,要这样写,

int

foo (x,  y)

        int x, int y;

....

并且使用一个单独的声明来指明函数原型:int foo (int , int);

无论如何你需要这样一个声明,把它放在头文件中,这样在所有调用这个函数的文件里就不用再写一遍。并且,一旦你声明了,以原始C风格定义函数你不会丢失任何信息。

这种技术对于宽度窄于int的类型不合适。如果你认为一个参数类型宽度比int窄,直接把它声明为int。

有些特殊情况下,这种技术很难用上。比如,如果一个函数的参数需要系统类型dev_t,你就陷入困境,因为dev_t在一些机器上比int短;但是你又不能用int来代替,因为dev_t在一些机器上比int宽。因此你没法以非标准方式安全地使用一种类型。既支持非标准C又能传递这样的参数的唯一方法就是使用Autoconf来检查dev_t的宽度,然后选择相宜给你的类型。这个问题这样处理可能不划算。
为了支持那些不能识别原型的非标准C编译器,你可能想使用这样的宏定义:

/* Declare the prototype for a general external function. */
#if defined (__STDC__) || defined (WINDOWSNT)
#define P_(proto) proto
#else
#define P_(proto) ()
#endif

3.5   条件编译

如果已经知道配置选项,那么在编译你的程序时,应该使用if(...)而不是条件编译,因为在前一种情况下,编译器能够对所有可能的路径进行广泛的检查.
比如: 要写

          if (HAS_FOO)

              ....

          else

             .....

而不是要写

          #ifdef  HAS_FOO

                ....

         #else

               .....

        #endif

在这两种情况下,现代的编译器比如GCC将会生成相同的代码,并且,我们已经在几个项目中成功地使用了类似的技术。当然,前一种方法假定HAS_FOO被定义为0或1.。

尽管它并不象银色子*(silver bullet)那样解决了所有的可移植性问题,并且并不是总是很恰当,但是遵照这种策略,可以每年节省GCC开发者许多小时,甚至许多天.。
在处理GCC中的类似函数的宏比如REVERSIBLE_CC_MODE时,不能简单地使用if(>...)语句。这里有一种简单的方法. 再引入另外一个HAS_REVERSIBLE_CC_MODE宏即可. 例如:

            #ifdef REVERSIBLE_CC_MODE
            #define HAS_REVERSIBLE_CC_MODE 1
            #else
            #define HAS_REVERSIBLE_CC_MODE 0
            #endif

原创粉丝点击