Ubuntu深入学习

本文详细介绍了Linux环境下GCC的使用,包括创建静态库和动态库的步骤,以及在程序中如何使用这些库。通过实例展示了从源代码到可执行程序的全过程,涉及预处理、编译、链接等阶段。同时,讲解了静态库与动态库的区别,并探讨了在链接时的选择。此外,还讨论了nasm的安装和使用,以及curses库在代码设计中的应用,如实现简单的贪吃蛇游戏。
摘要由CSDN通过智能技术生成


一、可执行程序是如何被组装的?

1.编辑:也就是编写C/C++程序。

2.预处理:相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。

3.编译:将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。

4.链接:通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序。 链接程序的主要工作就是将有关的目标文件彼此相连接,也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。在此过程中会发现被调用的函数未被定义。

二、用gcc生成静态库和动态库

1.编辑生成例子程序

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

mkdir test1
cd test1

在这里插入图片描述
这里使用vi命令创建并编辑3个所需文件:

程序 1: hello.h

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

程序 2: hello.c

#include <stdio.h>
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.a
rm: remove regular file `libmyhello.a'? y
# ./hello
Hello 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 shar
ed object file: No such file or directory

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

mv libmyhello.so /usr/lib
 ./hello
Hello everyone!

这里我们需要在命令行前加入sudo,提供我们所需要的权限,否则无法进行文件的复制
在这里插入图片描述
成功了。这也进一步说明了动态库在程序运行时是需要的。
我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的 gcc 命令完全一样,那当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。
先删除除.c 和.h 外的所有文件,恢复成我们刚刚编辑完举例程序状态。

rm -f hello hello.o /usr/lib/libmyhello.so
ls
hello.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
# ls
hello.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 shar
ed 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代码如下

#include<stdio.h>
extern 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.c
ls

运行结果如下
在这里插入图片描述

由ar命令建立静态库

#ar -crv 1.a sub1.o sub2.o

在这里插入图片描述
调用sub1可执行程序结果如下
在这里插入图片描述
文件大小如下
在这里插入图片描述

四、动态库应用

动态库应用内容:将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件, 然后用 gcc将 main函数的目标文件与此动态库文件进行链接,生成最终的可执行程序,记录文件的大小,并与之前做对比。

1.建立文件并写入代码

文件与代码与(三)静态库应用的内容一致,此处我们将.a和可执行程序删除后,进行动态库文件的生成和应用

#rm 1.a
#rm sub1
ls

在这里插入图片描述

2.建立动态库文件链接

由.o文件,用gcc创建动态库文件

#gcc -shared -fPIC -o 2.so sub1.o sub2.o

运行结果如下:
在这里插入图片描述
主函数与动态库文件进行链接成可执行文件
在这里插入图片描述
运行结果如下
在这里插入图片描述
文件大小如下
在这里插入图片描述

总结:动态库文件比静态库文件所占内存要小

五、Linux GCC常用命令

1.创建示例程序

//test.c
#include <stdio.h>
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.s,gas 汇编器负责将其编译为目标文件,如下

gcc -c test.s -o test.o

在这里插入图片描述

5.连接

gcc 连接器是 gas 提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。对于上一小节中生成的 test.o,将其与C标准输入输出库进行连接,最终生成程序 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.o
gcc -c test2.c -o test2.o
gcc 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

静态库链接时搜索路径顺序:

  1. ld 会去找 GCC 命令中的参数-L
  2. 再找 gcc 的环境变量 LIBRARY_PATH
  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初 compile gcc 时写在程序内的
    动态链接时、执行时搜索路径顺序:
  4. 编译目标代码时指定的动态库搜索路径
  5. 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径
  6. 配置文件/etc/ld.so.conf 中指定的动态库搜索路径
  7. 默认的动态库搜索路径/lib
  8. 默认的动态库搜索路径/usr/lib

六.nasm应用

1.安装nasm

sudo apt-get install nasm//安装nasm
nasm -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库和tty
endwin()关闭curses并重置tty
refresh()使屏幕按照你的意图显示
move(r,c)move(r,c)
addstr(s)在当前位置画字符串s
addch(c)在当前位置画字符c
clear()清屏

参考资料:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值