gcc编译流程、参数实例详细(总结)

一、简介

本文主要讲解gcc命令的参数以及动态库和静态库的制作,通过程序的编译过程运行具体的实例进行阐述。

二、编译过程

GCC即GNU Compiler Collection,编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和链接(Linking),gcc可以理解为编译管理工具,它会具体调用相关的工具进行执行,具体如下图:
在这里插入图片描述

2.1预处理

[root@localhost /]# gcc -E test.c -o test.i 或 gcc -E test.c

可以输出到test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码.
gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h 文件中的内容插入到test.c中了。
2.2编译为汇编代码(Compilation)
预处理之后,可直接对生成的test.i文件编译,生成汇编代码:

[root@localhost /]# gcc -S test.i -o test.s

gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。
2.3汇编(Assembly)
对于上一小节中生成的汇编代码文件test.s,as汇编器负责将其编译为目标文件,如下:

[root@localhost /]# gcc -c test.s -o test.o

2.4链接(Linking)
gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test

[root@localhost /]# gcc test.o -o test

gcc参数详解

编译参数详细说明
-o指定编译的⽬标,否则会⽣成的⽬标⽂件名是a.out; gcc main.c -o main
-S把源文件编译成汇编代码
-E只执行预处理
-include包含头文件,功能如同在源码的语句#include <xxx.h>
-I(大写i)指定程序包含头文件的路径,一般用于指定第三方库的头文件。
-L编译时,用于指定程序第三方库的查找路径。
-l链接时,指定程序需要进行链接的库。注:一般库文件名是libxxx.so,-I指定xxx即可。如-Ixxx
-rpath程序执⾏需要指定动态库的路径,但是可以⽤-rpath参数在编译时指定程序运⾏时需要加载的库的路径。
-D程序编译阶段可以定义一些宏,该方法可以让程序有选择性的运行代码。
-0n这是程序的优化等级。n的范围是0-3。n越大优化等级越高,程序运行的越快。否则越慢,n==0时是关闭优化。利于程序的调试,一般程序调试阶段会关闭优化等级,发布程序会把优化等级设为-O2。-O0:不进行优化处理。-O 或 -O1:优化生成代码。-O2:进一步优化。-O3 比 -O2 更进一步优化,包括 inline 函数。
-g打印程序的调试信息,如果需要使⽤gdb⼯具进⾏调试程序,程序编译的时候,需要加上该参数。
-share编译的时候尽量使用动态库。(除非只有静态库,没有动态库)
-static禁止使用动态库,编译的时候只加载静态库,这会导致执行件很大。
-w不生成任何的警告信息。
-Wall生成所有的警告信息。
-fpic使输出的对象模块可重定位地址方式行成的。
-shared把对应的源文件形成对应的动态链接库。

四、实例讲解

动态库和静态库
静态库
当程序与静态库连接时,库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中。这就会导致最终生成的可执行代码量相对变多,相当于编译器将代码补充完整了,优点是这样运行起来相对就快些。
不过会有个缺点:占用磁盘和内存空间。静态库会被添加到和它连接的每个程序中,而且这些程序运行时,都会被加载到内存中。无形中又多消耗了更多的内存空间。
动态库
与共享库连接的可执行文件只包含它需要的函数的引用表,而不是所有的函数代码,只有在程序执行时, 那些需要的函数代码才被拷贝到内存中。

**优点:**这样就使可执行文件比较小,节省磁盘空间,更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用,也同时节约了内存。
**缺点:不过由于运行时要去链接库会花费一定的时间,执行速度相对会慢一些,总的来说静态库是牺牲了空间效率,换取了时间效率,共享库是牺牲了时间效率换取了空间效率,没有好与坏的区别,只看具体需要了。
另外,一个程序编好后,有时需要做一些修改和优化,如果我们要修改的刚好是库函数的话,在接口不变的前提下,使用共享库的程序只需要将共享库重新编译就可以了,而使用静态库的程序则需要将静态库重新编译好后,将程序再重新编译一遍。这也是使用过程当中的差别,以现在的项目举例,在远程更新的时候,如果只是
.so动态库封装内容变化了,那么只需要更新
.so即可。
动态库生成:

#同静态库一样编译成目标文件:
[root@localhost /]# gcc –c a.c b.c
#生成共享库:
[root@localhost /]# gcc –fPIC –shared –o libshared.so a.o b.o

静态库的生成:

#在程序编译时,会全部代码加载到可执行程序中。
[root@localhost /]# ar rcs libstatic.a fun.o

静态库的链接方法:

[root@localhost /]# gcc –o staticcode –L. –lstatic main.c –static(默认库在当前文件夹)

动态库的链接方法:

[root@localhost /]# gcc –o sharedcode -L. –lshared main.c(默认库在当前文件夹)
  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
XNextEvent 函数用于从事件队列中获取下一个事件,而 XK_Print 是一个键盘事件,表示用户按下了“Print Screen”键。因此,如果你想在程序中监听用户按下“Print Screen”键的事件,可以使用 XNextEvent 函数来实现。 以下是一个监听 XK_Print 事件的示例程序: ```c #include <stdio.h> #include <X11/Xlib.h> #include <X11/keysym.h> int main() { Display *display = XOpenDisplay(NULL); Window root = DefaultRootWindow(display); XSelectInput(display, root, KeyPressMask); XEvent event; while (1) { XNextEvent(display, &event); if (event.type == KeyPress && XLookupKeysym(&event.xkey, 0) == XK_Print) { printf("Print Screen key pressed!\n"); } } XCloseDisplay(display); return 0; } ``` 这个程序使用 XOpenDisplay 函数打开一个与 X 服务器的连接,获取默认根窗口(root window),并使用 XSelectInput 函数设置监听的事件类型为 KeyPressMask,即键盘按键事件。 然后程序进入一个循环,每次调用 XNextEvent 函数等待事件发生。如果事件类型是 KeyPress,就使用 XLookupKeysym 函数获取键盘按键对应的 KeySym 值,如果这个值是 XK_Print,就打印一条信息。 编译流程如下: 1. 安装 X11 库 在 Ubuntu 上,可以使用以下命令安装 X11 库: ``` sudo apt-get install libx11-dev ``` 2. 编译程序 使用以下命令编译程序: ``` gcc -o xprint xprint.c -lX11 ``` 其中,-o 选项指定输出文件名为 xprint,-lX11 选项链接 X11 库。 3. 运行程序 使用以下命令运行程序: ``` ./xprint ``` 程序会等待用户按下“Print Screen”键,如果按下了这个键,就会在终端输出一条信息。要停止程序,可以使用 Ctrl+C 组合键。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值