gcc编译过程示例2.0版本

学习过数据结构的同学应该知道,当我们利用C语言创建一种数据对象(结构)的时候,比如链表,二叉树,矩阵等, 往往需要建立两个文件:.c文件和.h文件. 其中头文件.h存放结构的声明及对数据结构操作(函数)的声明, 源文件.c存放函数实体, 从而实现如下数据的功能:

1. 结构的生成;
2. 结构的销毁;
3. 在结构中查找满足规定条件的数据元素;
4. 在结构中插入新的数据元素;
5. 删除结构中已经存在的数据元素;
6. 遍历.

在具体问题中某种数据的标准操作可能不能满足需要, 往往要根据具体情况更改或增加一些新的操作, 那么问题来了, 在一个稍大点的工程中如何利用这两个文件定义的数据类型和算法, 还有怎样增加一些自定义的操作而不更改原来的两个文件? 这里给出一个在gcc下建立一个包含新的.c和.h文件的例子, 在原来的数据结构的基础上增加一些自定义操作. 这个示例所需要的源文件如下所示:

.
├── include        /* head files   */
│       ├── lib.h        /* 结构及操作的声明 */
│       └── my.h        /* 新增的操作声明  */
├── makefile       /* makefile文件  */
├── program.c       /* 测试主程序     */
|── source       /* source files */
 |       ├── lib.c        /* 结构的操作(函数) */
 |       └── my.c        /* 新增的函数      */

为简洁起见, 我们在lib.h和lib.c中定义一个复数类型, 且只有一个计算两个复数和的函数, 内容如下:

#define NUM_NUM 100

typedef struct {
double re;
double im;
}complex;

complex sum_complex(complex z1, complex z2);
#include "lib.h"

complex sum_complex(complex z1, complex z2){
complex z;
z.re=z1.re+z2.re;
z.im = z1.im+z2.im;
return z;
}

现在在上面的基础上增加一个计算复数的模的函数, 两样地建立相应的头文件, 就是文件my.h和my.c:

/* #include "lib.h"  这条语句不需要!
*/
double modu(complex z);
#include "lib.h"      /* 必须的语句 */
#include <math.h>;

double modu(complex z){
double m;
m = sqrt(z.im*z.im+z.re*z.re);
return m;
}

最后在测试函数program.c中定义复数并调用上面的两个函数, 其内容如下:

#include <stdio.h>
#include <stdlib.h>
#include "lib.h"
#include  "my.h"

int main(){
complex z1,z2,z;
z1.re = 1.0;
z1.im = 1.0;
z2.re = 2.0;
z2.im = 2.0;
/* compute the sum of the complex z1 and z2. */
z=sum_complex(z1,z2);
printf("z1=(%f,%f), z2=(%f,%f)\n",z1.re,z1.im,z2.re,z2.im);
printf("z = z1 + z2 =(%f,%f)\n",z.re,z.im);
/* compute the module of complex z. */
printf("The module of z is: %f\n", modu(z));

return 0;
}

示例工程已经建立好了, 接下来就要进行编译测试了. 这里要注意文件的存放位置, 以确保makefile文件正确执行.  我们按如下语句编译程序:

gcc  -g -Wall -ansi -I./include -c ./source/lib.c
gcc  -g -Wall -ansi -I./include -c ./source/my.c
gcc  -g -Wall -ansi -I./include -c program.c
gcc lib.o my.o program.o -lm -o program
./program

如果不出现错误的话,执行的结果应该是这样的:

z1=(1.000000,1.000000), z2=(2.000000,2.000000)
z = z1 + z2 =(3.000000,3.000000)
The module of z is: 4.242641

需要注意的地方
[1] 链接目标文件时的参数 -lm 在某些机器下不是必须的, 这个参数是告诉gcc链接到C标准库文件libmath.a. 在笔者的机器下如果没有-lm选项, 会出现如下错误:

my.o: In function `modu’:
undefined reference to `sqrt’
collect2: error: ld returned 1 exit status

[2] 要注意理解#include<lib.h>语句应该在什么地方出现, 或者说什么时候需要出现才合乎C语言的语法规则, 在实际应用中尽量少出错误. 比如, 文件my.h中就不能出现#imclude<lib.h>, 否则会出现结构体定义冲突, 读者可以自己试验一下.
[3] 现给出笔者自己的makefile文件以供参考:

CC = gcc
CFLAGS = -g -Wall -ansi

OBJS =  lib.o my.o program.o
LIB = -L /usr/local/lib/
LIBB = -lm
EXECNAME = program

$(EXECNAME) : $(OBJS)
$(CC) $(OBJS) $(LIBB) -o $(EXECNAME)
lib.o : ./source/lib.c
$(CC)  $(CFLAGS) -I./include -c ./source/lib.c
my.o : ./source/my.c
$(CC)  $(CFLAGS) -I./include -c ./source/my.c
program.o : program.c
$(CC)  $(CFLAGS) -I./include -c program.c

.PHONY : clean exe
exe :
./$(EXECNAME)

clean :
-rm -f $(OBJS);
-rm -f $(EXECNAME);
-rm -f *~