【Linux】程序的翻译过程

简介: 【Linux】程序的翻译过程

程序的翻译过程分为:预处理、编译、汇编、链接

Linux中,我们可以用gcc命令的各种选项看到翻译的各过程,可以在每个阶段停下来,并且可以看到中间的翻译结果,这样就更便于我们理解翻译过程

1.预处理阶段

gcc -E

gcc -E test.c -o test.i

形成一个test.i文件,文件中保存的是gcc -E产生的临时结果

头文件展开

我们的test.c文件中只有24行,结果test.i中多出800多行,那多出来的这么多是什么呢?

其实这么多代码都是从stdio.h这个头文件展开来的

在预处理阶段,编译器会将我们源代码中所需要的头文件拷贝到源文件中来,我们的头文件中可能也会包含头文件,所以可能会进行递归的拷贝,这个过程叫做头文件展开

在安装编译器的时候,C标准库的头文件一般会一并下载到/usr/include/

我们可以打开stdio.h看一下

对比一下,确实我们展开的是stdio.h

我们可以看到预处理阶段进行了宏替换注释也被替换

条件编译

我们现在下载的软件,大多都分为好几个版本:专业版、社区版、学生版...

那这是怎么维护的呢,如果一个版本有一份源代码,那维护起来的成本是非常大的,这就可以用条件编译来解决这个问题了,只需要维护一份代码,可以用条件编译进行代码的动态裁剪

我们在C语言阶段就有过条件编译的说明:#ifdef #elif #else #endif

具体在《C语言 预处理》专栏的条件编译有说明

C语言 预处理详解-CSDN博客

我们编译成proj.i看一下

我们可以看到,满足条件的保留,不满足条件的删除

gcc -D 动态添加宏

我们可以不在文件中宏定义,可以通过gcc -D进行命令行式的宏定义,这样我们就可以动态地向源代码添加宏

2.编译阶段

预处理的结果是test.i,是一份干净的C语言代码

gcc -S

gcc -S test.i -o test.s

gcc -S产生一个test.s的临时结果

但是这个代码我们可能看不懂,但是我们知道这是汇编语言

3.汇编阶段

gcc -c

gcc -c test.s -o test.o

gcc -c 将test.s文件转成test.o文件,.o表示.obj,在vs中我们编译文件就会产生.obj文件

产生的.obj文件叫做目标文件,这个目标文件不能直接执行,最终形成.exe可执行程序才能运行

4.链接阶段

gcc test.o -o my.exe

只有最终形成可执行文件,才可以执行

关于链接,我们有三个问题:

  • 是什么?
  • 为什么?
  • 怎么办?

是什么?

链接的过程是我们的程序和库结合的过程

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?

最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用

库:语言一定要有自己的标准库

我们可以用ldd命令来看到对应的动态库

这就是我们的C标准库

在安装开发环境的时候,会安装C标准库+C头文件,这时候我们才可以包含对应的头文件,调用头文件里声明的函数

函数库

函数库一般分为静态库和动态库两种

静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a

动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。

  • Linux中,动态库.so 静态库.a
  • Windows中,动态库.dll 静态库.lib

为什么?

  • 让开发站在巨人的肩膀上
  • 提高开发的效率

怎么办?

链接时,两种链接方式:

  • 动态链接
  • 静态链接

动态链接

动态库和动态链接的优缺点

  1. 不能丢失
  2. 节省资源

静态链接

静态库和静态链接的优缺点

  1. 一旦形成,和库无关
  2. 浪费资源

C动态库,是默认提供的

gcc默认形成的可执行程序,默认采用动态链接

而Linux中静态库默认是没有的

安装静态库我们可以用yum指令

sudo yum install -y glibc-static libstdc++-static

这时我们就可以编译通过了

同样运行也能通过

静态链接的应用场景

由于静态链接不依赖于任何的动态库,所以在移植到其他环境中时就不需要做过多的环境检测,可以直接运行,方便部署


相关文章
|
10月前
|
安全 Linux iOS开发
Nessus Professional 10.10 Auto Installer for RHEL 10, AlmaLinux 10, Rocky Linux 10 - Nessus 自动化安装程序
Nessus Professional 10.10 Auto Installer for RHEL 10, AlmaLinux 10, Rocky Linux 10 - Nessus 自动化安装程序
639 6
Nessus Professional 10.10 Auto Installer for RHEL 10, AlmaLinux 10, Rocky Linux 10 - Nessus 自动化安装程序
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
583 7
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
2220 2
|
Linux Python
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
959 2
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
1439 1
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
587 5
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
1259 3
|
Linux Windows Python
最新 Windows\Linux 后台运行程序注解
本文介绍了在Windows和Linux系统后台运行程序的方法,包括Linux系统中使用nohup命令和ps命令查看进程,以及Windows系统中通过编写bat文件和使用PowerShell启动隐藏窗口的程序,确保即使退出命令行界面程序也继续在后台运行。
|
存储 安全 Linux
【Azure 应用服务】App Service For Linux 怎么安装Composer,怎么安装PHP扩展,怎么来修改站点根路径启动程序?
【Azure 应用服务】App Service For Linux 怎么安装Composer,怎么安装PHP扩展,怎么来修改站点根路径启动程序?
259 0