简单了解C语言如何构建多文件项目

简单理解C语言如何构建工程

首先我们最好有一个好用的编辑器,vscode也好,visual studio也罢,dev也行,这里我们使用的是code::block;

一、了解C语言工程的构建原理

​ 首先我在这里放一张图,让我们来看一下是这张图叙述了那些事情:

image-20210320133801928

​ 上图取自大名鼎鼎的csapp,它描述了一个C语言程序出生的步骤,我们在编译器中写好程序按下“构建”(或者编译或者make)按钮,最先作用的是预处理器,预处理器根据头文件把该连接的源文件连接到一起,然后准备编译,编译的过程是词法分析的过程,经过词法分析之后,程序编译成了汇编程序,如果我们打开汇编程序,我们会看到一系列寄存器操作,汇编语言是非常底层的语言,再之后汇编语言经过汇编就变成了*.o文件,o文件经过链接,就变成了可执行的、二进制的目标文件

​ 下面呢,我们再来介绍一个东西,它叫做makefile,makefile呢,是gnu出品的自动化构建工程的工具,使用make命令就可以调用执行make文件(linux一切皆文件的思想嘛),make文件会根据当前目录下的makefile文件(没有后缀)来执行一系列终端命令,从而代替手工来完成程序的编译操作

[注]:如果你的工具链是mingw,那么相应的命令将是mingw32-make,你可以去工具链的安装目录中找一下这个程序(这个程序就叫mingw32-make)

​ 下面我们来介绍一个makefile的例子,

img
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工程,假如你想用其他的编译器也是一样的,我先使用这个编译器,在接下来的过程中

​ 我写了一个图的程序,这个图的程序呢,主要完成这几件事情,一是图的构建,这其中需要用到相关的结构体,第二个是图的遍历,

image-20210320150118722

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

image-20210320151337986

这中间的依赖关系难以用语言描述,我用图形画了出来,配合源码食用;

下面我们来看我在这个工程构建的时候菜的坑:

如何避免重复引用头文件

​ 重复引用头文件会造成怎么样的后果呢,相当于你在一个文件中定义的变量,声明的内容,预编译了两次,那么在编译的时候编译器发现你编译了两次,就会告诉你发生了冲突,重定义了

​ 比如你在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,这样的话该文件就会只预编译一次,简单而好用

为什么说我们要防止重复引用,什么情况下会发生重复引用呢?

我们看这个工程中的这个部分:

image-20210323164209411

​ 我们看这个位置,main.h引用到了dfs头文件,又同时引用到了建立图头文件,而这两个头文件都引用到了studio,在预编译阶段,就会把这些内容同时编译到main.c中,如果没有pragma once,那么stdio头文件就会被编译两次,就发生了头文件重复引用;

​ 吃一堑长一智,大话空话我不说多,我就指出,这个是菱形引入,这样是有可能重复引入头文件的,记住就行了

posted @   dou_fu_gan  阅读(117)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示