当前位置: 首页 > article >正文

【Linux】gcc编译器的使用

 

目录

零、gcc常用参数及指令

查看环境

一、程序的翻译过程

1.1验证

预处理阶段验证:

编译阶段验证:

汇编阶段验证:

二、动态库和静态库的理解

2.1概念

2.2优缺点

三、自动化构建代码

3.1make/make file基础

编辑Makefile自动构建过程:

四、PHONY

4.1在创建Makefile文件时,为什么设clean为伪目标?

探究:

4.1.1在这里我没要首先明确“伪目标”是一种什么概念。

4.1.2伪目标的特性

4.1.3不会重复编译的原因

4.1.4通过文件属性判断变化

常用选项:

示例:

五、Makefile中的语法

5.1 @

例子:

常见用法:

示例:

5.2 变量的运用

5.3$^ 和$@

示例

总结

5.4最终版本


零、gcc常用参数及指令

-B

将指定目录添加到搜索路径

-v

显示编译器调用的程序

-c

仅执行编译,不进行链接操作

-help

显示帮助信息

-E

仅执行编译预处理

-pipe

使用管道符

-I

设置头文件

-shared

创建动态共享库

-L

设置链接库

-static

使用静态链接

-o

指定要生成的输出文件

-time

设置每个子流程的执行时间

-S

将C代码转换为汇编代码

-version

显示版本信息

致谢:gcc命令 – C/C++语言编译器 – Linux命令大全(手册)提供的命令查询


查看环境

cat /etc/redhat-release

使用c99规则编译

gcc code.c -std=c99

一、程序的翻译过程

以下使我们在Win系统下编译器生成可执行文件的工作顺序:

  1. 预处理:在这一过程编译器主要进行以下几个过程:头文件展开去注释条件编译和宏替换,在经过这一过程后生成  .i  文件,文件使用的仍然是C语言,对于我们来说仍然是接近自然语言的编程语言。
  2. 编译:这一过程是将接近自然语言的C语言转化成对应的汇编语言。文件也由.i文件转为.s文件。
  3. 汇编:这一过程是将汇编语言转化为对应的二进制机器语言,文件由对应的.s文件转化为.o文件。
  4. 链接:这一过程总结起来就是 *.o + 可执行库 = 可执行文件。
阶段执行之前执行操作执行之后执行后本质
预处理.c头文件展开,去注释,条件编译,宏替换.iC语言
编译.i转化为汇编语言.s汇编语言
汇编.s转化为二进制语言.o二进制机器语言
链接.o链接可执行文件.o和可执行库

那么Linux下的编译器gcc的工作流程是否和Win下的编译器工作原理相同呢?

1.1验证

预处理阶段验证:

gcc -E code.c -o code.i

-E   符号使编译器进行到翻译过程的"预处理"阶段然后停止,整行指令使gcc生成进行完预处理过程临时文件code.i。因此,我们通过条件编译可以实现对代码的动态裁剪

编译阶段验证:

gcc -S code.i -o code.s

-S  条件编译符号,将对应c文件编译为code.s文件,即完成汇编阶段,

汇编阶段验证:

gcc -c code.s -o code.o

-c  将翻译过程进行到"编译阶段",不进行链接,即只生成到"可重定位二进制文件"阶段.

我们如果想查看当前生成的二进制文件,可以使用以下命令将文件以二级制文本形式打开

od code.o

当然我们需要知道"可重定位二进制文件"不可执行,虽然已经有了机器能读懂的二级制文件,但是我们使用的函数是包含在头文件中的,我们写的程序本身并没有,这些头文件对应着C语言的库,我们还需要找到对应的库,这样才能交给机器执行。

即:代码 + 头文件 + 库 = 可执行程序

ldd filename 

 使用以上命令可查看对应文件需要的库。

我们已经知道了编译器的工作过程,那么它本身就是一个软件,也是由语言编写而成,那么第一个编译器是怎样生成的呢?

起始最早的语言就是二进制语言,工程师编程使用的是打孔纸带,有空和无孔分别代表零一,人们为了提高效率,使用二进制机器语言先编写了一个可以解释汇编语言的软件,这样以后再使用汇编语言进行编程将大大提高效率,而且汇编语言便于理解,更接近自然语言,后来更进一步,人们使用汇编语言编写了一个能解释C语言的编译器,这样以后就可以使用C编程了.而我们观察到编译器的工作过程其实就是编程语言发展史的逆过程.


二、动态库和静态库的理解

之前我们提到可重定位二进制文件不能执需要链接对应库,库有两种链接方式,动态库和静态库。

  • Linux
    • 静态库:.a
    • 动态库:.so(共享对象)
  • Windows
    • 静态库:.lib
    • 动态库:.dll(动态链接库)

2.1概念

动态库:C/C++或其他第三方提供的方法集合,被所有程序以链接的方式关联起来,动态链接.

静态库:C/C++或其他第三方提供的方法集合,被所有程序以拷贝的方式,将需要的代码拷贝到需要的可执行程序当中.

2.2优缺点

动态库:体积较小,节省资源,速度相对较慢

静态库:无视库本体自己独立运行,体积相对较大,冗余度相对较高,动态链接的可执行程序如果有几兆B那么对应程序的静态链接版本则可能有几十兆.

指定动态链接:

ldd code -d

指定静态链接:

ldd code -s

 加入对应静态库的指令:

sudo yum install glibc -static

sudo yum install -y libstdc++ -static

编译器一般默认动态链接。我们一般所说的安装开发环境指的是包含所需要的库\合理的查找路径\规定连接方式。使用库可以大大减少开发难度和成本,使我们站在巨人的肩膀上,比如printf就可以让我们直接输出想要的内容,而不是从系统级接口甚至硬件寄存器去开始调用。

三、自动化构建代码

在vs中我们可以之间一个快捷键编译执行,其实使用gcc也有对应的方法,这需要借助我们的一个特殊文件:Makefile

3.1make/make file基础

  • make:一个命令.
  • make file:是一个在当前目录下具有特定格式的文本文件.
touch Makefile       在当前路径下创建一个文本文件

Vim Makefile         使用文本编辑器vim打开这个文件

编辑Makefile自动构建过程:

以下为makefile中的构建方式:

依赖关系 + 依赖方法 => 证明关联性和需要执行的内容

mybin:code.c                 此行代表依赖关系

    gcc code.c -o mybin      此行表示依赖方法

依赖关系 + 依赖方法 => 证明关联性和需要执行的内容 
.PHONY:clean                  伪目标,无依赖关系 
clean: 
    rm -f mybin

在Makefile中的所有缩进都必须是Tab,而不可以是空格!!不然会报错!!

我们可以把Makefile文件理解为类似于Python的脚本文件

四、PHONY

“PHONY”是一个英语单词,意思是假冒的、虚假的、不真诚的。在 Makefile 中,.PHONY 是一个特殊的目标,用于声明那些不对应于实际文件的目标。使用 .PHONY 可以避免 Make 误认为一个目标是文件,并导致不必要的构建。

当你有一些不生成文件的目标(比如 clean, install, run 等)时,你可以将其声明为 .PHONY,这表明它们总是应该被执行而不考虑文件是否存在。这样,即使在当前目录中有一个名为 clean 的文件,执行 make clean 时也会保证执行对应的命令。

4.1在创建Makefile文件时,为什么设clean为伪目标?

探究:

主要是避免与真实文件名的冲突。伪目标是不会生成实际文件的目标,它的定义主要用于执行特定的命令,而不是作为生成文件的规则。伪目标可以直接执行,不论当前目录中是否存在同名文件。例如,运行make clean时,Make始终会执行与clean关联的命令,而不会受到文件存在与否的影响。

4.1.1在这里我没要首先明确“伪目标”是一种什么概念。

在 Makefile 中,伪目标(phony target)是指那些并不代表实际文件的目标通常,伪目标用来定义无条件执行的命令,比如清理操作、构建文档、运行测试等。伪目标的名称有时与生成的文件同名,但目的是为了提供一个明确的操作指令。

在 Makefile 中定义伪目标时,通常会使用 .PHONY 声明,来告知 make 这个目标不是一个文件。例如:

.PHONY: clean all 
all: myprogram 
clean: 
    rm -f myprogram

在这个例子中,clean 和 all 是伪目标。即使在当前目录下存在名为 clean 或 all 的文件,make 仍然会执行这些目标关联的命令。

总之,伪目标是 Makefile 中一种很有用的工具,用于管理没有直接对应文件的任务。

4.1.2伪目标的特性

当命令被设置为伪目标后该命令一旦被调用便可无限制的执行。一些翻译过程(下图1-8行)如果检测到文件没发生变化则不会进行重复编译,但是伪目标“clean”则可依次执行。

图5-1:

4.1.3不会重复编译的原因

gcc会自动进行检测文件状态,当发现当前c文件和上次生成的可执行文件没有变动时将不会编译,因为编译会消耗大量系统资源,影响整体效率。那么gcc检测的内容是是什么呢?它是不是实时检测所有文件的一举一动呢?

其实不是的,gcc判断一个文件是否有被更改过靠的是对比文件属性变化,具体对比的内容又涉及到文件属性了。

4.1.4通过文件属性判断变化

$stat code.c 

查看文件属性

文件属性中包含三中事件:

  1. Access:内容被读取时发生变动.
  2. Modify:内容发生变化时改动.
  3. Change:文件属性发生变动时改动.

总结:经过实践我们可以得知编译之前对比的是c文件和bin文件的modify项,如果文件未发生变动,那么c文件的Modify项应该早于bin文件的Modifhy项。在过程中我们发现我们频繁地打文件进行查看然后关闭,随后便查看文件属性,但是Access项并不是随时变化的,这是因为文件被读取时间实际上是文件存储在磁盘上区域被访问的时间,但是我们通过学习计算机组成原理可知常用的文件为了被访问时快速调用通常被存放在内存中,当一段时间后才会将修改后的内容统一到磁盘中,如果多次直接访问磁盘会影响整体效率,因为相对来说磁盘的读取速度是很慢的。

如果我们更改M和A任意一个都会影响C项,但是我们如果使用

man touch

man touch 是用来查看 touch 命令手册的。在 Unix 和类 Unix 系统中,touch 命令的主要作用是:

  1. 创建空文件:如果指定的文件名在当前目录下不存在,touch 将创建一个空文件。
  2. 更新文件的时间戳:如果文件已经存在,touch 会更新该文件的访问时间(atime)和修改时间(mtime)到当前时间。这样做可以用于文件管理和版本控制,确保文件的时间戳是最新的。

基本的命令用法如下:

touch filename

常用选项:

  • -a:只更新访问时间。
  • -m:只更新修改时间。
  • -c:如果文件不存在,不创建新文件。
  • -t:使用明确的时间戳。

示例:

# 创建一个新文件 touch newfile.txt # 更新某个已存在文件的时间戳 touch existingfile.txt # 只更新访问时间 touch -a existingfile.txt # 只更新修改时间 touch -m existingfile.txt


Makefile本身具有一定的隐式推倒能力。

并不建议像图5-1一样编写Makefile。


五、Makefile中的语法

5.1 @

在 Makefile 中,@ 符号的作用是抑制命令的输出。当你在目标下方列出要执行的命令时,默认情况下,这些命令会被打印到标准输出(通常是终端)。使用 @ 可以阻止该命令的打印,只执行该命令。

例子:

target: @echo "这是一个不显示命令的输出"

在执行 make target 时,输出结果将是:

这是一个不显示命令的输出

而不会显示 echo "这是一个不显示命令的输出" 这一行。

常见用法:

  • 提高可读性:当你只想显示命令的结果,而不是命令本身的时候,使用 @ 可以让输出更简洁。
  • 隐藏不必要的信息:在某些情况下,你可能希望抑制一些命令的输出,以避免干扰。

注意事项:

  • 如果在一个目标下的多个命令前都加上 @,则它们都会被抑制输出。
  • 使用 @ 不会影响命令的执行,只是影响输出。

示例:

all: @echo "开始构建" @make compile @echo "构建完成"

这里,运行 make all 时,会看到 "开始构建" 和 "构建完成" 的输出,但不会看到 echo "开始构建" 和 make compile 的命令行输出。

图5-2

5.2 变量的运用

可以利用一些变量来调高代码健壮性:

# 定义变量  
CC = gcc  
CFLAGS = -Wall -g  
SRCS = main.c utils.c  
OBJS = $(SRCS:.c=.o)  
TARGET = my_program  

all: $(TARGET)  

$(TARGET): $(OBJS)  
	$(CC) $(CFLAGS) -o $@ $^  

%.o: %.c  
	$(CC) $(CFLAGS) -c $< -o $@  

.PHONY: clean  
clean:  
	rm -f $(OBJS) $(TARGET)

5.3$^ 和$@

在 Makefile 中,自动变量是一种特殊的变量,可以在规则的命令部分中使用,以便简化和提高可读性。下面是一些常用的自动变量,包括 $^ 和 $@:

1. $@

  • 含义:表示当前规则的目标文件。
  • 用法:在命令中引用目标名称。

2. $^

  • 含义:表示所有的依赖文件(prerequisites),并且去重。
  • 用法:在命令中引用所有依赖项的列表。

3. $<

  • 含义:表示第一个依赖文件。
  • 用法:常用于生成单个目标时,例如编译单一源文件。

4. $?

  • 含义:表示所有比目标文件新(即修改时间比目标文件晚)的依赖文件。
  • 用法:用于条件性构建,仅在需要更新的情况下重建目标。

5. $*

  • 含义:表示文件名的主名称部分(不包含后缀)。
  • 用法:用于模式规则,允许在同一规则中生成多个相关的文件。

示例

下面是一个示例 Makefile,演示这些自动变量的使用:

# 声明目标和依赖关系 
program: main.o utils.o 
    gcc -o $@ $^ 
main.o: main.c 
    gcc -c $< 
utils.o: utils.c 
    gcc -c $< 
clean: rm -f program *.o

在这个示例中:

  • 当 make program 被执行时,$@ 将被替换为 program,$^ 将被替换为 main.o utils.o。
  • main.o 和 utils.o 的规则中,$< 将分别引用 main.c 和 utils.c。

总结

  • $@:当前目标文件。
  • $^:所有依赖文件(去重)。
  • $<: 第一个依赖文件。
  • $?:所有比目标更新的依赖文件。
  • $*:文件名主部分(用在模式规则)。

使用这些自动变量可以使 Makefile 更加简洁且易于维护。

5.4最终版本

cc = gcc 
src = code.c 
target = mybin 

$(target) : $(src) 
    gcc $^ -o $@ 

.PHONY:clean 
clean: 
    rm -f $(target)

ok,现在你已经掌握gcc的使用了,也明白了自动构建的原理,赶紧去生成自己的可执行文件吧。


http://www.kler.cn/news/356024.html

相关文章:

  • HarmonyOS 通过用户首选项实现数据持久化
  • 深拷贝与浅拷贝区别
  • Mac M3安装VMWare Fusion
  • 责任链模式下,解决开闭原则问题实践
  • 重写QObjiet虚函数timerEvent()启动定时器
  • ssm基于SSM框架的成绩管理系统的设计与实现+vue
  • 矩阵相关算法
  • 【基于数据分析-画图展示】
  • 【Unity - 屏幕截图】技术要点
  • Spring Boot框架的电影评论系统设计与实现
  • 多种方式实现安全帽佩戴检测
  • 上位机-简介
  • 与双指针的亲密接触:快与慢的浪漫交错
  • 细说网络安全五家龙头企业,你去过哪一家?
  • Linux 中,`tee` 和 `grep` 分别用于处理输出流[从标准输入读取数据,并将数据同时输出到标准输出和文件]和文本内容的过滤。
  • fmql之Linux Uart
  • 【C++刷题】力扣-#252-会议室
  • 【AI学习】Mamba学习(八):HiPPO通用框架定义和方法
  • JAVA学习-练习试用Java实现“选择排序”
  • 学生社会适应能力测试