编译器背后的故事

执行程序的组装过程

用gcc生成静态库和动态库

我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
(1) 第 1 步:编辑生成例子程序 hello.h、hello.c 和 main.c。
先创建一个作业目录,保存本次练习的文件。

mkdir test1
cd test1

在这里插入图片描述

然后用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的 3 个文件。
hello.c是函数库的源程序,其中包含公用函数 hello,该函数将在屏幕上输出"HelloXXX!"。

#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}

在这里插入图片描述

在这里插入图片描述

hello.h为该函数库的头文件。
代码如下:

#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif //HELLO_H

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

main.c为测试库文件的主程序。

#include "hello.h"
int main()
{
hello("everyone");
return 0;
}

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

在主程序中调用了公用函数 hello

(2)第 2 步:将 hello.c 编译成.o 文件。
无论静态库,还是动态库,都是由.o 文件创建的。因此,我们必须将源程序 hello.c 通过 gcc 先编译成.o 文件。在系统提示符下键入以下命令得到 hello.o 文件。

 gcc -c hello.c

在这里插入图片描述
我们运行 ls 命令看看是否生存了 hello.o 文件。

# ls

在这里插入图片描述
在 ls 命令结果中,我们看到了 hello.o 文件。
(3)第 3 步:由.o 文件创建静态库。
静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为.a。创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件libmyhello.a

 ar -crv libmyhello.a hello.o

在这里插入图片描述

运行 ls 命令查看结果:

 ls

在这里插入图片描述
成功生成静态库

Linux ar命令用于建立或修改备存文件,或是从备存文件中抽取文件。ar可让您集合许多文件,成为单一的备存文件。在备存文件中,所有成员文件皆保有原来的属性与权限。
ar[-dmpqrtx][cfosSuvV][a<成员文件>][b<成员文件>][i<成员文件>][备存文件][成员文件]

(4) 第 4 步:在程序中使用静态库。
在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明静态库名,gcc 将会从静态库中将公用函数连接到目标文件中。
main.c中,我们包含了静态库的头文件hello.h,然后在主程序 main 中直接调用公用函数 hello。下面先生成目标程序 hello,然后运行 hello 程序看看结果如何。

gcc main.c libmyhello.a -o hello

在这里插入图片描述
gcc -o: 生成执行文件
运行hello

./hello

在这里插入图片描述
我们删除静态库文件试试公用函数 hello 是否真的连接到目标文件 hello 中了。

rm libmyhello.a

在这里插入图片描述
再次运行程序
在这里插入图片描述

程序照常运行,静态库中的公用函数已经连接到目标文件中了。
(5) 第 5 步:由.o 文件创建动态库文件。
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其文件扩展名为.so。
在系统提示符下键入以下命令得到动态库文件 libmyhello.so。

 gcc -shared -fPIC -o libmyhello.so hello.o

在这里插入图片描述

(6) 第 6 步:在程序中使用动态库;
在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。

gcc -o hello main.c -L. -lmyhello

在这里插入图片描述

运行hello
在这里插入图片描述
程序在运行时,会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们需要将文件 libmyhello.so 复制到目录/usr/lib 中

 mv libmyhello.so /usr/lib

在这里插入图片描述

如果无法移动,可以在mv前面加sudo,用管理员身份运行

 sudo mv libmyhello.so /usr/lib

Linux sudo命令以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行。

运行hello
在这里插入图片描述
成功了。这也进一步说明了动态库在程序运行时是需要的。

静态库.a与.so库文件的生成与使用

先创建一个作业目录,保存本次练习的文件。

mkdir test2
cd test2

在这里插入图片描述

然后用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的四个文件 A1.c A2.cA.htest.c

A1.c:

#include <stdio.h>
void print1(int arg){
printf("A1 print arg:%d\n",arg);
}

在这里插入图片描述

在这里插入图片描述

A2.c:

#include <stdio.h>
void print2(char *arg){
printf("A2 printf arg:%s\n", arg);
}

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

A.h

#ifndef A_H
#define A_H
void print1(int);
void print2(char *);
#endif

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

test.c:

#include <stdlib.h>
#include "A.h"
int main(){
print1(1);
print2("test");
exit(0);
}

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

1、静态库.a 文件的生成与使用。
1.1、生成目标文件(xxx.o)

gcc -c A1.c A2.c

在这里插入图片描述

1.2、生成静态库.a 文件

 ar crv libafile.a A1.o A2.o

在这里插入图片描述

1.3、使用.a 库文件,创建可执行程序(若采用此种方式,需保证生成的.a 文件与.c 文件保存在同一目录下,即都在当前目录下)

 gcc -o test test.c libafile.a 

在这里插入图片描述
1.4、运行test

./test

在这里插入图片描述
2、共享库.so 文件的生成与使用
2.1、生成目标文件(xxx.o() 此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so文件时会出错)

gcc -c -fpic A1.c A2.c

在这里插入图片描述

2.2、生成共享库.so 文件

 gcc -shared *.o -o libsofile.so

在这里插入图片描述

2.3、使用.so 库文件,创建可执行程序

 gcc -o test test.c libsofile.so 

在这里插入图片描述
2.4、运行

 ./test

在这里插入图片描述
这是因为动态库只在/lib and/usr/lib下搜索对应的.so文件。
用以下代码移动文件

 sudo cp libsofile.so /usr/lib

在这里插入图片描述
运行成功

主程序文件main.c调用两个函数x2x和x2y进行运算

main.c主程序:

#include<stdio.h>
#include<x2.h>
int main()	
{
	int a=25;
	int b;
	float c;
	b=x2x(a);
	c=x2y(a,b);
	printf("%f",c);
	return 0;
}

在这里插入图片描述

x2x.c用于实现25到100的累加
x2x.c主程序:

#include<stdio.h>
int x2x(int a)
{
for(int i=26;i<=100;i++)
	a=a+i;
return a;
}

在这里插入图片描述

x2y用于除法
x2y.c代码:

include<stdio.h>
float x2y(int a,int b)
{
float c;
c=b/a;
return c;
}

在这里插入图片描述

x2.h代码:

#ifndef X2_H
#define X2_H
int x2x(int a);
float x2y(int a,int b);
#endif //X2_H

在这里插入图片描述

用gcc -c生成main.o,x2x.o,x2y.o文件
在这里插入图片描述

方法一、静态库
生成静态文件库libx2x.a和libx2y.a
以lib为前缀,ar为命令符
在这里插入图片描述
在程序中使用静态库,生成可执行文件x2

gcc main.c libx2x.a  libx2y.a -o x2

在这里插入图片描述
运行x2
在这里插入图片描述
方法二、动态库
创建动态库

gcc -shared -fPIC -o libx2x.so x2x.o
gcc -shared -fPIC -o libx2y.so x2y.o

在这里插入图片描述
文件 libx2x.so,libx2y.so 复制到目录/usr/lib 中

sudo mv libx2x.so /usr/lib
sudo mv libx2y.so /usr/lib

生成执行文件

gcc main.c libx2x.so libx2y.so -o x2

运行
在这里插入图片描述
静态库与动态库的比较
静态库
在这里插入图片描述
动态库
在这里插入图片描述
对比两个的文件大小,动态库的占用量是大于静态库的

gcc编译工具集中各软件的用途

Linux GCC 常用命令

GCC 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在
示例程序:
创建test.c

#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}

一步到位的编译指令是:

gcc test.c -o test

在这里插入图片描述
预处理(可以让编译器在预处理后停止,并输出预处理结果)

gcc -E test.c -o test.i 或 gcc -E test.c

在这里插入图片描述
编译为汇编代码(Compilation)
预处理之后,可直接对生成的 test.i 文件编译,生成汇编代码:

gcc -S test.i -o test.s

在这里插入图片描述
汇编(Assembly)
对汇编代码文件 test.s,gas 汇编器负责将其编译为目标文件,如下:

gcc -c test.s -o test.o

在这里插入图片描述
图中test为一步生成程序生成的,不必理会
连接(Linking)
gcc 连接器是 gas 提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。对于上一小节中生成的 test.o,将其与C标准输入输出库进行连接,最终生成程序 test

gcc test.o -o test

在这里插入图片描述
多个程序文件的编译

gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test

检错 -pedantic 选项能够帮助程序员发现一些不符合ANSI/ISO C 标准的代码,但不是全部

gcc -pedantic illcode.c -o illcode

库文件连接
编译成可执行文件

gcc –c –I /usr/dev/mysql/include test.c –o test.o

链接

gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test

强制链接时使用静态链接库

gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test

GCC 编译器背后的故事

GCC:
GCC(GNU C Compiler)是编译工具。
Binutils:
一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、ldd、readelf、 size 等。这 一组工具 是开发和 调试不可 缺少的工具 ,分别简 介
如下:
(1) addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码
行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对
应的源代码位置。
(2) as:主要用于汇编,有关汇编的详细介绍请参见后文。
(3) ld:主要用于链接,有关链接的详细介绍请参见后文。
(4) ar:主要用于创建静态库。为了便于初学者理解,在此介绍动态库与静态库的概念:
1、如果 要将 多个 .o 目标 文件 生成 一个 库文 件, 则存 在两 种类 型的 库, 一种是静态库,另一种是动态库。
2、在 windows 中 静态 库是 以 .lib 为 后缀 的文 件 ,共 享库 是以 .dll 为 后缀的 文 件 。 在 linux 中 静 态 库 是 以 .a 为 后 缀 的 文 件 ,共 享 库 是 以 .so 为 后 缀的文件。
3、静 态 库 和 动 态 库 的 不 同 点 在 于 代 码 被 载 入 的 时 刻 不 同 。 静 态 库 的 代 码 在 编译 过 程 中 已 经 被 载 入 可 执 行 程 序 , 因 此 体 积 较 大 。共 享 库 的 代 码 是 在 可 执行 程 序 运 行 时 才 载 入 内 存 的 , 在 编 译 过 程 中 仅 简 单 的 引 用 , 因 此 代 码 体 积较 小 。 在 Linux 系 统 中 , 可 以 用 ldd命 令 查 看 一 个 可 执 行 程 序 依 赖 的 共 享库。
4、如 果 一 个 系 统 中 存 在 多 个 需 要 同 时 运 行的 程 序 且 这 些 程 序 之 间 存 在 共 享库,那么采用动态库的形式将更节省内存。
(5) ldd:可以用于查看一个可执行程序依赖的共享库。
(6) objcopy:将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或
者将.elf 转换成.bin 等。
(7) objdump:主要的作用是反汇编。有关反汇编的详细介绍,请参见后文。
(8) readelf:显示有关 ELF 文件的信息,请参见后文了解更多信息。
(9) size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等
C 运行库:
C 语言标准主要由两部分组成:一部分描述 C 的语法,另一部分描述 C 标准库。C 标准库定义了一组标准头文件,每个头文件中包含一些相关的函数、变量、类型声明和宏定义,譬如常见的 printf 函数便是一个 C 标准库函数,其原型定义在 stdio 头文件中。
C 语言标准仅仅定义了 C 标准库函数原型,并没有提供实现。因此,C 语言编译器通常需要一个 C 运行时库(C Run Time Libray,CRT)的支持。C 运行时库又常简称为 C 运行库。与 C 语言类似,C++也定义了自己的标准,同时提供相关支持库,称为 C++运行时库。
准备工作
先创建一 个工作目录 test0,然后用文本编辑器生成一个 C 语言编写的简单 hello.c 程序为示例,其源代码如下所示:

#include <stdio.h>
int main(void)
{
printf("Hello World! \n");
return 0;
}

在这里插入图片描述
预处理

gcc -E hello.c -o hello.i

hello.i代码片段
在这里插入图片描述

编译

gcc -S hello.i -o hello.s

hello.s代码片段
在这里插入图片描述
汇编

gcc -c hello.s -o hello.o

链接
可翻看前面示例,在此不再详细介绍

ar -crv libmyhello.a hello.o   //静态
gcc -shared -fPIC -o libmyhello.so hello.o  //动态
size hello //使用 size 查看大小
$ ldd hello //可以看出该可执行文件链接了很多其他动态库,主要是 Linux 的 glibc动态库

分析 ELF 文件
ELF 文件的段

readelf -S hello

在这里插入图片描述
反汇编 ELF
在这里插入图片描述

安装并使用nasm

使用 sudo apt-get install nasm安装nasm 并用nasm -version查看是否安装成功
在这里插入图片描述
把示例文件hello.asm复制到linux中,或者直接放到共享文件夹下:
在这里插入图片描述
对示例代码“hello.asm”编译生成可执行程序:
在这里插入图片描述

查看大小:在这里插入图片描述

对比c代码:在这里插入图片描述

显然,汇编代码占用内存小得多。

借助第三方库函数完成代码设计

1)了解Linux 系统中终端程序最常用的光标库(curses)的主要函数功能,写出几个基本函数名称及功能;

2)在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
在这里插入图片描述3)在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库
在这里插入图片描述
ubunt下,该库的头文件在 /usr/include ,库在/usr/lib/i386-linux-gnu/libc.so。
4)体验Linux 环境下C语言编译贪吃蛇游戏(http://www.linuxidc.com/Linux/2011-08/41375.htm)复制代码到ubuntu下,保存为snake.c
在这里插入图片描述
然后在该处打开终端输入输入

cc snake.c -lcurses -o mysnake  //生成可执行文件

然后输入./mysnake 及可运行贪吃蛇小游戏,如下图。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值