linux 下 C 编程和make的方法 (六:一个C模块的实现)

中山野鬼 发布于 2012/04/01 00:04
阅读 1K+
收藏 21
    我们就先做个tree 模块吧,这里说下讲TREE的理由,很简单,如下
    1、tree相对复杂点,我就说个堆栈,恐怕可以展开的东西不多。
    2、正在把文件生命周期管理的中间件进行开源。正好用tree开刀。
    3、tree好啊,文件夹里大把天然的测试数据,我省得造数据的麻烦了。
    4、不是菜鸟经常被别人说“去看内核源码”,那好吧,既然你们总被喷,不如咱们也从tree展开,等到最后,听完,也算对linux内核的vfs有所了解。野鬼不带新手逛点带彩的地方,怎么能算“成年”呢。你不自个逛那是因为你太害羞而已。
   
    这里谈一下tree的数据结构的一些基本特性。也就是啥是树。图论书籍中有非常科学严谨的解释。我这弱智的说说。什么算是一个树。
    1、一群个体,相互有血缘关系。血缘关系只有一种,父子关系。
    2、任何一个个体,只有一个老爸,且必须要有个老爸。
    就这么简单。有人说你胡扯,树的根,他就没有老爸。哈。不妨我反驳你,首先,树有个性质,从图论中说。就是加上任意条边,则构成环。当然自己指向自己的边这个不能算。因为这不是个朴素的图。如果自己指向自己的也算边,你的图可平面时,你就无法使用平面图上很多理论。
    我的定义:朴素图是,边是特指连接两个不同的顶点。就是说关系,仅指两个不同的个体之间的关联。
    如果这么说,那么对于一个树而言,只存在一个根的时候,就麻烦了。怎么加边呢?

    所以对于树而言,理论上是不存在只有一个顶点的情况。即便就是一个根,自然会出现两个顶点。只不过根指向一个没有任何意义的顶点,如同我们编写代码是,指针指向0,表示没有意义,但不代表这个0不存在。
    废话不多,我们开始上代码。不过上之前允许我再废话一下。我们要建目录了。执行如下命令:
$mkdir data_struct
$cd data_struct
$mkdir inc
$mkdir src
$mkdir bin
$mkdir obj

    为什么是data_struct,模块化的思想啦,很多结构,结构套结构的。我一层层的包起来,当面向对象啊?所以干脆data_struct库吧。以后涉及抽象数据结构的,都装里面。当然为了说明学习C而涉及。如果linux下有类似模块,我们本着 绝不重复开发的原则,还是坚持用别人已经开放的模块使用。
$cd src
$:>ds_tree.c
$scribes ds_tree.c //为啥用这个名字,还好还好,google一下这个名字没有太多信息,省得与其他库C文件名冲突
如下编辑
/***************
src/ds_tree.c
 by luckystar
 ***************/
static int ds_tree_flag =0;

void ds_tree_init(void){
    if (ds_tree_flag) {
        //log("module inited..",X);
        return;
    }
    ds_tree_flag = 1;
    //todo:module init...

}

void ds_tree_destory(void){
    if (!ds_tree_flag) {
        //log("module not inited..",X);
        return;
    }
    ds_tree_flag = 0;
    //todo:module destory...

}
保存退出,若问这是啥,别废话,敲你的键盘。
$cd ..\inc
$:>ds_tree.h
$sciribes ds_tree.h
如下编辑
#ifndef _ds_tree_H_
#define _ds_tree_H_

//ins_inc_file

//ins_typedef_def

//ins_def

//ins_func_declare


#endif //_ds_tree_H_
保存退出
$cd ..\src
$:>ds_tree.c
$scribes test_ds_tree_main.c //只是测试代码以后不用
如下编辑
#include <stdio.h>
#include "ds_tree.h"
//extern void ds_tree_init(void); //你可以尝试打开这个注释,看有什么变化,回答没有
int main(int argc,char*argv[]){
    ds_tree_init();
    ds_tree_destory();
    return 0;
}
保存退出
$cd ..
$:>Makefile
$scribes Makefile
如下编辑
.PNONY:build_lib
build_lib:bin/libds_tree.a
bin/libds_tree.a:obj/ds_tree.o
    ar -r bin/libds_tree.a obj/ds_tree.o
obj/ds_tree.o:src/ds_tree.c inc/ds_tree.h
    gcc -fpic -Iinc  -c src/ds_tree.c -o obj/ds_tree.o
   
.PNONY:test   
test:bin/test
bin/test:bin/libds_tree.a obj/test_ds_tree_main.o
    gcc obj/test_ds_tree_main.o -Lbin -lds_tree -o bin/test
#test
#    gcc  -Lbin -lds_tree obj/test_ds_tree_main.o -o bin/test
# you will not found func
obj/test_ds_tree_main.o:src/test_ds_tree_main.c inc/ds_tree.h
    gcc  -Iinc -c src/test_ds_tree_main.c  -o obj/test_ds_tree_main.o
   
.PNONY:clean
clean:
    -rm obj/*.o        
    -rm bin/*
保存退出//这里的makefile仍然弱智版的,我们不进化,一个章节只谈一个方向的问题。以后专门进化。
 
$make test
$./bin/test

 
看看出来啥东西。
OK。收工!!!

    估计有人要扁我了。"这和tree有个毛关系啊。啥代码都没有。"

    我的回答很简单。你如果是新手,记得上面的每个步骤,和第一次编译前,做且仅做的内容。那上面的事情都是啥?

    这叫“搭窝”,只有make过了,可以执行了,才表示你可以开发了。便便还要找蹲坑,找呢,当然我是说大的。记得第一次编译前,只做这么多。这叫"缩小开发反馈环大法"

由于咱们第一次讲linux下编写C程序,是和tree没关系,但和编写代码有关系。所以收工。当然收工前,提醒一下,关注以下内容并收集资料。

1、ar 命令,是用于将多个obj文件,归档到一个.a,也就是通常说的静态库的。
但起名字很关键。你要注意到make 里 bin/test的操作(还好是弱智版的,我要增加变量,就难解释了),这里有个 -Lbin ,这个意思是增加指定的库所在的路径。但这个-L只对-l(小写的L)有影响。 -l有什么用呢?和-Lbin组合起来就等于 bin/lib,他会把库文件前面 lib的前缀自动省去,同时某默认对.a文件而言,.a不需要书写,简化你的工作。当然你可以如下做
ar -r bin/ds_tree.a obj/ds_tree.o
这看起来很微软。其实无所谓。但是你对这个.a的使用,得如下
gcc obj/test_ds_tree_main.o bin/ds_tree.a -o bin/test
你需要给出全名。你可以试下
gcc obj/test_ds_tree_main.o bin/ds_tree -o bin/test
肯定不对啦。如果 bin/真有个ds_tree的文件怎么办?
估计有新手会说了,bin/ds_tree.a 明显比-Lbin -lds_tree 要少,我毛病要用你的-L -l的方式啊,还要在确保库是以lib打头的,如ds_tree的库为 libds_tree.a
哦。我没有意见,但你别忘了,每个库文件都有路径,每个库文件都有 .a要明确写出来。如果你有10个库文件,貌似 -L -l就更快了。这些先进的玩意是给大工程用的,如果你偏要用自己的库文件名的话,我没意见,反正(弱智版嘛)。
2、在ds_tree.c和Makefile里都有一些错误操作。你可以试试,体会下gcc的命令的原理。
3、对比下这个Makefile和前面的Makefile有什么不同了。显然不是少了 build all。为什么不把test,放在第一个。
4、哦,对了,最后说一下,最后的正确执行结果就是 啥结果都没有。不用吐,现在吐光了,后面还有更值得你要吐血的。
好,真收工了。
加载中
0
中山野鬼
中山野鬼
关于C代码为什么这么写,我后面再讨论,今天真的要收工了。
0
ddatsh
ddatsh

搞JAVA的太幸福了

没这堆事

0
中山野鬼
中山野鬼

引用来自“dd”的答案

搞JAVA的太幸福了

没这堆事

你灵活利用LINUX下的东西,会发现写C比JAVA要舒服很多还不用很多包,而且你永远不知道你是否学习到了足够的包的知识。
0
中山野鬼
中山野鬼
如果你COPY Makefile的代码时,需要注意TAB和顶头(依赖规则),我把目录全删重新生成代码时,Makefile丢失,再重网站上COPY下来,发现很累,大家姑且先讲究吧,后续会想办法找空间做成开源的项目。
0
林希
林希
我一直想搞好这些东西,可是没有时间,有时间的时候又去做别的事情了,好像我天生就怕这些东西一样
0
StormFour
StormFour

改成  gcc obj/test_ds_tree_main.o bin/ds_tree.a -o bin/test

反倒出错

$ make test
gcc -fpic -Iinc -c src/ds_tree.c -o obj/ds_tree.o
ar -r bin/libds_tree.a obj/ds_tree.o
ar: creating bin/libds_tree.a
gcc -Iinc -c src/test_ds_tree_main.c -o obj/test_ds_tree_main.o
gcc obj/test_ds_tree_main.o -Lbin -lds_tree.a -o bin/test
/usr/bin/ld: cannot find -lds_tree.a
collect2: ld returned 1 exit status

make: *** [bin/test] Error 1

什么原因

中山野鬼
中山野鬼
-lds_tree.a 如果正确,则证明文件是 libds_tree.a。你顶头的自然是错的。应该是 bin/libds_tree.a
0
asdfsx
asdfsx
感觉可以当做一个固定的工作流程了,hohoho
0
换大米
一个make就把整个程序模块梳理起来了,貌似前面野鬼提到过
0
luck3359
luck3359

$ make test
gcc -fpic -Iinc -c src/ds_tree.c -o obj/ds_tree.o
src/ds_tree.c:1:0: 警告:-fpic 在目标机上被忽略(所有代码都是与位置无关的)
ar -r bin/libds_tree.a obj/ds_tree.o
ar: creating bin/libds_tree.a
gcc -Iinc -c src/test_ds_tree_main.c -o obj/test_ds_tree_main.o
gcc obj/test_ds_tree_main.o -Lbin -lds_tree -o bin/test

 

返回顶部
顶部