ubuntu下用gcc生成与使用静态库和动态库以及与其他编译器的对比和函数库的应用
1. 静态库与动态库
1.1 准备工作
1.1.1新建main1.c、sub1.c、sub2.c文件
main1.c和sub1.c文件参考我之前的博客里面2.1.1和2.1.2编写即可
接下来新建sub2.c
输入命令:
touch sub2.c
即可创建一个名为sub2.c的文件
输入命令:
vim sub2.c
即可打开文件编辑
按I开始在光标处插入
输入代码:
float x2y(int a,int b)
{
float c;
c=a+b;
return c;
}
按ESC回到命令模式
输入:wq
退出vim编辑器并保存
至此3个 .c 文件已经准备完成
1.2 用gcc生成与使用静态库
1.2.1 用gcc生成.a静态库文件
要生成 .a 静态库文件首先要生成 .o 文件
输入命令:
gcc -c main1.c sub1.c sub2.c
即可生成main1.o、 sub1.o 、sub2.o
输入命令:
ar -crv libmysub.a sub1.o sub2.o
即可将sub1.o和sub2.o两个文件生成一个静态库文件libmysub.a
1.2.2 静态库的链接
输入命令:
gcc -o operation main1.o libmysub.a
即可生成可执行文件operation
输入命令:
./operation
运行结果如下:
1.3 用gcc生成与使用动态库
1.3.1用gcc生成.so动态库态库文件
前面已经生成了三个 .o文件,在此基础上
输入命令:
gcc -shared -fPIC -o libmysub.so sub1.o sub2.o
将sub1.o和sub2.o生成一个动态库libmysub.so
1.3.2 动态库的链接
输入命令:
gcc main1.c libmysub.so -o operation1
因为动态库的链接后要运行时程序要从目录 /usr/lib下找所以我们还需要把动态库文件libmysub.so移动到该文件夹内否则运行时会报错
错误如下:
解决错误:
输入命令:
mv libmysub.so /usr/lib
即可将动态库文件libmysub.so移动到目录 /usr/lib下
(如果出现如下错误:
则是权限不够,只要进入管理员权限就好
输入命令:
sudo -s
再输入密码进入管理员权限之后执行上面移动命令即可
输入命令:
exit
即可退出管理员权限)
再次运行程序operation1
输入命令:
./operation1
结果如下:
1.4 静态库与动态库生成可执行文件对比
静态库:即静态链接库。以 .a为文件后缀名。在程序编译时会被链接到目标代码中,程序运行时将不再需要该静态库。
动态库:即动态链接库。以 .so 为文件后缀名。在程序编译时并不会被链接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
静态库代码装载的速度快,执行速度略比动态库快。但是动态库更加节省内存,可执行文件体积比静态库小很多。静态库是在编译时加载,动态库是在运行时加载。
对比如下:
operation为静态库生成可执行文件
operation1为动态库生成可执行文件
2. 编译器gcc与nasm编译的可执行文件大小的对比
2.1 准备文件hello.c和hello.asm
文件内容如下:
hello.c
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
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.2 分别用gcc和nadm编译文件
2.1.1 使用gcc编译
输入命令:
gcc hello.c
生成可执行文件a.out
2.2.2 使用nasm编译
(如果没有nasm编译器则需要先下载安装
输入命令:sudo apt install nasm
安装完成)
输入命令:
nasm -f elf64 hello.asm
生成hello.o文件
输入命令:
ld -s -o hello hello.o
生成可执行文件hello
分别运行用gcc编译的a.out和用nasm编译的hello
结果如下:
2.3 对比文件大小
输入命令:
size hello a.out
查看文件大小
显然nasm编译的可执行文件要比gcc编译的小很多
3. 借助第三方库函数完成代码设计
3.1 基于光标库(curses)的简单函数
3.1.1 在Ubuntu中安装curses库
输入命令:
sudo apt-get install libncurses5-dev
即可
头文件默认位于 /usr/include
库文件默认位于 /usr/lib
3.1.2 使用curses库和其主要函数
需要加入头文件
#include <curses.h>
主要函数:
int insch(chtype ch); //插入ch,右移
int insertln(void); //插入空白行,下移
int delch(void); //删除光标位置字符,左移
int deleteln(void) //删除当前行,上移
int beep(void); //发声
int flash(void); //闪屏
int clrtobot(void);//清除光标位置到屏幕结尾的内容
int clrtoeol(void);//清除光标位置到该行行尾的内容
3.1.3 使用curses库例子
这里展示一个链接和使用光标库的游戏–弹球游戏
代码如下:
/*
* 基于curses库弹球游戏,编译gcc xxx.c -lcurses
*/
#include <curses.h>
#include <sys/time.h>
#include <signal.h>
#define RIGHT COLS-1 /*球所能到达的当前屏幕最大水平范围*/
#define BOTTOM LINES-1 /*球所能到达的当前屏幕最大垂直范围*/
#define BOARD_LENGTH 10 /*挡板长度*/
#define LEFT 0 /*当前屏幕的最左边*/
#define TOP 0 /*当前屏幕的最上边*/
char BALL= 'O'; /*球的形状*/
char BLANK= ' '; /*覆盖球走过的轨迹*/
int left_board; /*挡板左侧坐标*/
int right_board; /*挡板右侧坐标*/
int is_lose=0;
int hdir; /*控制球水平运动的变量*/
int vdir; /*控制球垂直运动的变量*/
int pos_X; /*球的横坐标*/
int pos_Y; /*球的纵坐标*/
int delay=100;
void moveBall();
void init();
void control();
int main()
{
//初始化 curses
initscr();
crmode(); /*中断模式*/
noecho(); /*关闭回显*/
move(6,28);
attron(A_BOLD);
addstr("Welcome to the BallGame!");
move(8,20);
attroff(A_BOLD);
addstr("Help:");
move(9,23);
addstr("'N':Start a new game.");
move(10,23);
addstr("'Q':Quit game.");
move(11,23);
addstr("'KEY_LEFT' :Control baffle left shift.");
move(12,23);
addstr("'KEY_RIGHT':Control baffle right shift.");
move(13,23);
addstr("'KEY_UP' :Control of the ball speed.");
move(14,23);
addstr("'KEY_DOWN' :Control of the ball reducer.");
int flag=1;
char choice;
move(16,24);
addstr("Please choose your choice!(n/q):");
refresh();
choice=getch();
while(flag){
if(choice=='q'||choice=='Q'||choice=='n'||choice=='N')
flag=0;
else choice=getch();
}
if(choice=='n'||choice=='N'){ /*开始游戏*/
clear();
move(10,25);
addstr("BallGame will start! Are you read?");
refresh();
sleep(3);
control();
}
else if(choice=='q'||choice=='Q'){ /*退出游戏*/
clear();
move(10,25);
addstr("You quit the game successfully!");
refresh();
sleep(3);
endwin();
}
endwin(); /*结束 curses*/
return 0;
}
void init(){
int i,j;
clear();
if(start_color()==OK){ /*改变球和挡板的颜色*/
attron(A_BOLD); /*打开粗体*/
init_pair(1,COLOR_YELLOW,COLOR_BLACK);
attron(COLOR_PAIR(1));
}
//初始球
pos_X =22; /*球初始的横坐标*/
pos_Y = BOTTOM-1; /*球初始的纵坐标*/
//初始化球的运动方向,朝右上方运动
hdir=1;
vdir=-1;
//初始挡板
left_board=20;
right_board=left_board+BOARD_LENGTH;
for(i=left_board;i<=right_board;i++){ /*显示挡板*/
move(BOTTOM,i);
addch('-');
}
//初始刷新时间
signal(SIGALRM,moveBall);
set_ticker(delay);
keypad(stdscr,TRUE); /*打开 keypad 键盘响应*/
attroff(A_BLINK); /*关闭 A_BLINK 属性*/
is_lose=0;
move(pos_Y,pos_X);
addch(BALL);
move(LINES-1, COLS-1);
refresh();
usleep(100000); /*睡眠*/
move(LINES-1,COLS-1);
refresh();
}
void moveBall(){
if(is_lose) return;
signal(SIGALRM,moveBall);
move(pos_Y,pos_X);
addch(BLANK);
pos_X += hdir;
pos_Y += vdir;
//改变球的方向时
if(pos_X >= RIGHT) { /*当球横坐标大于右边边缘时,球反弹朝左运动*/
hdir = -1;
beep(); /*球撞墙时,发出声音*/
}
if(pos_X <= LEFT) { /*当球横坐标大于左边边缘时,球反弹朝右运动*/
hdir = 1;
beep(); /*球撞墙时,发出声音*/
}
if(pos_Y <= TOP) { /*当球纵坐标大于顶部边缘时,球反弹朝下运动*/
vdir = 1;
beep(); /*球撞墙时,发出声音*/
}
//当球在底部的时候进行额外的处理
if(pos_Y >= BOTTOM-1){
if(pos_X>=left_board&&pos_X<=right_board) /*球在挡板处*/
vdir=-1;
else{ /*球不在挡板处*/
is_lose=1;
move(pos_Y,pos_X);
addch(BALL);
move(LINES-1, COLS-1);
refresh();
usleep(delay*1000); /*睡眠*/
move(pos_Y,pos_X);
addch(BLANK);
pos_X += hdir;
pos_Y += vdir;
move(pos_Y,pos_X);
addch(BALL);
move(LINES-1, COLS-1);
refresh();
}
}
//不改变球的方向时
move(pos_Y,pos_X);
addch(BALL);
move(LINES-1, COLS-1);
refresh();
}
void control(){
init();
int cmd;
while (1)
{
if(!is_lose){
cmd=getch();
if(cmd=='q'||cmd=='Q'||cmd==27) break; //强制退出游戏
//挡板左移
if(cmd==KEY_LEFT){
if(left_board>0){
move(BOTTOM,right_board);
addch(' ');
right_board--;
left_board--;
move(BOTTOM,left_board);
addch('-');
move(BOTTOM,RIGHT);
refresh();
}
}
//挡板右移
else if(cmd==KEY_RIGHT){
if(right_board<RIGHT){
move(BOTTOM,left_board);
addch(' ');
right_board++;
left_board++;
move(BOTTOM,right_board);
addch('-');
move(BOTTOM,RIGHT);
refresh();
}
}
//给球加速
else if(cmd==KEY_UP){
delay/=2;
set_ticker(delay);
}
//给球减速
else if(cmd==KEY_DOWN){
delay*=2;
set_ticker(delay);
}
}
else{
//输掉球后的处理
int flag=1;
char choice;
move(8,15);
addstr("Game Over!try again?(y/n):");
refresh();
choice=getch();
while(flag){
if(choice=='y'||choice=='Y'||choice=='n'||choice=='N')
flag=0;
else choice=getch();
}
if(choice=='y'||choice=='Y'){ /*游戏重新开始*/
delay=100; /*恢复球的初始速度*/
init();
continue;
}
else if(choice=='n'||choice=='N'){ /*结束游戏*/
break;
}
}
}
}
//设置定时器
int set_ticker(int n_msecs){
struct itimerval new_timeset;
long n_sec,n_usecs;
n_sec=n_msecs/1000;
n_usecs=(n_msecs%1000)*1000L;
new_timeset.it_interval.tv_sec=n_sec;
new_timeset.it_interval.tv_usec=n_usecs;
new_timeset.it_value.tv_sec=n_sec;
new_timeset.it_value.tv_usec=n_usecs;
return setitimer(ITIMER_REAL,&new_timeset,NULL);
}
(代码来源于博客curses库实现的弹球游戏)
编译执行结果如下:
3.2 体验远古时代的 BBS (一个用键盘光标控制的终端程序)
在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
结果如下:
4. 总结
通过以上,了解编译器背后的故事知道到静态库和动态库在程序运行时的区别以及优缺点,不同编译器的区别比如nasm和gcc以及了解如何借助第三方库函数完成代码设计。