Linux系统编译器的了解
可执行程序组装流程
1.先通过代码编写一个源程序。
2.将源程序进行预编译。
3.将预编译生成的文件进行编译。
4.汇编,将代码变成机器可以执行的指令。
5.链接。
Linux下静态库.a与.so库文件的的生成与使用
1.创建作业目录并打开
jzddyy@ubuntu:~$ mkdir test2
jzddyy@ubuntu:~$ cd test2
2.用nano编辑器生成四个文件A1.c,A2.c,A.h,test.c。
A1.c
#include <stdio.h>
void print1(int arg)
{
printf("A1 print arg:%d\n",arg);
}
A2.c
#include<stdio.h>
void print2(char *arg)
{
printf("A2 printf arg:%s\n",arg);
}
A.h
#ifndef A_H
#define A_H
void print1(int);
void print2(char *);
#endif
test.c
#include <stdio.h>
#include "A.h"
int main()
{
print1(1);
print2("test");
exit(0);
}
生成四个文件如下
3.静态库.a文件的生成与使用
3.1生成.o目标文件
jzddyy@ubuntu:~/test2$ gcc -c A1.c A2.c
3.2生成静态.a文件
jzddyy@ubuntu:~/test2$ ar crv libafile.a A1.o A2.o
a - A1.o
a - A2.o
3.3创建可执行程序
jzddyy@ubuntu:~/test2$ gcc -o test test.c libafile.a
jzddyy@ubuntu:~/test2$ ./test
A1 print arg:1
A2 printf arg:test
4.共享库.so文件的生成与使用
生成目标文件、生成共享库.so文件、使用.so文件创建可执行程序
gcc -c -fpic A1.c A2.c
jzddyy@ubuntu:~/test2$ gcc -shared *.o -o libsofile.so
jzddyy@ubuntu:~/test2$ gcc -o test test.c libsofile.so
出现错误
zddyy@ubuntu:~/test2$ ./test
./test: error while loading shared libraries: libsofile.so: cannot open shared object file: No such file or directory
将.so拷贝到对应路径并运行
jzddyy@ubuntu:~/test2$ sudo cp libsofile.so/usr/lib
jzddyy@ubuntu:~/test2$ ./test
用gcc生成.a静态库和.so动态库
1.编辑生成例子程序hello.h、hello.c、main.c
创建作业目录
jzddyy@ubuntu:~/test2$ mkdir test1
jzddyy@ubuntu:~/test2$ cd test1
hello.h
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif//HELLO_H
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文件
jzddyy@ubuntu:~/test2/test1$ gcc -c hello.c
jzddyy@ubuntu:~/test2/test1$ ls
hello.c hello.h hello.o main.c
3.由.o文件创建静态库
jzddyy@ubuntu:~/test2/test1$ ar -crv libmyhello.a hello.o
a - hello.o
jzddyy@ubuntu:~/test2/test1$ ls
hello.c hello.h hello.o libmyhello.a main.c
jzddyy@ubuntu:~/test2/test1$
4.在程序中使用静态库
jzddyy@ubuntu:~/test2/test1$ gcc -o hello main.c -L. -lmyhello
5.由.o文件创建动态库文件
jzddyy@ubuntu:~/test2/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
jzddyy@ubuntu:~/test2/test1$ ls
hello hello.c hello.h hello.o libmyhello.a libmyhello.so main.c
6.在程序中使用动态库
jzddyy@ubuntu:~/test2/test1$ gcc main.c libmyhello.so -o hello
jzddyy@ubuntu:~/test2/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
出现错误,执行如下操作
jzddyy@ubuntu:~/test2/test1$ mv libmyhello.so/usr/lib
编写一个x2y函数并实现上述操作
1.x2y函数如下
#include<stdio.h>
float x2y(int a,int b)
{float c;
c=a*b;
return c;
}
2.用gcc编译为3个目标文件
jzddyy@ubuntu:~$ gcc -c main1.c
jzddyy@ubuntu:~$ gcc -c sub1.c
jzddyy@ubuntu:~$ gcc -c sub2.c
jzddyy@ubuntu:~$ ls
1.sh examples.desktop main1.c.save Pictures sub1.o test2
Desktop hello main1.o Public sub2.c Videos
Documents main1 makefile snap sub2.o
Downloads main1.c Music sub1.c Templates
3.将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件
jzddyy@ubuntu:~$ ar -crv myword.a sub1.o sub2.o
a - sub1.o
a - sub2.o
jzddyy@ubuntu:~$ ls
1.sh examples.desktop main1.c.save myword.a sub1.c Templates
Desktop hello main1.o Pictures sub1.o test2
Documents main1 makefile Public sub2.c Videos
Downloads main1.c Music snap sub2.o
4.链接生成可执行文件并记录大小
jzddyy@ubuntu:~$ gcc main1.c myword.a -o myword
jzddyy@ubuntu:~$ ./myword
请输入两个数
4
5
两个数相除得0.8000
两个数相乘得20.000000
jzddyy@ubuntu:~$ size myword
text data bss dec hex filename
2165 624 8 2797 aed myword
5.将x2x、x2y目标文件用 ar工具生成1个 .a 动态库文件
jzddyy@ubuntu:~$ gcc -shared -fPIC -o myword.so sub1.o sub2.o
jzddyy@ubuntu:~$ gcc -o myword main1.c -L.-myword
gcc中EFF文件格式、汇编语言格式
1.gcc常用命令
示例程序
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
(1)编译指令(预处理、编译、汇编、链接)
gcc test.c -o test
预处理
gcc -E test.c -o test.i
编译
jzddyy@ubuntu:~$ gcc -S test.i -o test.s
汇编
jzddyy@ubuntu:~$ gcc -c test.s -o test.o
链接
jzddyy@ubuntu:~$ gcc test.o -o test
2.多个程序文件的编译
gcctest1.ctest2.c-o test
以上代码等效于以下代码
gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test
3.检错
gcc -pedantic illcode.c -o illcode
gcc -Wall illcode.c -o illcode
gcc在警告的地方停止编译
gcc -Werror test.c -o test
gcc编译器
1.编写一个简单的输出hello文件
#include <stdio.h> //此程序很简单,仅仅打印一个 Hello World 的字符串。
int main(void)
{ printf("Hello World! \n"); return 0; }
2.用gcc预处理生成.i文件
gcc -E hello.c -o hello.i
3.使用 gcc 进行编译
gcc -S hello.i -o hello.s
4.使用gcc进行汇编
gcc -c hello.s -o hello.o
5.链接并查看大小
gcc hello.c -o hello
size hello
nasm安装与使用
第三方库函数
1.基本函数功能名称介绍
echochar(ch):显示某个字元
addch(ch):显示某个字元
addstr(str):显示一串字串
move(y,x):将游标移动至x,y的位置
getyx(win,y,x):得到目前游标的位置
2.在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
贪吃蛇游戏测试
//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);
}
运行后的程序如下图所示(如果运行不成功,可添加#include<time.h>)
总结
这次的实验比较有趣,让我们更深入的了解了Linux的各种命令,对比了解了静态库和动态库之间的联系,区别,也让我们体验了利用代码实现简单游戏的乐趣。