编译器背后的故事

编译器背后的故事

关于可执行程序是如何被组装的

仿做学习材料“用gcc生成静态库和动态库.pdf”和“静态库.a与.so库文件的生成与使用.pdf”

学习材料“用gcc生成静态库和动态库.pdf”
第一步:编辑生成例子程序hello.c,hello.h,main.c

先创建一个作业目录,保存本次练习的文件
#mkdir test1
#cd test1
在这里插入图片描述

然后用vim、nano或gedit等文本编译器编辑生成所需要的3个文件。

  1. 编辑生成hello.h
    在这里插入图片描述

2.编辑生成hello.c
在这里插入图片描述

3.编辑生成main.c
在这里插入图片描述

第二步:将hello.c编译成.o文件

在这里插入图片描述
这里生成.o文件也需要加上-fPIC
查看是否生成了hello.o文件
在这里插入图片描述

第三步:由.o文件创建静态库

并用ls命令查看
在这里插入图片描述

第四步:在程序中使用静态库

方法一:
#gcc -o hello main.c -L.-lmyhello
自定义的库时,main.c还可放在-L.和-lmyhello之间,但是不能放在它俩之后,否则会提示myhello没定义,但是是系统的库时,如 g++ -o main (-L/usr/lib)-lpthread main.cpp就不出错。
方法二:
#gcc main.c libmyhello.a -o hello
方法三:
先生成main.o:
gcc -c main.c
再生成可执行文件:
gcc -o hello main.o libmyhello.a
动态库连接时也可以这样做。

#./hello
Hello everyone!

我们删除静态库文件试试公用函数 hello是否真的连接到目标文件hello 中了。
#rm libmyhello.a
rm: remove regular file 'libmyhello.a"? y
#.hello
Hello cveryone!

程序照常运行,静态库中的公用函数已经连接到目标文件中了。
这里我直接采用最快捷的一种:方法二。
在这里插入图片描述

在这里插入图片描述

第五步:由.o文件创建动态库文件

在这里插入图片描述

注意,如果第二步生成.o文件时,命令中不加-fPIC的话,这里就会出现错误。
###### 第六步:在程序中使用动态库
在这里插入图片描述

当静态库和动态库同名时,gcc优先使用动态库

第一步:先删除以上情况下的除hello.c,hello.h,main.c的所有文件
在这里插入图片描述
在这里插入图片描述

第二步:创建同名的静态库和动态库:libmyhello.so
在这里插入图片描述

第三步:运行gcc命令来使用函数库myhello生成目
标文件 hello,并运行程序hello。
通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文hello,并运行程序hello。
#gcc -o hello main.c -L.-lmyhello
在这里插入图片描述

(动态库和静态库同时存在时,优先使用动态库,当然,如果直接#gcc main.c libmyhello.a -o hello的话,就是指定为静态库了)
#./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory
从程序hello运行的结果中很容易知道,当静态库和动态库同名时,gcc命令将优先使用动态库,默认去连/usr/ib和/lib等目录中的动态库,将文件libmyhello.so复制到目录/usr/lib中即可。

学习材料“静态库.a与.so库文件的生成与使用.pdf”
第一步编辑生成所需的四个文件A1.c、A2.c、A.h、test.c

先创建一个作业目录,保存本次练习的文件
#mkdir test2
#cd test2
在这里插入图片描述

1.编辑生成A1.c
在这里插入图片描述

2.编辑生成A2.c
在这里插入图片描述

3.编辑生成A.h
在这里插入图片描述

4.编辑生成test.c
在这里插入图片描述

第二步:生成和使用静态.a文件

1.生成目标文件(.o)
在这里插入图片描述

2.生成静态.a文件
在这里插入图片描述

3.使用.a库文件
在这里插入图片描述

第三步:生成与使用共享库.so文件

1.生成目标文件
2.输出共享库.so文件
3.使用.so库文件,创建可执行文件
在这里插入图片描述

对第一次作业改编

第一次作业
第一步:扩展x2y
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第二步:gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第三步:记录文件大小
在这里插入图片描述
图中就是可执行文件的大小单位是字节。

说明gcc编译工具集中各软件的用途,了解EFF文件格式,汇编语言格式

仿做学习材料“Linux GCC常用命令.pdf”和“GCC编译器背后的故事.pdf”

学习材料“Linux GCC常用命令.pdf”

示例程序:
//test.c
#include <stdio.h>
int main (void)
{
printf(“Hello World! \n”);
return 0;
}
在这里插入图片描述

1.简单编译

直接编译
gcc test.c -o test
在这里插入图片描述

实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。

分步——四个阶段
1.预处理
在这里插入图片描述
在这里插入图片描述

2.编译
在这里插入图片描述

3.汇编
在这里插入图片描述

4.连接
在这里插入图片描述

2.多个程序文件的编译

这里的a1.c文件对应得是前面第一次作业的sub1.c,这里的a2.c文件对应得是前面的main1.c,这里的a3.c文件对应得是前面的sub1.h,
在这里插入图片描述
如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下三条命令:
gcc -c testl.c -o testi.o
gcc -c test2.c -o test2.o
gcc testl.o test2.o -o test

3.检错

先将之前写的test.c文件修改一下,制造一点错误。
在这里插入图片描述
在这里插入图片描述
然后使用检错命令检错
在这里插入图片描述

库文件连接

先将上一步骤对test.c的错误修改修正回来
在这里插入图片描述

第一步:编译生成可执行文件
第二步:链接
第三步:强制链接时使用静态链接库
在这里插入图片描述
默认情况下,GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
在/usrldevlmysqlib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
gcc -L /usr/dev/mysql/lib -static -lmysqlclient test.o -o test
静态库链接时搜索路径顺序:
1.ld会去找GCC命令中的参数-L
2.再找gcc的环境变量LIBRARY_PATH
3.再找内定目录/lib /usr/lib/usr/local/lib这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
1.编译目标代码时指定的动态库搜索路径
2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3.配置文件/etc/ld.so.conf中指定的动态库搜索路径
4.默认的动态库搜索路径/lib
5.默认的动态库搜索路径/usr/lib

学习材料“GCC编译器背后的故事.pdf”
准备工作

创建目录test0,编写一个Hello.c
在这里插入图片描述
在这里插入图片描述

编译过程

1.预处理
2.编译
3.汇编
4.链接
在这里插入图片描述
可以看出该执行文件链接了很多其他动态库
在这里插入图片描述

分析ELF文件

1.ELF文件的段
在这里插入图片描述

2.反汇编ELF
(1)使用objdump -D 对其进行反汇编如下:
在这里插入图片描述
(2)使用objdump -S 对其进行反汇编并且将其C语言源代码混合显示出来:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

nasm汇编编译器的安装与使用

安装
安装命令

sudo apt-get install nasm
在这里插入图片描述

使用
用nasm汇编编译器编译生成执行程序

在这里插入图片描述

在这里插入图片描述

与C代码的编译生成的程序大小进行对比

在这里插入图片描述

在这里插入图片描述
显示nasm编译器下生成的可执行文件较小。

了解实际程序是如何借助第三方库函数完成代码设计

写出几个基本函数名称及功能

了解Linux 系统中终端程序最常用的光标库(curses)的主要函数功能,

举例如下

cbreak():调用cbreak函数后,除了"Del"和"Ctrl"键外,接受其他所有字符输入。
raw(和cbreak()两个函数都可以禁止行缓冲(line buffering。区别是:在raw()函数模式下,处理挂起(CTRLZ)、中断或退出(CTRLC)等控制字符时,将直接传送给程序去处理而不产生终端信号;而在cbreak(模式下,控制字符将被终端驱动程序解释成其它字符。
nl (O /nonl 0):输出时,换行是否作为回车字符。nl函数将换行作为回车符,而nonl函数相反。
noecho()/echo():关闭/打开输入回显功能。
intrflush(WINDOW *win,bool bf)win为标准输出。当bf为true时输入Break,可以加快中断的响应。但是,有可能会造成屏幕输出信息的混乱。
keypad (WINDOW *win,bool bf : win为标准输出。调用keypad函数后,将可以使用键盘上的一些特殊字符,如方向键,转化成curses.h中的特殊键。
refresh():重绘屏幕显示内容。在调用initscr函数后,第一次调用refresh函数会清除屏幕显示。

体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)

操作方法

在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口(windows+R),命令行输入telnet bbs.newsmth.net。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库,请说明头文件(比如curses.h)和库文件都被安装到哪些目录中

安装命令

sudo apt-get install libncurses5-dev

头文件(比如curses.h)和库文件都安装的目录
查找命令

whereis / -name 文件名
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考 “Linux 环境下C语言编译实现弹球游戏”,用gcc编译生成一个终端游戏

在这里插入图片描述
代码参考(https://blog.csdn.net/psc0606/article/details/9990981)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值