**一、**用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.即可运行游戏: