编译器背后的故事
引入:gcc会对代码完成预处理,编译,汇编和链接四个步骤
gcc常用命令
一.用gcc生成静态库和动态库
1.按照书上的例子学习
1.1先创建执行程序所需要的文件,hello.c,main.c ,hello.h
1.2用gcc命令生成.o文件
静态库
1.3用ar命令生成静态libmyhello.a文件
1.4再链接.a文件和main.c文件生成可执行文件hello
动态库
1.5将hello.c文件编译生成.so共享动态库
1.6动态链接.so文件和main.c 文件生成可执行文件
2.自己编写程序练习静态库和动态库的步骤,并记录可执行文件的大小
2.1创建程序所需的x2x.c,x2y.c,try.c,X.h。
2.2按照上面的步骤生成静态链接库生成的可执行文件
2.3可执行文件的大小为8456b
2.4再用动态链接库生成可执行文件
2.5记录文件的大小为8352b
注:linux中查看文件大小的命令
stat try(文件名)
参考链接:
https://blog.csdn.net/longgeaisisi/article/details/90607642
3.使用静态库和动态库最后生成的可执行文件大小的比较
结论:动态链接生成的可执行文件要略小于静态链接生成的可执行文件
二、gcc不是一个人在战斗,gcc的集中用途,了解EFF文件格式,汇编语言格式
1.gcc的集中使用
创建一个简单的测试程序
一步到位的编译指令是:
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和连接(Linking)
1.1,预处理
gcc 的-E 选项,可以让编译器在预处理后停止,并输出预处理结果
1.2编译为汇编代码
gcc 的-S 选项,表示在程序编译期间,在生成汇编代码后,停止,-o 输出汇编代码文件
1.3汇编,将汇编代码文件生成目标文件
说明gas 汇编器负责将其编译为目标文件
1.4连接
说明gcc 连接器负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件
1.5执行文件
2.EFF文件格式
(1)分析 ELF 文件
1.ELF 文件的段
ELF 文件格式如下图所示,位于 ELF Header 和 Section Header Table 之间的都是段(Section)。一个典型的 ELF 文件包含下面几个段:
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
可以使用 readelf -S 查看其各个 section 的信息如下:
readelf -S test
(2)反汇编EFF文件
由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包含的指令和数据,需要使用反汇编的方法。
使用 objdump -D 对hello文件进行反汇编如下:
objdump -D hello
也使用 objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来。
gcc -o hello -g hello.c
objdump -S hello
3.nasm汇编编译器的运用
1.先安装nasm,在Linux环境下输入如下命令:
sudo apt-get install nasm
2.查看nasm是否安装成功,并开始用nasm进行编译
nasm -version
3.创建一个.asm文件,z在其中输入以下测试代码
; hello.asm
section .data ; 数据段声明
msg db "Hello, world!", 0xA ; 要输出的字符串
len equ $ - msg ; 字串长度
section .text ; 代码段声明
global _start ; 指定入口函数
_start: ; 在屏幕上显示一个字符串
mov edx, len ; 参数三:字符串长度
mov ecx, msg ; 参数二:要显示的字符串
mov ebx, 1 ; 参数一:文件描述符(stdout)
mov eax, 4 ; 系统调用号(sys_write)
int 0x80 ; 调用内核功能
; 退出程序
mov ebx, 0 ; 参数一:退出代码
mov eax, 1 ; 系统调用号(sys_exit)
int 0x80 ; 调用内核功能
4.用nasm编译链接如下结果
5.记录可执行文件大小并比较
(1)nasm
(2)c代码编译
结论:从这里可以看出nasm汇编代码要比c代码文件小得多
三.程序背后的优秀代码库
1.Linux 系统中终端程序最常用的光标库(curses)
(1)光标库的主要函数功能
curses函数库能够优化光标的移动并最小化需要对屏幕进行的刷新,从而也减少了必须向字符终端发送的字符数目。
(2)几个基本函数名称及功能
(2.1)WINDOW *initscr(void)
initscr()函数通常是Curses程序第一个调用函数,用于判断终端类型和初始化Curses数据结构,同时也对终端进行一次刷新以清除屏幕,为以后的操作做准备。若执行时发生错误则在标准错误输出中输出错误信息并退出,正确执行就返回stdscr这一属于WINDOW类型的变量。
(2.2)int endwin(void)
每一个Curses程序在退出或要临时脱离Curses模式时都应调用endwin()函数。调用这个函数将恢复tty终端原来的状态,把光标移到屏幕的左下角.重置终端为正确的非虚拟模式。
(2.3)bool has_colors(void)
has_colors()例程无需参数。若终端能产生彩色,则返回TRUE﹔否则返回FALSE。程序员通过调用它去判断终端是否可以使用彩色。
(2.4)int use_default_colors(void)
use_default_colors例程是Curses库的一个扩展.它告诉Curses库把终端设置为缺省的前景/背景.缺省时前景为白色.背景为黑色.成功设置返回OK,否则返回ERR,两者都是整型数值。
(2.5)int init_pair(short pair,short f, short b)
init_pair()例程用于更改一个彩色对的定义。彩色对是Curses的一个概念,它用一个整型数值去标志一对前景/背景彩色。该例程有三个参数﹔彩色对数值pair,其范围从1到
COLOR_PAIRS-1;f(foreground指定前景彩色;b(background指定背景彩色。
2.以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)
1.在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。
2.然后打开一个cmd命令行窗口,命令行输入
telnet bbs.newsmth.net
3.在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库,请说明 头文件(比如curses.h)和库文件都被安装到哪些目录中
头文件(比如curses.h)和库文件的安装目录在哪?
curses函数库的头文件和库文件就被分别安装在/usr/include/和/usr/lib/下
4.Linux 环境下C语言编译实现贪吃蛇游戏
1.创建一个snake.c的文件,将游戏代码复制进去
游戏代码:
http://www.linuxidc.com/Linux/2011-08/41375.htm
2.用curses编译链接生成可执行文件
注:警告不用管它,直接运行生成的可执行文件
四.总结
学习到了静态链接库和动态链接库的区别,ar汇编编译器用法,光标库,还有以前的bbs,以及在Linux系统下的游戏。