gcc的编译过程

我们在linux下写的.c文件,编译的时候只需要gcc xxxx.c 默认生成可执行文件a.out,其实编译的过程要经历四个过程,在此之前我先不讲这四个过程,我先讲一下,Linux下gcc的使用方法和选项

gcc的使用方法和选项

gcc的使用方法

gcc [选项] 文件名
在linux下我们可以使用 gcc --help 查看gcc的各个选项

gcc的常见选项

选项含义
-E只进行预处理 ,不编译
-S只编译,不汇编
-c只编译、汇编,不链接
-g包含调试信息
-o输出指定文件名
-L指定链接所需库(动态库或静态库)所在路径
-l(小写L)指定所需链接库的库名
-Werror不区分警告和错误,遇到任何警告都停止编译
-Wall开启大部分警告提示
-I(大写i)指定include包含文件的搜索目录
-ansiANSI标准
-std=c99C99标准
-O0关闭所有优化选项
-O1第一级别优化,使用此选项可使可执行文件更小、运行更快,并不会增加太多编译时间,可以简写为-O
-O2第二级别优化,采用了几乎所有的优化技术,使用此选项会延长编译时间
-O3第三级别优化,在-O2的基础上增加了产生inline函数、使用寄存器等优化技术
-Os此选项类似于-O2,作用是优化所占用的空间,但不会进行性能优化,常用于生成最终版本

gcc的编译过程

gcc编译的过程可分解为4个大的步骤:

  1. 预处理(Preprocessing)
  2. 编译(Compilation)
  3. 汇编(Assembly)
  4. 链接(Linking)

下面为了更加容易理解,我用绘图的方式展现编译过程
在这里插入图片描述
下面我们讲具体的讲一下gcc编译步骤
1、预处理
预处理是读取c源程序,对其中的伪指令和特殊符号进行“替代”处理;经过此处理,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,仍然是C文件,但内容有所不同。伪指令主要包括以下三个方面:
(1)宏定义指令,如#define NAME TokenString, #undef以及编译器内建的一些宏,如__DATE__,FILE, LINE, TIME,
__FUNCTION__等。
(2)条件编译指令,如#ifdef, #ifndef, #else, #elif, #endif等。
(3)头文件包含指令,如#include “FileName”或者#include 等。

预处理的过程主要包括以下过程:

  1. 将所有的#define删除,并且展开所有的宏定义
  2. 处理所有的条件预编译指令,比如#if 、#ifdef、#elif、#else、#endif等
  3. 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。
  4. 删除所有注释“//”和“ /* */”
  5. 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
  6. 保留所有的#pragma编译器指令,因为编译器需要使用它们

通常使用以下命令来进行预处理,参数-E表示只进行预处理:
gcc -E hello.c -o hello.i
也可以使用以下指令完成预处理过程,其中从cpp是预处理器:
cpp hello.c > hello.i

预处理后的结果hello.i还是c语言源代码,我们可以使用cat或vim命令查看它的代码
vim hello.i
2、编译
编译程序所要做的工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。关于编译环节想要了解更多的可以参考一下其他博客介绍。

我们可以使用下面命令进行编译生成汇编文件
gcc -S hello.i > hello.s
我们可以使用cat命令查看它的代码
cat hello.s
3、汇编
汇编过程实际上把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个c语言源程序,都将最终经过一处理而得到相应的目标文件。

我们可以用下面命令进行汇编:
gcc -c hello.s -o hello.o

4、链接
汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,在某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件的函数,等等,所有的这些问题,都需要经过链接才能得以解决,链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体,也就是可执行程序,根据开发人员指定的库函数的链接方式的不同,链接处理可分为两种:①静态链接 ②动态链接。
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越,在某些情况下动态链接可能带来一些性能上的损害。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GCC 是 C/C++ 编译器,不支持直接编译 Java 代码。Java 代码需要先被编译成字节码,然后由 Java 虚拟机(JVM)解释执行或者编译成本地代码执行。 Java 编译器通常使用 javac 命令进行编译。下面是一个简单的编译示例: ``` javac HelloWorld.java ``` 这将会编译名为 HelloWorld.java 的 Java 源代码文件,并生成一个名为 HelloWorld.class 的字节码文件。 如果你想使用 GCC 编译 Java 代码,你需要先将 Java 代码编译成字节码,然后再使用 GCC 编译字节码文件所生成的头文件和源文件。 下面是一个基本的步骤: 1. 使用 javac 命令编译 Java 代码文件: ``` javac HelloWorld.java ``` 2. 使用 javah 命令生成头文件: ``` javah HelloWorld ``` 这将生成一个名为 HelloWorld.h 的头文件。 3. 将头文件和字节码文件一起使用 JNI 编写 C 代码。在 C 代码中,你需要包含 Java 的头文件和 JNI 库,这样就可以调用 Java 方法了。 4. 使用 GCC 编译 C 代码: ``` gcc -c -I/usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux HelloWorld.c ``` 其中,-I 选项指定了 Java 和 JNI 的头文件所在的目录。 5. 使用 GCC 链接 C 代码和 Java 库: ``` gcc -shared -o libHelloWorld.so HelloWorld.o -lc -ljvm ``` 这将生成一个名为 libHelloWorld.so 的共享库,可以在 Java 中使用 System.loadLibrary() 方法加载。 需要注意的是,由于 GCC 不是专为编译 Java 代码而设计的,因此使用 GCC 编译 Java 代码可能会比较麻烦,并且可能会出现一些问题。建议还是使用专门的 Java 编译器 javac 来编译 Java 代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值