编译器后的故事(linux)

**一、**用gcc生成动态库和静态库,以及.a .so文件的生成
1)先创建一个目录,并进入目录。
在这里插入图片描述
2)用vim等文本编辑器生成所需要的3个文件。
1.hello.h
在这里插入图片描述
2.hello.c
在这里插入图片描述
3.main.c
在这里插入图片描述

3)将hello.c编译成.o文件,并用ls查看是否生成了.o文件
gcc -c hello.c
在这里插入图片描述

4)由.o文件创建静态库,并用ls查看结果
ar -crv libmyhello.a hello.o
在这里插入图片描述

5)在程序中使用静态库
gcc main.c libmyhello.a -o hello
在这里插入图片描述
生成了一个可执行的文件hello。
6)运行可执行文件
./hello
在这里插入图片描述
7)我们删除静态库文件试试公用函数hello是否真的连接到目标文件中
在这里插入图片描述
运行成功说明已经连接到目标文件中了。
8)由.o文件创建动态库文件,并用ls查看是否生成。
gcc -shared -fPIC -o libmyhello.so hello.o
在这里插入图片描述
9)在程序中使用动态库
gcc -o hello main.c -L -lmyhello
在这里插入图片描述
发现报错了,No such file or directory(找不到文件) 我们将文件复制到目录/user/lib中尝试一哈。
在这里插入图片描述
发现运行成功
二、 生成静态文件.a 与动态文件.so,并比较文件大小
1)建立三个文件mian.c sub1.c sub2.c

在这里插入图片描述
2)分别编译三个文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3)把他们编译成.o目标文件,然后生成一个静态库文件,最后用gcc将目标文件与静态库文件连接,查看输出结果。
在这里插入图片描述
4)记录文件大小
在这里插入图片描述
5)生成动态文件,记录文件大小

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
**三、**gcc不是一个人在战斗,gcc及其编译工具中的软件(gcc 背后的故事)
1)gcc 编译过程
四个过程
在这里插入图片描述

预处理(pre-processing)E
编译器将C源代码中的包含的头文件如stdio.h编译进来,替换宏。
编译(Compiling)S
gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。
汇编(Assembling) c
把编译阶段生成的”.s”文件转成二进制目标代码。
链接 (Linking)
链接到库中,生成可执行文件。
2)二进制工具
例:addr2line、ar、objdump、as、ld、ldd、readelf、size等
1.addr2line:用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置。
2.as:主要用于汇编。
3.ld:主要用于链接。
4.ar:主要用于创建静态库。在此介绍动态库与静态库的概念:
如果要将多个.o目标文件生成一个库文件,则存在两种类型的库,一种是静态库,另一种是动态库。
在 windows中静态库是以.lib为后缀的文件,共享库是以.dll为后缀的文件。在 linux 中静态库是以.a为后缀的文件,共享库是以.so为后缀的文件。
静态库和动态库的不同点在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。共享库的代码是在可执行程序运行时才载入内存的,在 编译过程中仅简单的引用,因此代码体积较小。在 Linux系统中,可以用ldd命令查看一 个可执行程序依赖的共享库。
如果一个系统中存在多个需 要同时运行的程序且这些程序之间存在共享库,那么采用动态库的形式将更节省内存。
5.ldd:可以用于查看一个可执行程序依赖的共享库。
6.objdump:主要的作用是反汇编。
7.readelf:显示有关 ELF 文件的信息
8.size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小
等。
3)示例编译过程
1.建立一个简单的hello.c文件,并编译
在这里插入图片描述
在这里插入图片描述
2.预处理
在这里插入图片描述
3.编译
在这里插入图片描述
在这里插入图片描述

4.汇编
在这里插入图片描述
5.链接
在这里插入图片描述

在这里插入图片描述
4)分析ELF文件
1.ELF文件的段

在这里插入图片描述
在这里插入图片描述

2.反汇编ELF
在这里插入图片描述
5)安装nasm,并比较hello.asm生成的可执行文件和文件hello worldC代码编译生成的程序大小。
1.安装nasm
在这里插入图片描述

2.生成hello.nasm的可执行文件
在这里插入图片描述
3.进行大小比较可发现hello.nasm生成的可执行文件大小要远远小于hello wordC语言编译生成可执行文件的大小。
四、 每一个程序背后都站着一堆优秀的代码库,那么如何借助库函数完成代码设计
1)curses介绍
1.全局变量
  WINDDW* curscr:当前屏幕
  WINDOW* stdscr:标准屏幕
2.函数说明
1、字符显示
  WINDOW* initscr()
 SCREEN* newterm(char *type, FILE *outfd, FILE *infd)
 初始化函数,对用户访问的每个终端都应该调用newterm,type是终端的名称,包括在$TERM中(如ansi, xterm, vt100等等) 。
2、方框和直线
  int border(ls, rs, ts, bs, tl, tr, bl, br)
  int wborder(win, ls, rs, ts, bs, tl, tr, bl, br)
  int box(win, vert, hor)
  这些函数在窗口的边界(或者win的边界)画上方框。在下面的表格中,读者将可以看到字符,以及它们的默认值。当用零去调用box(.)时将会用到这些默认值。在下面的图中读者可以看到方框中字符的位置
3、输出选项
  int  idlok(win, bf)
  void fdcok(win, bf)
  这两个函数为窗口使能或者关闭终端的insert/delete特征(idlok(.)针对一行,而fdcok(.)则针对字符)。
4、如何在您的程式使用 curses:
在您的 C 程式的档头将 <cureses.h>include 进来.当您引进 curses.h 这个函式库後, 系统会自动将 <stdio.h>和 <unctl.h>一并 include 进来.
另外, 在 System V 版本中, <terminfo.h>这个函式库也将一并include进来.
#include <curses.h>
main()
{
: :
: :
}

5、如何编译(compile):
当您编辑好您的程式, 在 UNIX 提示符号下键入:
gcc [file.c] -lcurses

gcc [file.c] -lcurses -ltermlib(*注二)

6、如何开始我的第一个 curses 程式:
在开始使用 curses 的一切命令之前, 您必须先利用 initscr()这个函式来开启 curses 模式.
相对的, 在结束 curses 模式前 ( 通常在您结束程式前 ) 也必须以endwin()来关闭 curses 模式.
#include < curses.h >

main()
{
initscr();
: :
: :
: :
endwin();
}

这是一般 curses 程式标准的模式.
此外, 您可以就您程式所须, 而做不同的设定. 当然, 您可以不做设定,而只是呼叫 initscr(). 您可以自己写一个函式来存放所有您所须要的设定. 平常使用时, 只要呼叫这个函式即可启动 curses 并完成一切设定. 下面的例子, 即是笔者将平常较常用的一些设定放在一个叫 initial()的函式内.
void initial()
{
initscr();
cbreak();
nonl();
noecho();
intrflush(stdscr,FALSE);
keypad(stdscr,TRUE);
refresh();
}
2)以游客身份体验即将绝迹的远古时代的BBS
操作如下:
1.在这里插入图片描述
2.在这里插入图片描述
在这里插入图片描述
通过用在这里插入图片描述
+键盘R键即可打开cmd指令窗口。
在这里插入图片描述
在指令窗口输入
在这里插入图片描述
即可打开页面。
在这里插入图片描述

在这里插入图片描述
3)安装curses库
在这里插入图片描述
在这里插入图片描述

ubuntu下,该库的头文件在 /usr/include ,库在/usr/lib/i386-linux-gnu/libc.so。
4)C语言实现贪吃蛇游戏
1.建立一个mysnake1.0.c文件并编译代码如下
//mysnake1.0.c
//用方向键控制蛇的方向
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <signal.h>
#include <sys/time.h>
#define NUM 60

struct direct //用来表示方向的
{
int cx;
int cy;
};
typedef struct node //链表的结点
{
int cx;
int cy;
struct node *back;
struct node *next;
}node;

void initGame(); //初始化游戏
int setTicker(int); //设置计时器
void show(); //显示整个画面
void showInformation(); //显示游戏信息(前两行)
void showSnake(); //显示蛇的身体
void getOrder(); //从键盘中获取命令
void over(int i); //完成游戏结束后的提示信息

void creatLink(); //(带头尾结点)双向链表以及它的操作
void insertNode(int x, int y);
void deleteNode();
void deleteLink();

int ch; //输入的命令
int hour, minute, second; //时分秒
int length, tTime, level; //(蛇的)长度,计时器,(游戏)等级
struct direct dir, food; //蛇的前进方向,食物的位置
node *head, *tail; //链表的头尾结点

int main()
{
initscr();
initGame();
signal(SIGALRM, show);
getOrder();
endwin();
return 0;
}

void initGame()
{
cbreak(); //把终端的CBREAK模式打开
noecho(); //关闭回显
curs_set(0); //把光标置为不可见
keypad(stdscr, true); //使用用户终端的键盘上的小键盘
srand(time(0)); //设置随机数种子
//初始化各项数据
hour = minute = second = tTime = 0;
length = 1;
dir.cx = 1;
dir.cy = 0;
ch = ‘A’;
food.cx = rand() % COLS;
food.cy = rand() % (LINES-2) + 2;
creatLink();
setTicker(20);
}

//设置计时器(这个函数是书本上的例子,有改动)
int setTicker(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;     
n_msecs = 1;
n_sec = n_msecs / 1000 ;
n_usecs = ( n_msecs % 1000 ) * 1000L ;
new_timeset.it_value.tv_sec     = n_sec  ;     
new_timeset.it_value.tv_usec    = n_usecs ;    
return setitimer(ITIMER_REAL, &new_timeset, NULL);

}

void showInformation()
{
tTime++;
if(tTime >= 1000000) //
tTime = 0;
if(1 != tTime % 50)
return;
move(0, 3);
//显示时间
printw(“time: %d:%d:%d %c”, hour, minute, second);
second++;
if(second > NUM)
{
second = 0;
minute++;
}
if(minute > NUM)
{
minute = 0;
hour++;
}
//显示长度,等级
move(1, 0);
int i;
for(i=0;i<COLS;i++)
addstr("-");
move(0, COLS/2-5);
printw(“length: %d”, length);
move(0, COLS-10);
level = length / 3 + 1;
printw(“level: %d”, level);
}

//蛇的表示是用一个带头尾结点的双向链表来表示的,
//蛇的每一次前进,都是在链表的头部增加一个节点,在尾部删除一个节点
//如果蛇吃了一个食物,那就不用删除节点了
void showSnake()
{
if(1 != tTime % (30-level))
return;
//判断蛇的长度有没有改变
bool lenChange = false;
//显示食物
move(food.cy, food.cx);
printw("@");
//如果蛇碰到墙,则游戏结束
if((COLS-1head->next->cx && 1dir.cx)
|| (0head->next->cx && -1dir.cx)
|| (LINES-1head->next->cy && 1dir.cy)
|| (2head->next->cy && -1dir.cy))
{
over(1);
return;
}
//如果蛇头砬到自己的身体,则游戏结束
if(’’ == mvinch(head->next->cy+dir.cy, head->next->cx+dir.cx) )
{
over(2);
return;
}
insertNode(head->next->cx+dir.cx, head->next->cy+dir.cy);
//蛇吃了一个“食物”
if(head->next->cxfood.cx && head->next->cyfood.cy)
{
lenChange = true;
length++;
//恭喜你,通关了
if(length >= 50)
{
over(3);
return;
}
//重新设置食物的位置
food.cx = rand() % COLS;
food.cy = rand() % (LINES-2) + 2;
}
if(!lenChange)
{
move(tail->back->cy, tail->back->cx);
printw(" “);
deleteNode();
}
move(head->next->cy, head->next->cx);
printw(”
");
}

void show()
{
signal(SIGALRM, show); //设置中断信号
showInformation();
showSnake();
refresh(); //刷新真实屏幕
}

void getOrder()
{
//建立一个死循环,来读取来自键盘的命令
while(1)
{
ch = getch();
if(KEY_LEFT == ch)
{
dir.cx = -1;
dir.cy = 0;
}
else if(KEY_UP == ch)
{
dir.cx = 0;
dir.cy = -1;
}
else if(KEY_RIGHT == ch)
{
dir.cx = 1;
dir.cy = 0;
}
else if(KEY_DOWN == ch)
{
dir.cx = 0;
dir.cy = 1;
}
setTicker(20);
}
}

void over(int i)
{
//显示结束原因
move(0, 0);
int j;
for(j=0;j<COLS;j++)
addstr(" ");
move(0, 2);
if(1 == i)
addstr(“Crash the wall. Game over”);
else if(2 == i)
addstr(“Crash itself. Game over”);
else if(3 == i)
addstr(“Mission Complete”);
setTicker(0); //关闭计时器
deleteLink(); //释放链表的空间
}

//创建一个双向链表
void creatLink()
{
node *temp = (node *)malloc( sizeof(node) );
head = (node *)malloc( sizeof(node) );
tail = (node *)malloc( sizeof(node) );
temp->cx = 5;
temp->cy = 10;
head->back = tail->next = NULL;
head->next = temp;
temp->next = tail;
tail->back = temp;
temp->back = head;
}

//在链表的头部(非头结点)插入一个结点
void insertNode(int x, int y)
{
node *temp = (node *)malloc( sizeof(node) );
temp->cx = x;
temp->cy = y;
temp->next = head->next;
head->next = temp;
temp->back = head;
temp->next->back = temp;
}

//删除链表的(非尾结点的)最后一个结点
void deleteNode()
{
node *temp = tail->back;
node *bTemp = temp->back;
bTemp->next = tail;
tail->back = bTemp;
temp->next = temp->back = NULL;
free(temp);
temp = NULL;
}

//删除整个链表
void deleteLink()
{
while(head->next != tail)
deleteNode();
head->next = tail->back = NULL;
free(head);
free(tail);
}
(引用他人代码)
2.输入编译命令:cc mysnake1.0.c -lcurses -o mysnake1.0
3.即可运行游戏:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值