Linux系统中关于gcc生成静态库和动态库,库函数的基础操作(编译器背后的故事)以及相关库的使用,程序游戏介绍

一.源程序到可执行程序的过程

一个源程序到一个可执行程序的过程,分为以下四步:预编译、编译、汇编、链接。其中,编译是主要部分,其中又分为六个部分:词法分析、语法分析、语义分析、中间代码生成、目标代码生成和优化,链接中,分为静态链接和动态链接两个部分。同样在在gcc的编译流程中通常认为也是四个步骤:(1)预处理,(2)编译,(3)汇编,(4)链接,实际上就是将命令分成4步执行。

二.练习(关于用gcc生成静态库和动态库)

(1)练习1

链接: 练习PDF文件.

我们通常把一些公用函数制作成函数库,供其它程序使用。
函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中,
程序运行时将不再需要该静态库。
动态库在程序编译时并不会被连接到目标代码中,
而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

(静态库)

(1)先创建一个作业目录并进入该目录,保存本次练习的文件。

mkdir test1
cd test1

(2)然后用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的 3 个文件。(此处用vim)
hello.h文件:
在这里插入图片描述

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

hello.c文件:
在这里插入图片描述

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

main.c文件
在这里插入图片描述

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

(3)将 hello.c 编译成.o 文件,并用ls命令查看
在这里插入图片描述
(4)由.o 文件创建静态库,并用ls查看生成的文件。
静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为 myhello,则静态库文件名就是 libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件
libmyhello.a。

在这里插入图片描述

创建静态库文件代码:

ar -crv libmyhello.a hello.o

(5)在程序中使用静态库(此处有三种方法,我演示第一种)
方法一:
在这里插入图片描述

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

自定义的库时,main.c 还可放在-L.和 –lmyhello 之间,但是不能放在它俩之后,否则会提示 myhello 没定义,但是是系统的库时,如 g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。
方法二:

gcc main.c libmyhello.a -o hello

方法三:
先生成 main.o:

gcc -c main.c

再生成可执行文件:

gcc -o hello main.o libmyhello.a

动态库连接时也可以这样做。
(6)删除静态库文件公用函数 hello。

rm libmyhello.a

程序照常运行,静态库中的公用函数已经连接到目标文件中了。

(动态库)

(1)由.o 文件创建动态库文件。
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其
文件扩展名为.so。例如:我们将创建的动态库名为 myhello,则动态库文件名就是 libmyh
ello.so。用 gcc 来创建动态库。

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

( 动态库那里,一些人的系统是ubuntu16,从hello.c 生成 hello.o,需要将 gcc -c hello.c 换成 gcc -fpic -c hello.c)
(2)在程序中使用动态库

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

(或 gcc main.c libmyhello.so -o hello 不会出错(没有 libmyhello.so 的话,会出错),但是接下来./hello 会提示出错,因为虽然连接时用的是当前目录的动态库,但是运行时,是到/usr/lib 中找库文件的,将文件 libmyhello.so 复制到目录/usr/lib 中就 OK 了)

./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

(此处报错,原来是找不到动态库文件 libmyhello.so。程序在运行时,会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件 libmyhello.so 复制到目录/usr/lib 中,再试试。)

mv libmyhello.so /usr/lib

在这里插入图片描述
成功啦!!!!!!
(3)假设那当静态库和动态库同名时,gcc 命令会使用哪个库文件呢
先删除除.c 和.h 外的所有文件,恢复成我们刚刚编辑完举例程序状态。

rm -f hello hello.o /usr/lib/libmyhello.so
ls
hello.c hello.h main.c

再来创建静态库文件 libmyhello.a 和动态库文件 libmyhello.so。

gcc -c hello.c
ar -cr libmyhello.a hello.o 
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

(动态库和静态库同时存在时,优先使用动态库,当然,如果直接的话,就是指定为静态库了)

gcc main.c libmyhello.a -o hello 
./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory

从程序 hello 运行的结果中很容易知道,当静态库和动态库同名时,gcc 命令将优先使用动态库,默认去连/usr/lib 和/lib 等目录中的动态库,将文件 libmyhello.so 复制到目录/usr/lib中即可。

(2)练习2 Linux 下静态库.a 与.so 库文件的生成与使用(与练习一类似)

(1)与前者相似创建目录,然后用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的四个文件 A1.c 、 A2.c、 A.h、test.c在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)静态库.a 文件的生成与使用。
在这里插入图片描述
(3)使用.a 库文件,创建可执行程序(若采用此种方式,需保证生成的.a 文件与.c 文件保存在同一目录下,即都在当前目录下)
在这里插入图片描述
在这里插入图片描述
(4)动态库,生成目标文件(xxx.o() 此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so文件时会出错)
在这里插入图片描述
运行 ldd test,查看链接情况
在这里插入图片描述
这是由于 linux 自身系统设定的相应的设置的原因,即其只在/lib and /usr/lib 下搜索对应的.so 文件,故需将对应 so 文件拷贝到对应路径。
在这里插入图片描述
成功啦!!!!!!!!!

(3) 练习3(紧接上次的练习进行操作: 链接.)

(1)增添新的函数x2y
在这里插入图片描述
(2)用gcc分别编译为3个.o 目标文件
在这里插入图片描述
(3)将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件在这里插入图片描述
(4)然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序
在这里插入图片描述
(5)将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件,步骤与练习一一致
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(6)记录文件的大小,并与之前做对比。
在这里插入图片描述

在这里插入图片描述
通过比较发现静态库要比动态库要小很多,生成的可执行文件大小只存在较小差距

三.gcc编译工具集中各软件的用途,了解EFF文件格式,汇编语言格式。

(1)gcc常用命令

GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年的发展,GCC 已经不仅仅能支持 C语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言,以及支持函数式编程和逻辑编程的 Mercury 语言,等等。而 GCC 也不再单只是 GNU C 语言编译器的意思了,而是变成了 GNU Compiler Collection 也即是 GNU 编译器家族的意思了。另一方面,说到 GCC 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。

gcc常用编译代码

gcc常用的编译代码
这里只介绍部分
1、ar
用于创建静态链接库。为了便于初学者理解,在此介绍动态库与静态库 的概念:
1)如果要将多个.o 目标文件生成一个库文 件,则存 在两种类型的库,一种是静态库,另一种是动态库。
2)在 windows 中静态库是 以 .lib 为 后缀 的文 件 ,共享库 是以 .dll 为 后缀 的 文 件 。在 linux 中静 态库是以 .a 为 后 缀 的 文 件 , 共 享 库 是 以 .so 为 后 缀 的文件。
3) 静 态 库 和 动 态 库 的 不 同 点 在 于 代 码 被 载 入 的 时 刻 不 同 。 静 态 库 的 代 码 在 编 译 过 程 中 已 经 被 载 入 可 执 行 程 序 , 因 此 体 积 较 大 。 共 享 库 的 代 码 是 在 可 执 行 程 序 运 行 时 才 载 入 内 存 的 , 在 编 译 过 程 中 仅 简 单 的 引 用 , 因 此 代 码 体 积 较 小 。 在 Linux 系 统 中 , 可 以 用 ldd 命 令 查 看 一 个 可 执 行 程 序 依 赖 的 共 享 库。
4)如 果 一 个 系 统 中 存 在 多 个 需 要 同 时 运 行 的 程 序 且 这 些 程 序 之 间 存 在 共 享 库,那么采用动态库的形式将更节省内存。
2、ld
用于链接。
3、as
用于汇编。
4、ldd
可以用于查看一个可执行程序依赖的共享库。
5、size
查看执行文件中各部分的大小。
6、addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码 行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对 应的源代码位置。

单文件编译

示例:

#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

2.编译为汇编代码

gcc -S test.i -o test.s

3.汇编

gcc -c test.s -o test.o

4.链接

gcc test.o -o test

在命令行窗口中,执行./test, 让它说 HelloWorld 吧!

多文件编译

通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用 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

检错

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

编译成可执行文件

首先我们要进行编译 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

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

默认情况下, 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

(2)GCC编译器背后的故事

GCC编译器背后的故事.提取码:wlzy

linux下gcc命令详解.

(3)as汇编编译器

1.安装

sudo apt install nasm

在这里插入图片描述
2.使用nasm对示例代码“hello.asm”编译生成可执行程序
创建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.编译输出:
在这里插入图片描述
在这里插入图片描述
4.与“hello world”C代码的编译生成的程序大小进行对比
hello.asm文件编译输出后的大小:
在这里插入图片描述
“hello world”C代码的编译生成的程序大小:
在这里插入图片描述

四.Linux中的第三方库函数

(1)Linux 系统中终端程序最常用的光标库的主要函数功能

curses函数库能够优化光标的移动并最小化需要对屏幕进行的刷新,从而也减少了必须向字符终端发送的字符数目。

(2)几个基本函数名称及功能

从屏幕读取:

chtype inch(void);  //返回光标位置字符
int instr(char *string);  //读取字符到string所指向的字符串中
int innstr(char *string, int numbers);//读取numbers个字符到string所指向的字符串中

清除屏幕:

int erase(void);//在屏幕的每个位置写上空白字符
int clear(void);//使用一个终端命令来清除整个屏幕,相当于vi内的Ctrl+L
//内部调用了clearok来执行清屏操作,(在下次调用refresh时可以重现屏幕原文)

int clrtobot(void);//清除光标位置到屏幕结尾的内容
int clrtoeol(void);//清除光标位置到该行行尾的内容

移动光标:

int move(int new_y, int new_x);    //移动stdcsr的光标位置
int leaveok(WINDOW *window_ptr,bool leave_flag);
//设置一个标志,用于控制在屏幕刷新后curses将物理光标放置的位置。

窗口移动和更新屏幕:

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,但是此时针对特定窗口操作,而不是stdcur

int touchwin(WINDOW *win);     //指定该窗口内容已改变、
//下次wrefresh时,需重绘窗口。利用该函数,安排要显示的窗口
	
int scrollok(WINDOW *win, bool flag);    //指定是否允许窗口卷屏
int scroll(WINDOW *win);   //把窗口内容上卷一行

窗口优化屏幕刷新:

int wnoutrefresh(WINDOW *window_ptr);
//The wnoutrefresh subroutine determines which parts of the terminal may need updating.
	
int doupdate(void);
//The doupdate subroutine sends to the terminal the commands to perform any required changes.

(3)体验即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)

1.在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.找到以下两个文件勾选:
1).
在这里插入图片描述
2).
在这里插入图片描述
3.打开一个cmd命令行窗口(win+r 输入cmd即可进入)命令行输入 telnet bbs.newsmth.net,即可体验

(4)curses库的安装

1.在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库
在这里插入图片描述

2.头文件(比如curses.h)和库文件的安装目录
curses函数库的头文件和库文件就被分别安装在/usr/include/和/usr/lib/下

五.Linux 环境下C语言编译实现一些游戏

linux环境下c语言编译实现贪吃蛇游戏.
curses库实现的弹球游戏.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值