Ubuntu深入学习
- 一、可执行程序是如何被组装的
- 二、用gcc生成静态库和动态库
- 1.编辑生成例子程序
- 2.将hello.c编译成.o文件
- 3.由.o 文件创建静态库文件
- 4.在程序中使用静态库
- 5.由.o 文件创建动态库文件
- 6.在程序中使用动态库
- 三、静态库应用
- 1.建立文件并写入代码
- 2.编译.o文件并建立静态库文件链接
- 四、动态库应用
- 1.建立文件并写入代码
- 2.建立动态库文件链接
- 五、Linux GCC常用命令
- 1.创建示例程序
- 2.预处理
- 3.编译为汇编代码
- 4.汇编
- 5.连接
- 6.多个程序文件的编译
- 7.检错
- 8.库文件连接
- 六.nasm应用
- 1.安装nasm
- 2.nasm使用
- 七.借助第三方库函数完成代码设计
- 1.光标库 curses
- 2.体验即将绝迹的远古时代的 BBS
- 3.curses库应用
- 4.贪吃蛇游戏
- 八.总结
一、可执行程序是如何被组装的
1.编辑也就是编写C/C程序。
2.预处理相当于根据预处理指令组装新的C/C程序。经过预处理会产生一个没有宏定义没有条件编译指令没有特殊符号的输出文件这个文件的含义同原本的文件无异只是内容上有所不同。
3.编译将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后产生相应的汇编代码文件。
4.链接通过链接器将一个个目标文件或许还会有库文件链接在一起生成一个完整的可执行程序。 链接程序的主要工作就是将有关的目标文件彼此相连接也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。在此过程中会发现被调用的函数未被定义。
二、用gcc生成静态库和动态库
1.编辑生成例子程序
先创建一个作业目录保存本次练习的文件
mkdir test1cd test1
这里使用vi命令创建并编辑3个所需文件
程序 1: hello.h
#ifndef HELLO_H#define HELLO_Hvoid hello(const char *name);#endif //HELLO_H
程序 2: hello.c
#include void hello(const char *name){printf("Hello %s!\n", name);}
程序 3: main.c
#include "hello.h"int main(){hello("everyone");return 0;}
2.将hello.c编译成.o文件
无论静态库还是动态库都是由.o 文件创建的。因此我们必须将源程序 hello.c 通过 gcc 先编译成.o 文件。 在系统提示符下键入以下命令得到 hello.o 文件。
gcc -c hello.c
然后用ls命令查看是否生成了hello.o文件
3.由.o 文件创建静态库文件
静态库文件名的命名规范是以 lib 为前缀紧接着跟静态库名扩展名为.a。 例如我们将创建的静态库名为 myhello则静态库文件名就libmyhello.a。在创建和使用静态库时 需要注意这点。创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
ar -crv libmyhello.a hello.o
然后用ls命令查看结果 ls 命令结果中有 libmyhello.a
4.在程序中使用静态库
静态库制作完了如何使用它内部的函数呢 只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明然后在用 gcc 命令生成目标文件时指明静态库名gcc 将会从静态库中将公用函数连接到目标文件中。 注意gcc 会在静态库名前加上前缀 lib然后追加扩展名.a 得到的静态库文件名来查找静态库文件。
#gcc main.c libmyhello.a -o hello
运行结果如下 我们删除静态库文件试试公用函数 hello 是否真的连接到目标文件 hello 中了
# rm libmyhello.arm: remove regular file libmyhello.a? y# ./helloHello everyone!#
5.由.o 文件创建动态库文件
动态库文件名命名规范和静态库文件名命名规范类似也是在动态库名增加前缀 lib但其文件扩展名为.so。例如我们将创建的动态库名为 myhello则动态库文件名就是 libmyhello.so。 用 gcc 来创建动态库。
gcc -shared -fPIC -o libmyhello.so hello.o
依然使用ls命令查看是否生成动态库文件libmyhello.so
6.在程序中使用动态库
在程序中使用动态库和使用静态库完全一样也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明然后在用 gcc 命令生成目标文件时指明动态库名进行编译。 我们先运行 gcc 命令生成目标文件再运行它看看结果。
gcc -o hello main.c -L. -lmyhello
# ./hello./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
运行结果如下 程序在运行时会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到则载入动态库否则将提示类似上述错误而终止程序运行。我们将文件 libmyhello.so 复制到目录/usr/lib 中再试试
mv libmyhello.so /usr/lib./helloHello everyone!
这里我们需要在命令行前加入sudo提供我们所需要的权限否则无法进行文件的复制 成功了。这也进一步说明了动态库在程序运行时是需要的。 我们回过头看看发现使用静态库和使用动态库编译成目标程序使用的 gcc 命令完全一样那当静态库和动态库同名时gcc 命令会使用哪个库文件呢抱着对问题必究到底的心情来试试看。 先删除除.c 和.h 外的所有文件恢复成我们刚刚编辑完举例程序状态。
rm -f hello hello.o /usr/lib/libmyhello.solshello.c hello.h main.c
这里同样使用了sudo运行结果如下 再来创建静态库文件 libmyhello.a 和动态库文件 libmyhello.so。
# gcc -c hello.c# ar -cr libmyhello.a hello.o 或-cvr # gcc -shared -fPIC -o libmyhello.so hello.o# lshello.c hello.h hello.o libmyhello.a libmyhello.so main.c
运行结果如下 通过上述最后一条 ls 命令可以发现静态库文件 libmyhello.a 和动态库文件 libmyhello.so 都已经生成并都在当前目录中。然后我们运行 gcc 命令来使用函数库 myhello 生成目标文件 hello并运行程序 hello。
# gcc -o hello main.c -L. –lmyhello# ./hello./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory#
三、静态库应用
静态库应用内容除了x2x函数之外再扩展写一个x2y函数功能自定main函数代码将调用x2x和x2y 将这3个函数分别写成单独的3个 .c文件并用gcc分别编译为3个.o 目标文件将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接生成最终的可执行程序记录文件的大小。
1.建立文件并写入代码
#gedit sub1.c#gedit sub2.c#gedit main.c
子函数sub1.c代码如下
float x2x(float a, float b) {return a * b;}
子函数sub2.c代码如下
float x2y(float a, float b) {return a b;}
主函数main.c代码如下
#includeextern float x2x(float a, float b);extern float x2y(float a, float b);int main() {float a 2;float b 3;printf("ax2xb%f\n",x2x(a,b));printf("ax2yb%f\n",x2y(a,b)); }
2.编译.o文件并建立静态库文件链接
#gcc -c sub1.c#gcc -c sub2.c#gcc -c main.cls
运行结果如下
由ar命令建立静态库
#ar -crv 1.a sub1.o sub2.o
调用sub1可执行程序结果如下
文件大小如下
四、动态库应用
动态库应用内容将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件, 然后用 gcc将 main函数的目标文件与此动态库文件进行链接生成最终的可执行程序记录文件的大小并与之前做对比。
1.建立文件并写入代码
文件与代码与三静态库应用的内容一致此处我们将.a和可执行程序删除后进行动态库文件的生成和应用
#rm 1.a#rm sub1ls
2.建立动态库文件链接
由.o文件用gcc创建动态库文件
#gcc -shared -fPIC -o 2.so sub1.o sub2.o
运行结果如下 主函数与动态库文件进行链接成可执行文件
运行结果如下
文件大小如下
总结动态库文件比静态库文件所占内存要小
五、Linux GCC常用命令
1.创建示例程序
//test.c#include int main(void){printf("Hello World!\n");return 0;}
2.预处理
gcc -E test.c -o test.i 或 gcc -E test.c
可以输出 test.i 文件中存放着 test.c 经预处理之后的代码。打开 test.i 文件看一看就明白了。后面那条指令是直接在命令行窗口中输出预处理后的代码. gcc 的-E 选项可以让编译器在预处理后停止并输出预处理结果。在本例中预处理结果就是将stdio.h 文件中的内容插入到 test.c 中了。 运行结果如下
3.编译为汇编代码
预处理之后可直接对生成的 test.i 文件编译生成汇编代码
gcc -S test.i -o test.s
gcc 的-S 选项表示在程序编译期间在生成汇编代码后停止-o 输出汇编代码文件。
4.汇编
对于上一小节中生成的汇编代码文件 test.sgas 汇编器负责将其编译为目标文件如下
gcc -c test.s -o test.o
5.连接
gcc 连接器是 gas 提供的负责将程序的目标文件与所需的所有附加的目标文件连接起来最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。对于上一小节中生成的 test.o将其与标准输入输出库进行连接最终生成程序 test
gcc test.o -o test
运行结果如下
6.多个程序文件的编译
通常整个程序是由多个源文件组成的相应地也就形成了多个编译单元使用 GCC 能够很好地管理 这些编译单元。假设有一个由 test1.c 和 test2.c 两个源文件组成的程序为了对它们进行编译并最终生成可执行程序 test可以使用下面这条命令
gcc test1.c test2.c -o test
如果同时处理的文件不止一个GCC 仍然会按照预处理、编译和链接的过程依次进行。如果深究起 来上面这条命令大致相当于依次执行如下三条命令
gcc -c test1.c -o test1.ogcc -c test2.c -o test2.ogcc test1.o test2.o -o test
7.检错
gcc -pedantic illcode.c -o illcode
-pedantic 编译选项并不能保证被编译程序与 ANSI/ISO C 标准的完全兼容它仅仅只能用来帮助Linux 程序员离这个目标越来越近。或者换句话说pedantic 选项能够帮助程序员发现一些不符合ANSI/ISO C 标准的代码但不是全部事实上只有 ANSI/ISO C 语言标准中要求进行编译器诊断的那些情况才有可能被 GCC 发现并提出警告。 除了-pedantic 之外GCC 还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W开头其中最有价值的当数-Wall 了使用它能够使 GCC 产生尽可能多的警告信息。
gcc -Wall illcode.c -o illcode
GCC 给出的警告信息虽然从严格意义上说不能算作错误但却很可能成为错误的栖身之所。一个优秀的 Linux 程序员应该尽量避免产生警告信息使自己的代码始终保持标准、健壮的特性。所以将警告信息当成编码错误来对待是一种值得赞扬的行为所以在编译程序时带上-Werror 选项那么 GCC 会在所有产生警告的地方停止编译迫使程序员对自己的代码进行修改如下
gcc -Werror test.c -o test
运行结果
8.库文件连接
编译成可执行文件 首先我们要进行编译 test.c 为目标文件这个时候需要执行
gcc –c –I /usr/dev/mysql/include test.c –o test.o
链接 最后我们把所有目标文件链接成可执行文件:
gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test
Linux 下的库文件分为两大类分别是动态链接库通常以.so 结尾和静态链接库通常以.a 结尾 二者的区别仅在于程序执行时所需的代码是在运行时动态加载的还是在编译时静态加载的。 强制链接时使用静态链接库 默认情况下 GCC 在链接时优先使用动态链接库只有当动态链接库不存在时才考虑使用静态链 接库如果需要的话可以在编译时加上-static 选项强制使用静态链接库。 在/usr/dev/mysql/lib 目录下有链接时所需要的库文件 libmysqlclient.so 和 libmysqlclient.a为了让 GCC 在链接时只用到静态链接库可以使用下面的命令:
gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test
静态库链接时搜索路径顺序
六.nasm应用
1.安装nasm
sudo apt-get install nasm//安装nasmnasm -version//查看版本
2.nasm使用
对示例代码“hello.asm”编译生成可执行程序并与“hello world”C代码的编译生成的程序大小进行对比。 所需代码 编译
nasm -f elf64 hello.asm
链接
ld -s -o hello hello.o
运行
./hello
运行结果如下 生成文件的大小
此处为“hello world”的C代码经过gcc编译生成的可执行程序的文件大小
总结nasm的文件比gcc生成的文件要小
七.借助第三方库函数完成代码设计
1.光标库 curses
函数名作用initscr()初始化curses库和ttyendwin()关闭curses并重置ttyrefresh()使屏幕按照你的意图显示move(r,c)move(r,c)addstr(s)在当前位置画字符串saddch(c在当前位置画字符cclear()清屏参考资料
https://blog.csdn.net/akof1314/article/details/5948578
2.体验即将绝迹的远古时代的 BBS
3.curses库应用
安装curses库
sudo apt-get install libncurses5-dev
安装过程遇到该错误
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?
参考资料得以解决
https://blog.csdn.net/pfanaya/article/details/6695810
其头文件被安装在/usr/include
4.贪吃蛇游戏
建立可执行文件代码如下 游戏如图所示
八.总结
学习过程艰难花费时间较多收获良多下次继续努力QAQ