Ubuntu18.04下C语言程序的编译原理
我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
本文主要通过举例来说明在 Linux 中如何创建静态库和动态库,以及使用它们。
一、用gcc生成静态库和动态库
(1)生成x2x.c、x2y.c、main1.c C语言文件
x2x.h
#ifndef __x2x_H
#define __x2x_H
#include<stdio.h>
float x2x(int a,int b);
#endif
x2x.c(两个数相乘)
#include "x2x.h"
float x2x(int a,int b)
{
return a*b;
}
x2y.h
#ifndef __x2y_H
#define __x2y_H
#include<stdio.h>
float x2y(int a1,int b1);
#endif
x2y.c(两个数相加)
#include "x2y.h"
float x2y(int a1,int b1)
{
return a1+b1;
}
main1.c
#include"x2x.h"
#include"x2y.h"
int main()
{
int x=2,y=9;
printf("%.2f\n",x2x(x,y));
printf("%.2f\n",x2y(x,y));
return 0;
}
(2)使用gcc分别将.c文件编译为3个.o 目标文件
无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将程序通过gcc先编译成.o 文件。
1、使用gcc命令
gcc -c x2x.c x2y.c main1.c
2、输入ls查看结果
可以看到已经生成了相关的.o文件。
(3)由.o文件生成静态库
静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名。
1、输入gcc命令
ar crv libtest.a x2x.o x2y.o
2、使用ls查看运行结果
(4) 在程序中使用静态库
静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包 含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从 静态库中将公用函数连接到目标文件中。注意,gcc 会在静态库名前加上前缀 lib,然后追加扩展名.a 得到的静态库文件名来查找静态库文件。
1、输入gcc命令
gcc main1.c libtest.a -o main1
运行main1查看结果
./main1
运行结果:
查看main1大小:
(5)由.o 文件创建动态库文件
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其文件扩展名为.so。
1、输入gcc命令
gcc -shared -fPIC -o libtest.so main1.o
用ls命令运行结果:
(6)在程序中使用动态库
1、输入gcc命令
gcc main1.c libtest.so -o main1
./main1
运行结果:
我们可以看到有错误提示:找不到动态库文件libtest.so。这是因为程序在运行时,会在 /usr/lib 和 /lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提
示类似上述错误而终止程序运行。
解决方法:我们将文件 libtest.so 复制到目录 /usr/lib 中。
sudo mv libtest.so /usr/lib
./main1
运行结果:
(7)静态库与动态库比较
静态库文件的大小:
使用静态库生成可执行文件的大小:
动态库文件的大小:
使用动态库生成可执行文件的大小:
可以看到静态库文件要比动态库文件要小。
二、as汇编编译器
(1)下载安装nasm
1、输入gcc命令
sudo apt-get install nasm
运行结果:
(2)对示例代码“hello.asm”编译生成可执行程序
1、创建“hello.asm”文件
vim 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 ; 调用内核功能
2、生成可执行文件
编译:
nasm -f elf64 hello.asm
链接:
ld -s -o hello hello.o
运行:
./hello
运行结果:
大小:
(3)用C语言编译生成程序
1、创建"hello1.c"
vim hello1.c
hello1.c
#include<stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
编译且运行:
gcc hello1.c -o hello1
./hello1
运行结果:
C语言编译生成的可执行文件大小:
nasm编译生成可执行文件大小:
可以看到,C语言编译生成的可执行文件要比nasm要大。
三、Linux中的第三方库函数
(1)最常用的光标(curses)的主要函数功能
1、 游标的控制:
move(y,x): 将游标移动至 x,y 的位置.
getyx(win,y,x): 得到目前游标的位置. (请注意! 是 y,x 而不是&y,&x)
2、 有关清除屏幕:
clear() and erase(): 将整个萤幕清除. (请注意配合refresh() 使用)
3、 如何在屏幕上显示字元:
echochar(ch): 显示某个字元.
addch(ch): 显示某个字元.
mvaddch(y,x,ch): 在(x,y)上显示某个字元. 相当于呼叫move(y,x);
addch(ch); addstr(str): 显示一串字串.
mvaddstr(y,x,str): 在(x,y) 上显示一串字串. 相当於呼叫move(y,x);addstr(str);
printw(format,str): 类似 printf() , 以一定的格式输出至屏幕.
mvprintw(y,x,format,str): 在(x,y) 位置上做 printw 的工作,相当於呼叫move(y,x);printw(format,str);
4、如何从键盘上读取字元:
getch(): 从键盘读取一个字元. (注意! 传回的是整数值)
getstr(): 从键盘读取一串字元.
scanw(format,&arg1,&arg2...): 如同 scanf, 从键盘读取一串字元.
(2)以游客身份体验一下即将绝迹的远古时代的 BBS
在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client"和"适用于Linux的Windows子系统”(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net
(3) 查看头文件与库文件
1、输入gcc命令:
sudo apt-get install libncurses5-dev
2、查看头文件
cd /usr/include
ls
3、查看库文件
cd /usr/lib
ls
(4)Linux 环境下C语言编译实现贪吃蛇游戏
代码:http://www.linuxidc.com/Linux/2011-08/41375.htm贪吃蛇代码
四、总结
本篇文章主要总结了如何用gcc生成静态库与动态库,并生成可执行文件,比较了动态库与静态库的大小;了解了as汇编编译器以及第三方库函数curses函数。
五、参考资料
贪吃蛇代码:http://www.linuxidc.com/Linux/2011-08/41375.htm
curses函数:https://blog.csdn.net/byxdaz/article/details/1780449