GCC与Makefile
2026/9/8 16:07:51 网站建设 项目流程

一个 C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)等 4 步才能变成可执行文件。

我们可以通过不同的gcc选项控制这些过程:

在实际使用的过程中只需要了解以下常用的选项即可:
实操

通过这条命令就可以将.c文件转化为可执行文件,其中会经过预处理、编译、汇编和链接

gcc -o hello hello.c

然后我们就可以直接执行

./hello

gcc指令可以完整的执行全过程,我们可以通过下面的指令来查看这些步骤

gcc -o hello hello.c -v

当然我们也可以手工执行以下命令体验一下

gcc -E -o hello.i hello.c gcc -S -o hello.s hello.i gcc -c -o hello.o hello.s gcc -o hello hello.o

Makefile的使用

在 Linux 中使用 make 命令来编译程序,特别是大程序;而 make 命令所执行的动作依赖于 Makefile 文件。最简单的 Makefile 文件如下:

hello: hello.c gcc -o hello hello.c clean: rm -f hello

make 命令根据文件更新的时间戳来决定哪些文件需要重新编译,这使得可以避免编译已经编译过的、没有变化的程序,相比较使用gcc将所有的文件都进行编译,make可以大大提高编译效率。

因此我们可以编写Makefile 文件来制定规则其样式如下:

目标(target)…: 依赖(prerequiries)… <tab>命令(command)
命令被执行的2个条件:依赖文件比目标文件,或是目标文件还没生成

实操

a.c文件: #include <stdio.h> int main() { func_b(); return 0; } b.c文件: #include <stdio.h> void func_b() { printf("this is b\n"); } Makefile文件: test: a.o b.o gcc -o test a.o b.o a.o: a.c gcc -c -o a.o a.c b.o: b.c gcc -c -o b.o b.c

通配符

但是如果每个文件都写入Makefile当中,显然是很麻烦的,因此,我们可以使用通配符或者其他方式来简化

%.o :通配符

$@:表示目标

$<:表示第一个依赖文件

$^:表示所有依赖文件

我们再加入一个c.c文件,和b.c几乎一致,同时修改Makefile文件:

test: a.o b.o c.o gcc -o test $^ %.o: %.c gcc -c -o $@ $<

同时我们还可以加入clean,其中.PHONY是假想目标的意思

test: a.o b.o c.o gcc -o test $^ %.o: %.c gcc -c -o $@ $< clean: rm *.o test .PHONY: clean

及时变量和延时变量

在 GNU make 中对变量的赋值有两种方式:延时变量、立即变量。上面语句中,变量 A 是延时变量,它的值在使用时才展开、才确定。

A = xxx // 延时变量 B ?= xxx // 延时变量,只有第一次定义时赋值才成功;如果曾定义过,此赋值无效 C := xxx // 立即变量 D += yyy // 如果 D 在前面是延时变量,那么现在它还是延时变量;

常用函数

$(foreach var,list,text) //在text中取出符合patten格式的值 $(filter pattern..., text) //在text中取出不符合patten格式的值 $(filter-out pattern..., text) $(wildcard pattern) //pattern定义了文件名的格式,wildcard取出其中存在的文件 $(patsubst pattern,replacement,text) //寻找`text’中符合格式`pattern’的字,用`replacement’替换 它们。`pattern’和`replacement’中可以使用通配符。

示例

A = a b c B = $(foreach f, $(A), $(f).o) all: @echo B = $(B) 输出:B = a.o b.o c.o C = a b c d/ D = $(filter %/, $(C)) E = $(filter-out %/, $(C)) all: @echo D = $(D) @echo E = $(E) 输出: D = d/ E = a b c

Makefile实例

objs := main.o sub.o test : $(objs) gcc -o test $^ # 需要判断是否存在依赖文件 # .main.o.d .sub.o.d dep_files := $(foreach f, $(objs), .$(f).d) dep_files := $(wildcard $(dep_files)) # 把依赖文件包含进来 ifneq ($(dep_files),) include $(dep_files) endif %.o : %.c gcc -Wp,-MD,.$@.d -c -o $@ $< clean: rm *.o test -f distclean: rm $(dep_files) *.o test -f

文件IO

Linux上操作文件时,有两套函数:标准IO、系统调用IO。标准IO的 相关函数是:fopen/ fread/ fwrite/ fseek/fflush/fclose等。系统调用IO 的相关函数是:open/ read/ write/ lseek/ fsync/ close
标准IO的内部,会分配一个用户空间的buffer,读写操作先经过这个 buffer。在有必要时,才会调用底下的系统调用IO向内核发起操作。
所以:标准IO效率更高;但是要访问驱动程序时就不能使用标准IO,而是使用系统调用 IO
文件的IO操作是比较重要的,下面仅给出open/ read/ write的相关说明:
注:尽管使用open函数创建文件时可以规定读写权限,但其最终结果为(mode & ~umask)

实操:

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> int read_line (int fd, char *buffer) { int len = 0; while(1) { char ch; int re = read(fd, &ch, 1); if(re <= 0) { break; } if(ch != '\n' && ch != '\r') { buffer[len++] = ch; } else { //read(fd, &ch, 1); break; } } buffer[len] = '\0'; // Null-terminate the string return len; } void process_data(const char *input, char *output) { char name[100]; int scores[3]; int sum; char *levels[] = {"A+", "A", "B"}; int level; if (input[0] == 0xef) /* 对于UTF-8编码的文件,它的前3个字符是0xef 0xbb 0xbf */ { strcpy(output, input); } else { sscanf(input, "%[^,],%d,%d,%d,", name, &scores[0], &scores[1], &scores[2]); //printf("result: %s,%d,%d,%d\n\r", name, scores[0], scores[1], scores[2]); //printf("result: %s --->get name---> %s\n\r", input, name); sum = scores[0] + scores[1] + scores[2]; if (sum >= 270) level = 0; else if (sum >= 240) level = 1; else level = 2; sprintf(output, "%s,%d,%d,%d,%d,%s", name, scores[0], scores[1], scores[2], sum, levels[level]); //printf("result: %s", result_buf); } } int main(int argc, char **argv) { int fd_data, fd_result; if(argc != 3) { printf("err"); return 1; } fd_data = open(argv[1], O_RDONLY); if(fd_data < 0) { printf("err"); return 1; } fd_result = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); if(fd_result < 0) { printf("err"); return 1; } while(1) { // Read data from the input file char buffer[100]; int len = read_line(fd_data, buffer); if(len <= 0) { break; } else { //printf("Read line: %s\n", buffer); // process the data char processed_data[100]; process_data(buffer, processed_data); // Write the processed data to the output file write(fd_result, processed_data, strlen(processed_data)); write(fd_result, "\r\n", 2); // Add a newline after each processed line } } close(fd_data); close(fd_result); return 0; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询