【转载】为何不常见include .c文件

备:对于#include  <filename.h> ,编译器从标准库路径开始搜索 filename.h
       对于#include  “filename.h” ,编译器从用户的工做路径开始搜索 filename.hshell

今天有人问我: #include能不能include一个(多个.c文件)?
偶的回答是:从理论上讲能够,可是不推荐。

  为何常常见到include .h文件而不是include .c文件?或者说include是否是就是为包含.h文件设定的语法?这个问题的答案偶不知道,没有见有文档记载、说明这个问题。不过从语法角度讲,include的意思就是从当前位置包含另一个文件,就象宏替换同样把当前行用另一个文件的整个内容替换掉。

  从这点讲,include .c文件是可行的,c编译器彻底可以正常处理。可是为何不常见include .c文件?从设计角度上讲,源代码区分为.h和.c文件,是为了接口与实现的分离,实际上二者没什么本质的差异。.h文件提供接口,.c文件提供具体的实现,二者能够一一对应,也能够不一一对应,没有强制要求。一个.c文件作为一个模块的实现,有可能要跟其余的模块打交道,这个时候就须要include其余模块的接口(其余模块的.h文件);而包含其余模块的实现(.c文件)是没有意义的、危险的。
因此,咱们不该该在项目中include .c文件,这样使用者出于直觉很难想到这里会有问题,增长了排错的难度。前几天偶移植一个国际知名大公司的代码就遇到了这个问题,耗费了半天的时间查看了所有的源码和makefile才发现了这个不常见编译现象。固然,那个公司的代码之因此这么作,是他认为这些代码已经很成熟了,不须要修改和反复从新编译。但它的作法确实对个人调试形成了很大的障碍。

----- GNU Make Document 中的相关章节 -----函数

4.12 自动生成依赖
  在为一个程序编写的makefile文件中,经常须要写许多仅仅是说明一些OBJ文件依靠头文件的规则。例如,若是‘main.c’经过一条#include语句使用‘defs.h’,您须要写入下的规则:
    main.o: defs.h
  您须要这条规则让make知道若是‘defs.h’一旦改变必须从新构造‘main.o’。由此您能够明白对于一个较大的程序您须要在makefile文件中写不少这样的规则。并且一旦添加或去掉一条#include语句您必须十分当心地更改makefile文件。
  为避免这种烦恼,现代C编译器根据原程序中的#include语句能够为您编写这些规则。若是须要使用这种功能,一般可在编译源程序时加入‘-M’开关,例如,下面的命令:
  cc -M main.c
  产生以下输出:
    main.o : main.c defs.h
  这样您就没必要再亲自写这些规则,编译器能够为您完成这些工做。
  注意,因为在makefile文件中说起构造‘main.o’,所以‘main.o’将永远不会被隐含规则认为是中间文件而进行搜寻,这同时意味着make不会在使用它以后自动删除它;参阅隐含规则链。
  对于旧版的make程序,经过一个请求命令,如‘make depend’,利用编译器的特色生成依赖是传统的习惯。这些命令将产生一个‘depend’文件,该文件包含全部自动生成的依赖;而后makefile 文件可使用include命令将它们读入(参阅包含其它makefile文件)。
  在GNU make中,从新构造makefile文件的特色使这个惯例成为了过期的东西――您永远没必要具体告诉make从新生成依赖,由于GNU make老是从新构造任何过期的makefile文件。参阅Makefile文件的从新生成的过程。
  咱们推荐使用自动生成依赖的习惯是把makefile文件和源程序文件一一对应起来。如,对每个源程序文件‘name.c’有一名为‘name.d’的 makefile文件和它对应,该makefile文件中列出了名为‘name.o’的OBJ文件所依赖的文件。这种方式的优势是仅在源程序文件改变的状况下才有必要从新扫描生成新的依赖。
  这里有一个根据C语言源程序‘name.c’生成名为‘name.d’依赖文件的格式规则:
    %.d: %.c
          set -e; $(CC) -M $(CPPFLAGS) $< \
                  | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
                [ -s $@ ] || rm -f $@
  关于定义格式规则的信息参阅定义与从新定义格式规则。‘-e’开关是告诉shell若是$(CC)命令运行失败(非零状态退出)当即退出。正常状况下,shell退出时带有最后一个命令在管道中的状态(sed),所以make不能注意到编译器产生的非零状态。
  对于GNU C编译器您可使用‘-MM’开关代替‘-M’,这是省略了有关系统头文件的依赖。详细内容参阅《GNU CC使用手册》中控制预处理选项。
  命令Sed的做用是翻译(例如):
    main.o : main.c defs.h
    到:
    main.o main.d : main.c defs.h
  这使每个‘.d’文件和与之对应的‘.o’文件依靠相同的源程序文件和头文件,据此,Make能够知道若是任一个源程序文件和头文件发生变化,则必须从新构造依赖文件。
  一旦您定义了从新构造‘.d’文件的规则,您可使用使用include命令直接将它们读入,(参阅包含其它makefile文件),例如:
    sources = foo.c bar.c
    include $(sources:.c=.d)
  (这个例子中使用一个代替变量参照从源程序文件列表‘foo.c bar.c'翻译到依赖文件列表‘foo.d bar.d'。详细内容参阅替换引用。)因此,‘.d’的makefile文件和其它makefile文件同样,即便没用您的任何进一步的指令,make 一样会在必要的时候从新构建它们。参阅Makefile文件的从新生成过程。命令行

 

  GNU Make手册的开头就说出了不少人不知道的知识——它不只仅用于编译的:p 
----- GNU Make Document 中的相关章节 ----- 
  GNU Make符合IEEE Standard 1003.2-1992 (POSIX.2) 6.2章节的规定。 
  由于C语言程序更具备表明性,因此咱们的例子基于C语言程序,但Make并非仅仅可以处理C语言程序,它能够处理那些编译器可以在Shell命令下运行的的各类语言的程序。事实上,GNU Make不只仅限于程序,它能够适用于任何若是一些文件变化致使另一些文件必须更新的任务。 
  若是要使用Make,必须先写一个称为Makefile的文件,该文件描述程序中各个文件之间的相互关系,而且提供每个文件的更新命令。在一个程序中,可执行程序文件的更新依靠OBJ文件,而OBJ文件是由源文件编译得来的。 
  一旦合适的Makefile文件存在,每次更改一些源文件,在shell命令下简单的键入: 
  make 
  就能执行全部的必要的从新编译任务。Make程序根据Makefile文件中的数据和每一个文件更改的时间戳决定哪些文件须要更新。对于这些须要更新的文件,Make基于Makefile文件发布命令进行更新,进行更新的方式由提供的命令行参数控制。

  这属于滥用#include. 

  为什么要分为头文件和源文件?原本就是为了传递以下信息: 
  类型定义 
  外部函数原型 
  外部变量 
  宏 

  这部分不产生任何实际代码的东西。 
  c各文件之间的代码是经过#include来引入的吗?这属于链接器的工做! 

  怀疑使用这个的人,也许是由于源文件的代码太长了,就滥用#include,将源代码分到几个文件上。 

  我估计将实现这种招数的办法是将源代码划分为一个主多个从的关系,主的引入全部从文件。 

  他们的内容有以下规定: 
  主文件,包括全部外部链接的代码(公共) 
  从文件,全部元素具有内部链接(私有) 
  对于客户端来讲,只有主文件才是他们真正去要链接的。 
  而由于从文件都是内部链接,相关的.obj将毫无疑义的被丢弃,由于主文件有着如出一辙的拷贝。 

  固然更蹩脚的办法是将这些#include了的源文件排除出项目定义文件。翻译

相关文章
相关标签/搜索