C语言多模块程序及Makefile那些子事儿

一、多模块程序

(1)

1、预处理 gcc -E 源文件
将源文件当中的预处理指令替换为对应的内容

2、编译 gcc -S 源文件 或 前一步生成的预处理文件
将预处理后的文件转换成对应的汇编文件,此时会生成一个 . s 结尾的汇编文件

3、汇编 gcc -c 源文件 或 前一步生成的汇编文件
将汇编文件转换为目标文件(二进制码文件)此时会生成一个 . o 结尾的目标文件

4、链接 gcc 源文件 或 前一步生成的多个目标文件
将前一步生成的目标文件和系统的库(静态库或动态库)文件合并生成为可执行文件
一般来说,在这一步,是系统当中的链接器进行操作的,只不过gcc当中融合了链接器

5、运行
目录下的可执行文件的方式一般如果是当前目录 . / 否则会将其当做系统指令运行

编译期出的错误,一般为c语言语法性的错误
链接期的错误,一般为找不到实际调用的错误
运行期错误,(1)会导致程序奔溃的错误一般是由于不符合系统处理的错误(2)数据型错误

(2)

在刚开始学习C语言当程序只有一个【main.c】文件时,可以在命令行中通过【gcc -o program main. c】对单个代码文件进行编译,生成可执行文件 【program】,并且通过【. / program】运行编译生成的程序。

相比于单个文件、单一功能的程序,当程序有多个模块时,问题就开始变得复杂了。对毎一个模块会首先编译出毎个模块对应的【* . o】目标代码文件。例如【gcc -c -o set.o set.c】会将一个【set.c】文件编译成一个【set.o】的目标代码文件。这里的【-c】表示生成目标代码文件。【-o】与之前单文件的时候一样,在它之后会写明被生成的文件的名称。

当完成了每一个独立模块的编译并获得它们的目标代码文件后,可以将程序的目标代码文件与他们链接在一起。
例如 【gcc -o program main. o set.o others.o】。将目标代码文件【set.o】和 【others.o】与【main.o】链接在一起,并且输出可执行文件 【program】。我们依然可以通过【. / program】运行编译生成的程序。

(3)

将一个程序写在多个文件中时,每一个文件中的变量和函数默认都是只有文件内的部分才可以访问的。但是有一些特殊的全局变量、类型定义、函数可能会需要在多个文件中被使用。这时候可以将这类的内容单独写成一个头文件,并且将全局变量、类型定义、函数声明写到头文件中。对于一个文件【set.c】,习惯上它的头文件会被命名为【set.h】。在所有需要用【set.h】中全局变量、类型定义、声明的函数的文件中,用#include “set.h”将对应的头文件引入。在这里的引入头文件方式和引入系统库头文件的方式很类似,只不过这里用的是引号 “ ” 而不是尖括号< >

由于头文件里也可以引入头文件,因此可能事实上多次引入同一个文件,比如引入1.h和2h,且1.h也引入2.h,这时因为2.h被引入了两次,就有可能出现重复的声明。

为了解决这个问题,在2.h中定义个宏,在2.h的最开始判断这个宏是否被定义过,如果被定义过,就跳过2.h整个文件的内容。

这里将会用到两个预处理指令# ifndef xxx和#endif,它们成对出现且# ifndef 在前,作用是如果这时并未已定义xx宏,则这对# ifndef xxx,# endif之间的内容有效。(其中xx可以替换为任意宏名)
这样2.h可以写为类似于如下的内容
在这里插入图片描述
看上图可以发现,如果在程序中尚未引入2.h的位置定义了xx宏,则# include"2.h"中的声明并不会被引入,因此不应该在此使用xx这种平凡的名字。实际上,一般会采用一个与头文件名相关的名字来代替xx,比如一个常用的代码风格里,这个宏的名字开式为:工程名_路径名_文件名_H_。

(4)

1、某一代码中定义的函数如果需要被其他代码文件所使用,应该将函数的声明放入头文件,并在其他代码文件中引入这一头文件(对)
通过这种方式可以在其它代码文件中使用对应的函数。在连接后程序才可以正常的使用。
2、#include可以被用于引入系统库头文件也可以被用于引入自己实现的头文件(对)
对的,只不过在引入系统库头文件时,往往会使用尖括号<>,而在引入自己实现的头文件时一般用 “ ”
3、用gcc时,-c之后写明的是生成可执行文件的名称。(错)
-o之后写的是生成可执行文件的名称。-c的参数的使用会帮助得到一个对象文件
4、多模块的程序一定要有头文件(错)
可以只有多个.c的文件,也并不一定非要都拆出.h文件

二、makefile

一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。

为了解决所遇到的问题,方便开发,我们使用一个叫做make的命令,它可以读取Makefile文件,并且根据Nakefile中的规则描述把源文件生成为可执行的程序文件。

最基本的Makefile中包含了一系列形式如下的规则。请注意,每—条规则的命令前,必须要有一个制表符\t。

目标:依赖1 依赖2 ...
     命令

例如,可以写一条规则:

array.o: array.c array.h
gcc -c -o array.o array.c

表示生成的文件是目标代码文件array.o ,它依赖于array.c和array.h。
当在命令行中执行make array.o时,根据这—规则,如果array.o不存在或者array.c 与array.h至少之一比array.o更新,就会执行gcc -c -o array.o array.c .

把上述代码保存为Makefile,与array.c和 array.h放在同一目录,在那个目录里执行make array.o就能看到效果。

注意:Makefile里的除当前目录隐藏文件外的第一个目标会成为运行make不指定目标时的默认目标。

main: array.o main.o2
      gcc -o main array.o main.o

main.o: main.c array.h
      gcc -c -o main.o main.c

array.o: array .c array.h
      gcc -c -o array.o array.c

在Makefile有多条规则时,如果希望只生成其中一个,可以在make命令后加上需要生成的目标的名称。例如,在这里可以执行make main.o 、make array.o或 make main。当执行make main时,make命令发现array.o和main.o不存在,就会根据以它们为目标的规则先生成它们。

很多时候,你会需要将.o为后缀的目标代码文件和可执行的程序文件删除,完全从头进行编译。那么可以写—条clean规则,例如:

clean:
     rm -f array.o main.o main

rm命令表示删除文件,-f表示强制,因此rm -f array.o main.o main按照预期,当执行make clean就可以删除array. o 、 main.o和main了。
事实真的这样吗?
其实这时如果已经存在clean文件,rm命令就不会执行了。为了解决这个问题,通过一个特殊的方法告诉make这个名为clean的规则在clean存在的时候仍然有效。

.PHONY : clean

clean:
     rm -f array.o main.o main

.PHONY用于声明—些伪目标,伪目标与普通的目标的主要区别是伪目标不会被检查是否存在于文件系统中而默认不存在且不会应用默认规则生成它。

在Makefile中还可以使用它的变量和注释。

# 井号开头的行是一个注释
# 设置c语言的编译器
CC = gcc

# -g 增加调试信息
# -wall 打开大部分警告信息
CFLAGS = -g -wall

# 整理一下main依赖哪些目标文件
MAINOBJS = main.o array.o

.PHONY: clean

main: $(MAINOBJS)
    $(CC) $(CFLAGS) -o main $(MAINOBJS)

array.o: array.c array.h18
    $(CC) $(CFLAGS) -c -o array.o array.c

main.o: main.c array.h
    $(CC) $(CFLAGS) -c -o main.o main.c

clean :
    rm -f $(MAINOBJS) main

上面这个例子已经是一个较为完整的Makefile 了。以#开头的是注释,在这里用注释说明了定义的 Makefile变量的用途。cc变量定义了编译器,CFLAGS变量标记了编译参数,MAINOBS变量记录了main依赖的目标文件。定义的变量可以直接通过$(变量名)进行使用。

1、make命令根据Makefile 完成的工作通过一条条输别的命令也能完成。(对)
Makefile其实描述了一系列转为对象文件、联编的过程,不使用make也是可以完成的。
2、Makefile中的变量就是C语言里的变量。(错)
C语言里面的变量不用$()的方式来用
3、Makefile可以包含多个规则。(对)
一个Makefile可以包含多个规则,既可以每次在make后说明执行哪个功能,也可以通过定义的all来执行一系列的规则。
4、在用gcc编译时加上-wall才会显示错误信息。(错)
-wall是用于显示大部分警告信息的,编译错误信息默认就会显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值