一、用gcc生成.a静态库和.so动态库
1.创建3个程序hello.h、hello.c和main.c作为本次实验例子
先创建一个test1作业目录,保存文件
mkdir test1
cd test1
然后用nano文本编辑器编辑生成所需要的3个文件
hello.h
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif
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文件
输入以下命令得到hello.o文件
gcc -c hello.c
运行ls命令查看发现已生成了hello.o文件
3.由.o 文件创建.a静态库
在终端中输入如下指令
ar -crv libmyhello.a hello.o
4.在程序中使用静态库
输入如下指令
gcc main.c libmyhello.a -o hello
./hello
得到如下结果
删除静态库文件试试公用函数hello是否真的连接到目标文件hello中
rm libmyhello.a
./hello
结果如下
程序照常运行,静态库中的公用函数已经连接到目标文件中了
5.由.o文件创建动态库文件
用gcc来创建动态库,输入以下命令得到动态库文件libmyhello.so
gcc -shared -fPIC -o libmyhello.so hello.o
6.在程序中使用动态库
运行gcc命令生成目标文件,查看结果
gcc main.c libmyhello.so -o hello
./hello
这时出现错误
这里我们输入以下命令将文件 libmyhello.so 复制到目录/usr/lib 中即可运行
sudo cp libmyhello.so /usr/lib
二、动静态库联系并比较大小
编写一个x2x函数,一个x2y函数,main函数代码将调用x2x和x2y
main1.h
#ifndef MAIN1_H
#define MAIN1_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif
x2x.c
#include<stdio.h>
float x2x(int a,int b)
{
float c;
c=a+b;
return c;
}
x2y.c
#include<stdio.h>
float x2y(int a,int b)
{
float c;
c=a/b;
return c;
}
main1.c
#include"main1.h"
#include"stdio.h"
int main()
{
int a,b;
float c,d;
scanf("%d%d",&a,&b);
c=x2x(a,b);
d=x2y(a,b);
printf("c=%f",c);
printf("d=%f",d);
return 0;
}
使用gcc生成.o文件
将x2x,x2y使用ar工具生成.a静态库文件
执行查看大小
大小为8520B
使用命令生成动态库
gcc -shared -fPIC -o libmain1.so x2x.o x2y.o
使用.so库文件,创建可执行程序
gcc -o main2 main1.c libmain1.so
执行查看大小
大小为8520B
三、Linux GCC常用命令
1.完成该实验需要准备以下一个test.c文件
#include <stdio.h>
int main(void)
{ printf("Hello World!\n");
return 0;
}
2.一步到位的编译
gcc test.c -o test
3.分步编译
上一个步骤中一步到位的编译指令其实分为以下四步进行:
预处理
编译为汇编代码
汇编
链接生成可执行文件
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.o
gcc test.o -o test
运行结果如下
四、比较hello.asm与C代码生成的可执行文件
1.在Ubuntu下安装nasm
输入以下指令下载安装nasm
sudo apt install nasm
2.新建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 ; 调用内核功能
3.生成并运行可执行文件hello
输入以下指令生成hello.o文件
nasm -f elf64 hello.asm
生成可执行文件hello
ld -s -o hello hello.o
运行hello文件并记录文件大小
c语言输出hello world生成的程序大小
可以明显看出nasm方式编译得到的可执行文件要比用gcc编译得到的文件要小很多
五、Linux 系统中终端程序最常用的光标库(curses)
int addch(const chtype char_to_add); //当前位置添加字符
int delch(void) //删除光标左边的字符
chtype inch(void); //返回光标位置字符
int move(int new_y, int new_x); //移动stdcsr的光标位置
void getyx(WINDOW *win,int y,int x); //得到目前游标的位置. (请注意! 是 y,x 而不是&y,&x )
int mvwin(WINDOW *win, int new_y, int new_x); //移动窗口
六、体验远古时代的 BBS
进入WIN10系统控制面板,选择“程序和功能”
2.点击”启用或关闭 Windows 功能“
3.勾选”Telnet Client“与”适用于 Linux 的 Windows 子系统“
4.打开 cmd 控制台,输入以下指令
telnet bbs.newsmth.net
5.开始体验
七、在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库
输入以下指令
sudo apt-get install libncurses5-dev
curses头文件所在目录为/usr/include
八、Ubuntu 环境下C语言编译实现贪吃蛇游戏
1.创建 GreedySnake.c 文件,复制以下代码并粘贴到文件中
//mysnake1.0.c
//编译命令:cc mysnake1.0.c -lcurses -o mysnake1.0
//用方向键控制蛇的方向
#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-1==head->next->cx && 1==dir.cx)
|| (0==head->next->cx && -1==dir.cx)
|| (LINES-1==head->next->cy && 1==dir.cy)
|| (2==head->next->cy && -1==dir.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->cx==food.cx && head->next->cy==food.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.使用以下指令生成可执行文件
gcc tcs.c -lcurses -o tcs
3.运行文件