GCC命令背后故事以及静、动态库应用

GCC命令背后故事以及静、动态库应用

一.用gcc生成静态库和动态库

静态库与动态库 .我们通常把一些公用函数制作成函数库 ,供其它程序使用。函数库分为静态库和动态库两种。 静态库在程序编译时 会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程 序编译时并不会被连接到目标代码中,而是在程序运行 时才被载入,因此在程序运行时还需 要动态库存在。 本文主要通过举例来说明在 Linux 中如何创建静态库和动态库,以及使用它们。

1. 建立子程序 hello.h、hello.c 和main.c。

首先建立一个工作目录test1,并在在此目录下建立需要的子程序文件,如下:

#mkdir test1 
#cd test1

然后用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的 3 个文件。
hello.c(见程序 2)是函数库的源程序,其中包含公用函数 hello,该函数将在屏幕上输出"Hello XXX!"。hello.h(见程序 1)为该函数库的头文件。main.c(见程序 3)为测试库文件的主程序, 在主程序中调用了公用函数 hello。
程序一: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; }
2. 将 hello.c 编译成.o 文件。
gcc -c hello.c

然后ls命令可以看到生成了hello.o
在这里插入图片描述

3. 由.o 文件创建静态库。

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

4. 在程序中使用静态库。(我们有三种方法)

   学会制作静态库后,要使用它内部的函数只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc 命令生成目标文件时指明静态库名,gcc 将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀 lib,然后追 加扩展名.a 得到的静态库文件名来查找静态库文件。

  以示例3 main.c 中,我们包含了静态库的头文件 hello.h,然后在主程序 main 中直接调用 公用函数 hello。下面先生成目标程序 hello,然后运行 hello 程序看看结果如何。

方法一:

#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

动态库连接时也可以这样做。(运行如图:)
在这里插入图片描述删除静态库文件试试公用函数 hello 是否真的连接到目标文件 hello 中了
在这里插入图片描述
从结果看出静态库中的公用函数已经连接到目标文件中了。

5. 由.o 文件创建动态库文件。

gcc -shared -fPIC -o libmyhello.so hello.o
在这里插入图片描述开始报错,我的解决办法:
在这里插入图片描述此时可以正常运行,使用ls 命令查看动态库文件已经生成libmyhello.so

6. 在程序中使用动态库

  在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含 这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。我 们先运行 gcc 命令生成目标文件,再运行它

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

我们会发现在运行hello的时候会产生报错。在这里插入图片描述这是因为虽然连接时用的是当前目录的动态库,但是运行时,是到 /usr/lib 中找库文件的,需要将文件 libmyhello.so 复制到目录/usr/lib 。在这里插入图片描述我们可以看到此时成功,这说明动态库说明在程序运行之时被载入。

我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的 gcc 命令完全一样, 那当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?抱着对问题必究到底的心情, 来试试看。 先删除除.c 和.h 外的所有文件,恢复成我们刚刚编辑完举例程序状态。

7. 静态库和动态库同名时,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 (或-cvr ) 
# gcc -shared -fPIC -o libmyhello.so hello.o

通过最后一条 ls 命令,可以发现静态库文件 libmyhello.a 和动态库文件 libmyhello.s o 都已经生成,并都在当前目录中。
我们运行 gcc 命令来使用函数库 myhello 生成目 标文件 hello,并运行程序 hello。# gcc -o hello main.c -L. –lmyhello在这里插入图片描述此时从程序 hello 运行的结果中可以看出,当静态库和动态库同名时,gcc 命令将优先使用动态库,将文件 libmyhello.so 复制到目录/usr/lib 中即可。
若如果直接#gcc main.c libmyhello.a -o hello #gcc main.c libmyhello.a -o hello 的话,就是指定为静态库了
在这里插入图片描述

8. Linux 下静态库.a 与.so 库文件的生成与使用

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

#mkdir test2 
#cd test2

A1.c:

#include <stdio.h> 
void print1(int arg)
{ printf("A1 print arg:%d\n",arg); 
}

A2.c

#include <stdio.h> 
void print2(char *arg)
{ printf("A2 printf arg:%s\n", arg); 
}

A.h

#ifndef A_H #define A_H void print1(int); void print2(char *); #endif

test.c

#include <stdlib.h> 
#include "A.h" 
int main()
{ print1(1); print2("test"); exit(0); }
1. 静态库.a 文件的生成与使用。

1.1 生成目标文件(xxx.o)

> gcc -c A1.c A2.c

在这里插入图片描述

1.2 生成静态库.a 文件

> ar crv libafile.a A1.o A2.o

在这里插入图片描述

1.3 使用.a 库文件,创建可执行程序(若采用此种方式,需保证生成的.a 文件与.c 文件保 存在同一目录下,即都在当前目录下)

gcc -o test test.c libafile.a

在这里插入图片描述

2. 共享库.so 文件的生成与使用

2.1 生成目标文件(xxx.o() 此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so 文件时会出错)
---> gcc -c -fpic A1.c A2.c
2.2 生成共享库.so 文件
---> gcc -shared *.o -o libsofile.so
2.3 使用.so 库文件,创建可执行程序
---> gcc -o test test.c libsofile.so
运行时发现错误,这是因为由于 linux 自身系统设定的相应的设置的原因,即其只在/lib and /usr/lib 下搜索对应 的.so 文件,故需将对应 so 文件拷贝到对应路径。在这里插入图片描述
--->sudo cp libsofile.so /usr/lib在这里插入图片描述再次执行./test,即可成功运行。

二. 生成静态库文件并连接记录大小

以一个程序做介绍:

1. 建立三个文件,main.c ,sub1.c ,sub2.c

程序一:main.c

#include"sub1.c"
#include"sub2.c"
int main()
{
int a,b;
printf("inpute number please:\n");
scanf("%d%d",&a,&b);
printf("%d x %d=%f\n",a,b,x2x(a,b));
printf("%d + %d=%d\n",a,b,x2y(a,b));
return 0;
}

程序二:sub1.c

float x2x(int a,int b)
{float c;
c=a*b;
return c;}

程序三: sub2.c

int x2y(int a,int b)
{float d;
d=a+b;
return d;}
2. 静态文件生成

2.1 生成三个目标文件

gcc -c main.c sub1.c sub2.c

2.2 生成静态文件.a文件
在这里插入图片描述
2.3 用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序
在这里插入图片描述
2.4 运行如图
在这里插入图片描述
2.5 记录文件大小
在这里插入图片描述

3. 生成动态文件库

3.1 生成目标文件(这里必须添加-fpic,否则会出错)在这里插入图片描述
3.2 生成动态库.so文件创建可执行程序在这里插入图片描述
3.3用 gcc将 main函数的目标文件与此动态库文件进行链接,生成最终的可执行程序
命令行:gcc -o main1 main.c libsofile.so在这里插入图片描述
执行程序main1在这里插入图片描述
3.4 记录文件的大小在这里插入图片描述
静态库文件和动态库文件大小相比看出,两个可执行程序文件一样大,但通常动态库要小一些。

三. gcc常用命令使用与gcc背后故事

GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL 语言,以及支持函数式编程和逻辑编程的 Mercury 语言,等等。
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译 (Compilation)、汇编 (Assembly)和连接(Linking)。

示例程序 test.c

//test.c #include <stdio.h> 
int main(void) 
{ printf("Hello World!\n"); return 0; }
1. 预处理:

gcc -E test.c -o test.i 或 gcc -E test.c
输出 test.i 文件中存放着 test.c 经预处理之后的代码。gcc 的-E 选项,可以让编译器在预处理后停止,并输出预处理结果。查看test.i
在这里插入图片描述

2. 编译为汇编代码:

预处理之后,可直接对生成的 test.i 文件编译,生成汇编代码:
gcc -S test.i -o test.s
gcc 的-S 选项,表示在程序编译期间,在生成汇编代码后,停止,-o 输出汇编代码文件。

3.汇编(Assembly)

对于上一小节中生成的汇编代码文件 test.s,gas 汇编器负责将其编译为目标文件 gcc -c test.s -o test.o

4.连接(Linking)

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

gcc test.o -o test
5.多个程序文件的编译

通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用 GCC 能够很好地管理 这些编译单元。假设有一个由 test1.c 和 test2.c 两个源文件组成的程序,为了对它们进行编译,并 最终生成可执行程序 test,可以使用下面这条命令:

gcc -c test1.c -o test1.o 
gcc -c test2.c -o test2.o 
gcc test1.o test2.o -o test
6.检错
gcc -pedantic illcode.c -o illcode

-pedantic 选项能够帮助程序员发现一些不符合 ANSI/ISO C 标准的代码,

gcc -Wall illcode.c -o illcode

GCC 还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W 开头,其中最有价值的当数-Wall 了,使用它能够使 GCC 产生尽可能多的警告信息。

gcc -Werror test.c -o test

在编译程序时带上-Werror 选项,那 么 GCC 会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改。

7. 库文件连接

GCC 在编译时必须用自己 的办法来查找所需要的头文件和库文件。
首先我们要进行编译 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 –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test

四. nasm汇编编译器编译

1. 用nasm编译文件nasm -f elf64 hello.asm

在这里插入图片描述

2. 将目标文件链接到可执行文件hello1

在这里插入图片描述

3. 运行该可执行文件

在这里插入图片描述

4. 记录文件的大小

在这里插入图片描述
c源代码编译形成的程序大小
在这里插入图片描述
对比两个文件大小,可以看出nasm编译器生成文件要比c源程序编译形成的文件要小。

五 .体验即将绝迹的bbs以及了解curse库

1.启用telnet client和适用于LINUX的windows子系统,(光标控制)
  1. 快捷键 Windows+r,输入control,即可进入控制面板

在这里插入图片描述
“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"。
2. 输入cmd即可进入命令行窗口
在这里插入图片描述按要求既可体验.

2.curse库
函数功能
initscr()初始化curses库和tty
endwin()关闭curses并重置tty
refresh(刷新屏幕显
mvaddch(y,x,c)在坐标(y,x)处显示字符c
mvaddstr(y,x,str)在坐标(y,x)处显示字符串str
cbreak()开启输入立即响应
noecho()输入不回显到屏幕
curs_set(0)使光标不可见
attrset()开启图形显示模式、
keypad(stdscr, true)开启小键盘方向键输入捕捉支持

curse库的头文件和库文件分别安装在 user/include和usr/lib下面。

六.Linux 环境下C语言编译实现贪吃蛇游戏

  1. 创建文件 mysnake1.0.c, 将游戏代码写入文件 mysnake1.0.c
 vim mysnake1.0.c
  1. 使用终端图形库curses.h文件
    需要自行下载安装,Ubuntu下可以这么下载 sudo apt-get install libncurses5-dev 已经替换成ncurses.h 即 new curses.h的意思,完全兼容curses
sudo apt-get install libncurses5-dev 

在这里插入图片描述

  1. 编译链接生成可执行文件mysnake.1.0
cc mysnake1.0.c -lcurses -o mysnake1.0
  1. 生成贪吃蛇游戏动画
    在这里插入图片描述
    从图中可以看到,完成了一个贪吃蛇游戏的编写与运行,更多用c语言的游戏制作可以参考网上相关代码。

总结:

  从这次学习GCC命令中,我们知道在GCC这一条简单的命令背后有着许多的步骤,从开始的.c文件到生成最后的可执行文件,中间也经历了许多过程。从了解静,动态库到学会运用静,动态库区链接文件,这样的收获真的很多。我还学会了编译运行一个小游戏,curse库的作用,更体验了 gcc编译的作用。一个可执行程序的组装更是多个的作用。希望在以后能更深入和熟练的掌握gcc命令。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值