简单了解C语言如何构建多文件项目
简单理解C语言如何构建工程
首先我们最好有一个好用的编辑器,vscode也好,visual studio也罢,dev也行,这里我们使用的是code::block;
一、了解C语言工程的构建原理
首先我在这里放一张图,让我们来看一下是这张图叙述了那些事情:

上图取自大名鼎鼎的csapp,它描述了一个C语言程序出生的步骤,我们在编译器中写好程序按下“构建”(或者编译或者make)按钮,最先作用的是预处理器,预处理器根据头文件把该连接的源文件连接到一起,然后准备编译,编译的过程是词法分析的过程,经过词法分析之后,程序编译成了汇编程序,如果我们打开汇编程序,我们会看到一系列寄存器操作,汇编语言是非常底层的语言,再之后汇编语言经过汇编就变成了*.o文件,o文件经过链接,就变成了可执行的、二进制的目标文件
下面呢,我们再来介绍一个东西,它叫做makefile,makefile呢,是gnu出品的自动化构建工程的工具,使用make命令就可以调用执行make文件(linux一切皆文件的思想嘛),make文件会根据当前目录下的makefile文件(没有后缀)来执行一系列终端命令,从而代替手工来完成程序的编译操作
[注]:如果你的工具链是mingw,那么相应的命令将是mingw32-make
,你可以去工具链的安装目录中找一下这个程序(这个程序就叫mingw32-make)
下面我们来介绍一个makefile的例子,

main:main.o abc.o xyz.o
gcc main.o abc.o xyz.o -o main
main.o:main.c abc.h xyz.h
gcc -c main.c –o main.o -g
abc.o:abc.c abc.h xyz.h
gcc -c abc.c –o abc.o -g
xyz.o:xyz.c xyz.h
gcc -c xyz.c -o xyz.o -g
clean:
rm main main.o abc.o xyz.o -f
那么我们为什么要来介绍这个例子呢?我们重点看的是第一行,也就是上面那个”C程序的出生“中链接的部分:真正生成一个C工程,我们需要不同的源文件生成o文件之后,最终链接到一起,也就是这个例子中,你要生成名为main的可执行文件(exe也好,其他的也好),总之你需要三个o文件,
上面我们介绍了编译汇编,最终要生成工程,真正起作用的地方还是在链接,理解了这个点之后,我们接着往下看如何编写一个C语言工程;
二、一个C语言工程实例
点击这里下载例程
这是我第一次使用基站,如果发现不妥请见谅ヽ(*  ̄▽ ̄ *)ノ
好了,回归正题,这个例程呢,是codeblock工程,假如你想用其他的编译器也是一样的,我先使用这个编译器,在接下来的过程中
我写了一个图的程序,这个图的程序呢,主要完成这几件事情,一是图的构建,这其中需要用到相关的结构体,第二个是图的遍历,

这个是我们的工程目录,下面是我们的工程依赖关系

这中间的依赖关系难以用语言描述,我用图形画了出来,配合源码食用;
下面我们来看我在这个工程构建的时候菜的坑:
如何避免重复引用头文件
重复引用头文件会造成怎么样的后果呢,相当于你在一个文件中定义的变量,声明的内容,预编译了两次,那么在编译的时候编译器发现你编译了两次,就会告诉你发生了冲突,重定义了
比如你在a.h中定义了int a,引用了两次,那就是上下文中有两个int a,肯定会报错,不过我的报错没有那么简单,我的报错是结构体引用了两次,中间还用了typedef,报错内容就更复杂了,不过出错的原因很简单,就是重复引用了头文件
那么如何避免重复引用头文件呢?可以使用宏定义,ifndef
endif
下面我们来介绍如何使用这个东西:
ifndef、endif用法
#ifndef B_H
#define B_H
#include "b.h"
#endif B_H
现在我们来解释一下这个宏定义,ifndef==如果没有定义B_H,那么我们先来定义B_H然后紧跟着引入b头文件,聪明的读者也许发现了,这个B_H是一个标识符,他在我们的程序中起到了标识的作用,如果在别的地方已经定义过B_H了,那么也应该在别的地方已经引入了b.h,所以这时候我们就不需要再引入b.h,而是继续执行endif下面的内容,
也许你会发现,为什么这个工程中引用了多次stdio文件,但是却没有报错?下面我们来介绍防止重复引用头文件的第二种方法:
在被引用的头文件开头使用#pragma once
,这样的话该文件就会只预编译一次,简单而好用
为什么说我们要防止重复引用,什么情况下会发生重复引用呢?
我们看这个工程中的这个部分:

我们看这个位置,main.h引用到了dfs头文件,又同时引用到了建立图头文件,而这两个头文件都引用到了studio,在预编译阶段,就会把这些内容同时编译到main.c中,如果没有pragma once,那么stdio头文件就会被编译两次,就发生了头文件重复引用;
吃一堑长一智,大话空话我不说多,我就指出,这个是菱形引入,这样是有可能重复引入头文件的,记住就行了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现