文章目录
一、静态库.a的生成和使用
1、编译所需的3个.c文件
(1)创建一个目录,用于保存本次文件
(2)用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的3个文件 sub1.c 、 sub2.c和 main.c
sub1.c
sub2.c
main.c
2、生成.o目标文件
(1)用以下命令得到 .o 文件
(2)运行 ls 命令校验是否生成了.o 文件
3、生成.a 静态库文件
(1)将sub1.o、sub2.o目标文件用 ar工具生成1个.a 静态库文件
(2)运行 ls 命令校验是否生成了.a文件
4、使用.a 库文件,创建可执行程序
(1)用 gcc将 main函数的目标文件与静态库文件进行链接,生成可执行程序
(2)编译运行程序main.c
5、记录文件大小
二、 动态库.so的生成和使用
1、生成.o目标文件
(此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so 文件时会出错)
2、生成.so 动态库文件
(1)用以下命令得到 .so 文件
(-o不可少)
(2)运行 ls 命令校验是否生成了.so文件
3、使用.so 库文件,创建可执行程序
(1)用 gcc将 main函数的目标文件与动态库文件进行链接,生成可执行程序
(2)编译运行程序main.c
发现错误,运行
ldd main
,查看链接情况
发现确实是找不到对应的.so 文件
这是由于 linux 自身系统设定的相应的设置的原因,即其只在/lib或/usr/lib下搜索对应的.so文件,故需将对应.so文件拷贝到对应路径。
再次执行./main,即可成功运行
同时可直接使用
gcc -o test test.c -L. -lname
,来使用相应库文件
其中:
① -L.:表示在当前目录下,可自行定义路径path,即使用-Lpath即可。
②-lname:name:即对应库文件的名字(除开 lib),即若使用libafile.a,则name为 afile;若要使用libsofile.so,则name为sofile)。
4、记录文件大小
(1)用以下命令获得文件大小
(2)与静态库中的文件大小相比较,发现两个可执行程序文件的大小差不多
三、关于可执行程序如何被组装
一个源程序到一个可执行程序的过程:预编译、编译、汇编、链接。
①预编译:主要处理源代码文件中的以“#”开头的预编译指令。
②编译:把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码文件。
③汇编:将汇编代码转变成机器可以执行的指令(机器码文件)。
④链接
其中,②编译是主要部分,其中又分为六个部分:词法分析、语法分析、语义分析、中间代码生成、目标代码生成和优化。
④链接分为静态链接和动态链接,本文主要是静态链接。
(具体要深入了解可以访问该博客:https://blog.csdn.net/qq_39755395/article/details/78293733)
四、Linux GCC常用命令
1、gcc的编译过程
示例程序如下:
//test.c
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
这个程序,一步到位的编译指令是:
gcc test.c -o test
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译 (Compilation)、汇编 (Assembly)和连接(Linking)。
(1)预处理
gcc -E test.c -o test.i
或gcc -E test.c
可以输出 test.i 文件中存放着 test.c 经预处理之后的代码
-E :可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将 stdio.h 文件中的内容插入到 test.c 中了。
(2)编译为汇编代码
预处理之后,可直接对生成的 test.i 文件编译,生成汇编代码:
gcc -S test.i -o test.s gcc
-S :表示在程序编译期间,在生成汇编代码后,停止,-o 输出汇编代码文件。
(3)汇编
对于上一小节中生成的汇编代码文件 test.s,gas 汇编器负责将其编译为目标文件,如下:
gcc -c test.s -o test.o
(4)连接
gcc 连接器是 gas 提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生 成可执行文件。
附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的 test.o,将其与C标准输入输出库进行连接,最终生成程序:
test gcc test.o -o test
2、多个程序文件的编译
通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用 GCC 能够很好地管理 这些编译单元。
假设有一个由 test1.c 和 test2.c 两个源文件组成的程序,为了对它们进行编译,并 最终生成可执行程序 test,可以使用下面这条命令:
gcc test1.c test2.c -o test
如果同时处理的文件不止一个,GCC 仍然会按照预处理、编译和链接的过程依次进行。如果深究起 来,上面这条命令大致相当于依次执行如下三条命令:
gcc -c test1.c -o test1.o
gcc test1.o test2.o -o test
gcc -c test2.c -o test2.o
3、gcc中二进制程序处理工具
GCC(GNU C Compiler)是编译工具。二进制程序处理工具是将 C/C++语言编写的程序转换成为处理器能够执行的二进制代码的过程即由编译器完成。
一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、 ldd、readelf、 size 等。这 一组工具是开发和调试不可缺少的工具 ,分别简介如下:
(1)addr2line:用来将程序地址转换成其所对应的程序源件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置。
(2) as:主要用于汇编。
(3) ld:主要用于链接。
(4) ar:主要用于创建静态库。
(5) ldd:可以用于查看一个可执行程序依赖的共享库。
(6)objcopy:将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或者将.elf 转换成.bin等。
(7)objdump:主要的作用是反汇编。
(8)readelf:显示有关 ELF 文件的信息。
(9) size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小 等。
五、nasm汇编编译器
as汇编编译器针对的是AT&T汇编代码风格,Intel风格的汇编代码则可以用nasm汇编编译器编译生成执行程序。
1、在ubuntu中下载安装nasm
用以下命令安装nasm
2、对示例代码“hello.asm”编译生成可执行程序
(1)建立一个asm文件,并进入编辑
(2)输入代码“hello.asm”
代码如下:
; 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 ; 调用内核功能
(3)生成.o目标文件并用ls命令查看是否生成
(4)生成可执行文件并用ls命令查看是否生成
(5)运行程序并记录文件大小
文件大小
3、程序之间的大小对比
将上述程序与“hello world”C代码的编译生成的程序大小进行对比
(1)hello.c文件大小
(2)hello.asm文件大小
经对比,对示例代码“hello.asm”编译生成的可执行程序比“hello world”C代码的编译生成的程序要小得多。
六、关于实际程序如何借助第三方库函数完成代码设计
1、Linux系统中光标库的函数及功能
几个基本函数名称及功能如下
(1)从屏幕读取字符
1 chtype inch(void); //返回光标位置字符
2 int instr(char *string); //
3 int innstr(char *string, int numbers); //将返回内容写入字符数组中
(2)清除屏幕
int erase(void); //在屏幕的每个位置写上空白字符
int clear(void); //使用一个终端命令来清除整个屏幕,内部调用了clearok来执行清屏操作,(在下次调用refresh时可以重现屏幕原文)
int clrtobot(void); //清除光标位置到屏幕结尾的内容
int clrtoeol(void); //清除光标位置到该行行尾的内容
(3)键盘输入
int getch();
int getstr(char *string);
int getnstr(char *string, int number); //建议使用
int scanw(char*format,...);
(4)移动和更新窗口
int mvwin(WINDOW *win, int new_y, int new_x); //移动窗口
int wrefresh(WINDOW *win);
int wclear(WINDOW *win);
int werase(WINDOW *win);
//类似于上面的refresh, clear, erase,但是此时针对特定窗口操作,而不是strcsr
int touchwin(WINDOW *win); //指定该窗口内容已改变
int scrollok(WINDOW *win, bool flag); //指定是否允许窗口卷屏
int scroll(WINDOW *win); //把窗口内容上卷一行
(5)移动光标
int move(int new_y, int new_x); //移动stdcsr的光标位置
更详细的参考:
Linux下curses函数库
2、远古时代的 BBS:一个用键盘光标控制的终端程序
(1)在 win10 系统中,打开“控制面板”–>“程序”
(2)“启用或关闭Windows功能”
(3) 启用 “telnet client” 和"适用于Linux的Windows子系统"
此时会要求重启电脑
(4)重启之后打开一个cmd命令行窗口
①关于打开一个cmd命令行窗口见链接:
https://zhinan.sogou.com/guide/detail/?id=316513277418
②命令行输入 telnet bbs.newsmth.net即可体验一个用键盘光标控制的终端程序
3、Ubuntu中安装curses库
(1)在Ubuntu中输入命令sudo apt-get install libncurses5-dev
等待安装完毕即可
(2)头文件(比如curses.h)和库文件被分别安装在/usr/include/和/usr/lib/下
输入命令ls +目录名称,即可显示里面的文件
4、用gcc编译生成一个终端游戏
用gcc编译生成一个终端游戏,体会curses库如何被链接和使用
(1)创建一个新的.c文件
(2)输入代码
代码参考: Linux 环境下C语言编译实现贪吃蛇游戏或弹球游戏或https://blog.p2hp.com/archives/3615 中列举的10个终端游戏
此处我选择的是贪吃蛇游戏
(3)生成可执行文件
(4)运行程序即可进入贪吃蛇游戏