一些新手搞不清楚工程,和源代码,C文件,头文件的区别。这里特意为新手说明一下:
不管你是否写过程序。你从用过软件。你会发现不多一个软件就一个文件。你能够在window下看一下某个具体软件的位置,并在这个位置打开文件夹,会发现 有不少文件。从设计软件或程序的开发角度也是同样的,一个程序不少状况下,除非足够简单,你只用一个C文件便可。例如:
1 |
int main( int argc, char *argv[]){ |
这个文件你甚至都不须要#include <stdio.h>。函数
但若是你想打印个信息在屏幕上,如”hello world",则你须要调用别人帮你作好的库函数,例如printf,而此时,虽然即使你仍是一个C文件能够生成程序,可是你已经用到另外两个文件。一个 是头文件 stdio.h。一个是存放printf设计实现的库函数。他们在哪这不是目前关系的,但至少你的设计中包含了不止一个东西。此时,你没法用一个源代码 (如你上面本身写的那个C文件)来描述你操做的范围。此时这个总体就是工程。工程是个抽象的名词,和系统同样,很抽象。学习
须要说明,文档组织形式,没有什么国际标准。每每本身习惯的,就是最好的(基于这种习惯能保证你的工做效率)。这里先介绍一下我喜欢的工程组织形式,一个目录下至少存在这样几个子目录。
src ; inc ; doc ;obj;bin
必要时,还须要有asm,output,input,lib
src,很简单,对应里面存在的是c文件。和inc分离,是由于可能存在asm,或非C代码的程序文本,非C代码的程序文本独立使用一个目录组织,但C的 编译器使用外部其余语言编写的模块时,仍是须要接口说明,这些说明每每也是放在.h里。所以分割为src和inc,这是个好注意,不是我发明的。
inc,里面存放的是.h文件,或其余预编译时有效的文件
doc,里面存放的是说明文档。文档就是文档。和工程的模块编译执行,毛关系没有。你大能够在转移工程内容时,脱离掉doc。分类出一个doc目录,方便 实际查询资料和代码管理。例如版本控制程序,多半是用递增方式维护的,虽然有不一样版本,可是是将两个版本的差别进行保存。而文档,一般是流水方式,依次记 录过程当中对代码的调整历史,这类资料独立出一个目录是有必要的。
obj,里面存放的是.o文件,这个不用说了,
是多么的方便,若是你但愿rebuild all的时候。
bin,里面存放的是静态库,动态库,执行文件,准确说是链接器的输出文件存放的地方,一般为了方便寻找到你折腾半天所想要的最终生成的东西。例如你编译 了半天,但愿结果的文件能远程加载到SD卡里,open一个bin的专门目录,比在一堆文件中求索哪一个才是你须要的要方便不少。固然这里的所谓库,都是由 当前这个总目录下生成的库文件,而不是别人给你的,别人给你的,一般建议,你增长个lib目录,或增长环境配置,这另谈。这里须要补充纠正一个观点,静态 库,并非实际的链接工做。其实是个归档的工做。将一堆obj归档到一个文件中。一般使用ar命令。但广义的说链接,这种归档整理的工做也算是一个,这 是野鬼版的胡搅蛮缠。
asm,里面都是汇编源码文件,实际是不少须要汇编实现的函数保存的文件。大多数人不须要。但个人工做常常用到手写汇编没办法,单片机,ARM,DSP都折腾过工程级的开发,因此没办法,习惯了src,asm分离。
input,output,一个里面存放的是程序执行时的输入资料,一个存放的是程序执行时的输出资料。这个在不少工程里,并不存在,只是个人工做常常遇 到大批量的输入数据资料,来验证代码的正确性和有效性。此时将输入输出归到子目录里,只是方便处理而已。同时,对于程序设计而言,也比较好规范出一个习 惯,对输入输出的接口有个比较规范的组织形式。否则,代码执行,老是在当前目标下查找资料,或者在环境变量path下查找,毕竟不能解决全部问题。
针对learn_make,咱们开始作以下调整。 ui
至少先这4个。能简单的别复杂了。
这里会提示说,有些是目录,没法删除。这是好事情,咱们须要将learn_make目录下的文件删除干净,目录不动。
将来每一个文件都会有对应当前目录下的具体存放位置,经过我上面的理由作出的约束规范。后期这有个好处,C语言和其余语言同样,但愿代码可以复用,尽量的 让模块能够在不一样工程里均有效实现。而C语言的目标是一次编写,处处编译(别迷信“一次编译,处处运行”这种广告用语,和街头的“老军医”没什么区别),spa
所以C语言但愿C的源代码,按照C的文本文件为最小单元,可以在不一样工程里复用,所以经过目录,子目录的方式,能够有效提升操做者后期代码文本文件的组织能力(其实就是源代码,但但愿能增强新手对源代码的认知,它只是个按照语言规则书写的文本文件)。设计
我说了,我不期望个人资料能够提升新学者的智商;个人目的是,提升新学者管理组织工程的能力,使得当工程规模变大时,新学者能够相对其余人更好的驾驭工程。在你智商不明显高出竞争者时,你提升复琐事务的控制能力,是一个击败对手的比较可行的办法。版本控制
如今咱们在learn_make下,运行gcc,看看有多少累。先说下,为何在learn_make目录下执行gcc。由于除了这,你在那执行都麻烦。 你要在src里,当你的输出和引用头文件,都须要退出本目录,到上一级目录后,再进入对应子目录,既然一堆孩子都争吵但愿gcc在本身目录下执行,那干脆 一碗水端平,老爸本身占便宜得了。
哈,这里确定有个错误,由于learn_make.h咱们移动到inc目录下了,说错误,是但愿引出一个gcc的参数,-Idirectory。
同时也建议新手,就是错误,也要执行一次,当屏幕上,先存在错误提示,后出现正确提示时,这种差别,会刺激你的大脑,快速的记住知识。想迅速快速入门,那么最好你就不停的跳进个人坑里,看看坑是什么模样的,之后才有认识真正的陷阱的能力。
须要补充说明的是,gcc对参数是大小写区分的,-c和-C是不同的,-i和-Idirectory也是不同的。这里我特地使用了gcc参考资料的描 述,而没有使用 -I的描述是但愿新学者注意,目录名和I是没有空格的。他们组成了一个新单词。此处小写的directory是抽象的描述你但愿表达的目录。若是你存在多 个目录里面都有头文件须要引用,假设当前目录下,存在inc1, inc2两个放头文件的子目录,那么你须要 -Iinc1 -Iinc2。 code
1 |
$gcc -Iinc src/learn_make.c |
注意下,如今显示的是/learn_make目录。
一切又正常了。但没有达到咱们的要求。由于当前目录下,有了个a.out。咱们但愿a.out存放在bin下。你彻底能够继续这样执行。
2 |
$bin/a.out //此处是调用bin下的a.out执行。 |
但确实够“弱智”的。能够考虑修改一下特定输出目标。
1 |
$gcc -Iinc src/learn_make.c -o bin/learn_make |
很爽吧,当前目录下,很干净,并且各自文件都纳入到本身的位置。
可是生成的.o文件,是值得保留的。这样能够提升效率。所以咱们须要继续调整方法
1 |
$gcc -Iinc -c src/learn_make.c -o obj/learn_make.o |
记得虽然你指定了目标文件,learn_make.o。可是若是没有-c,
gcc会生成一个learn_make.o的可执行文件(这包含了连接的动做),而不是你想要的obj。
有些所谓高手说我这是废话,资料上不是说的清清楚楚吗?可是就是这么清楚,这样笔误仍然会致使一个.o的出现,后果很严重。这是个很大的“陷阱”因此我要反复弱智的说一下。 接口
1 |
$gcc obj/learn_make.o -o bin/learn_make |
这里不须要 -Iinc了。由于须要inc目录是由于你的c文件须要#include "learn_make.h",在预处理时,须要在合适的位置(目录里)找到对应头文件,此时是对obj的链接工做,和头文件已经没有任何关系了。
若是你如今尚未欲望学习make的话,不妨修改一下,learn_make.c的以下语句
02 |
#include "learn_make.h" |
03 |
#include "learn_make1.h" |
04 |
#include "learn_make2.h" |
05 |
#include "learn_make3.h" |
06 |
int main( int argc, char *argv[]) |
07 |
printf ( "%s\n%s\n%s\n%s\n" ,\ |
08 |
TEST_GCC_CMD,TEST_GCC_CMD1,TEST_GCC_CMD2,TEST_GCC_CMD3); |
12 |
//以上是src/learn_make.c的文件 |
13 |
//如下是learn_make/inc1/learn_make1.h的内容 |
14 |
#ifndef _LEARN_MAKE_1_H_ |
15 |
#define _LEARN_MAKE_1_H_ |
16 |
#define TEST_GCC_CMD1 "test gcc cmd1" |
17 |
#endif// _LEARN_MAKE_1_H_ |
18 |
//一样,你在learn_make/inc2 ;learn_make/inc3里也建立相似的头文件,<code>方便主函数调用我不一一展开</code> |
如上,相似learn_make.h的方式,编辑learn_make1.h, learn_make2.h, learn_make3.h, 分别存放到learn_make/下的新目录inc1,inc2,inc3下。
你为了保障你的编译能够经过,你得这写。
$gcc -Iinc -Iinc1 -Iinc2 -Iinc3 -c src/learn_make.c -o obj/learn_make.o
“很长,很暴力”。新手会说“你弱智啊”,为何要搞这么多inc 目录。恩,这个例子很弱智,但表现了一些大型工程中会遇到的典型问题。事务
大型工程,首先是分组实现的,不一样组的代码会在不一样目录下,这样方便管理。同时你可能引用第三方的库,须要依赖第三方的头文件。但这些头文件你都不方便放到glibc库的头文件或系统库的头文件目录下,所以你不能用开发
的方式实现。你也不可能由于目录的不一样用绝对路径实现例如
1 |
#include "/usr/src/learn_make/inc/learn_make.h" |
这是很不开源的作法。这样意味着,使用你代码的人必须重现你的所有工做环境。你可能会说“关我屁事!”,惋惜这我的说不定就是你本身呢。
因此C语言设计,千万不要将文件的路径问题,在代码中固化下来。与人与己都是有利的。相对路径会好点,例如,你也潜规则你们,默认C文件在src下,头文件在inc下,并且src和inc在同一个目录下,则能够
1 |
#include "../inc/learn_make.h" |
但这个问题治标不治本,同时不方便后期的源代码管理。因此仍是老实的用-Idirectory吧。废话这么多,是但愿把你说晕了,好跳到个人坑里,当我把你说晕,必定要用-I时,实际上,我已经挖好了make的坑,并且你已经打算主动跳进去了。