GCC编译器的应用和它的背景

前言

这篇文章是描述GCC编译器的应用,静态库和动态库文件的应用和使用,以及GCC编译器工具集中各种软件的用途,还有关于如何借助第三方库函数完成代码设计。

一、可执行程序的组装,静态库.a和动态库.so的生成与使用

1.准备工作

先创建一个目录,然后在此目录下进行编程
在这里插入图片描述
创建四个文件并用gedit 对其进行编译

 touch main1.c
 touch sub1.c
 touch sub2.c
 touch sub1.h
 gedit main1.c
 gedit sub1.c
 gedit sub1.h
 gedit sub2.c

代码:
main1.c

#include<stdio.h>
#include"sub1.h"
void main()
{
 int a=8,b=6;
 float c,w;
 c=x2x(a,b);
 w=x2y(a,b);
 printf("%f\n",c);
 printf("%f\n",w);
}

sub1.c

float x2x(int a,int b)
{
  float s;
  s=a+b;
  return s;
}

sub1.h

#ifndef SUB1_H
#define SUB1_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif

sub2.c

float x2y(int a,int b)
{
  float w;
  w=a-b;
  return w;
}

2.GCC创建静态库

2.1.生成目标文件

在这里插入图片描述

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

2.3.使用静态库文件,创建可执行程序

在这里插入图片描述

3.生成动态库文件和其使用

3.1.生成目标文件

在这里插入图片描述

3.2.共享动态库文件

在这里插入图片描述

3.3.使用动态库文件,创建可执行程序

在这里插入图片描述
出现错误,查看链接情况

在这里插入图片描述
发现没有对应的.so文件
这是linux自身系统设定的相应的设置的原因,就是只在/lib and /usr/lib下搜索对应动态库文件,故需要将对应.so文件拷贝到对应路径
结果去下:
在这里插入图片描述

3.4.对比二者生成的可执行文件大小

在这里插入图片描述

二、GCC编译工具集中各种软件的用途,ELF文件格式,汇编语言格式

1.GCC常用命令的应用

1.1.准备工作

创建并编辑文件test.c
在这里插入图片描述
代码:

#include<stdio.h>
int main()
{
  printf("Hello World");
  return 0;
}

1.2.预处理

在这里插入图片描述
这里是将test.c文件预处理为test.i文件

1.3.编译

在这里插入图片描述
将.i文件编译生成汇编程序.s,
汇编结果:

main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$.LC0, %edi
	movl	$0, %eax
	call	printf
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
	

1.4.链接

动态链接
在这里插入图片描述
静态链接:在这里插入图片描述

2.ELF文件

2.1.ELF 文件包含下面几个段

(1) .text:已编译程序的指令代码段

(2) .rodata:ro 代表 read only,即只读数据(譬如常数 const)

(3) .data:已初始化的 C 程序全局变量和静态局部变量

(4) .bss:未初始化的 C 程序全局变量和静态局部变量

(5) .debug:调试符号表,调试器用此段的信息帮助调试
查看各个section的信息:
在这里插入图片描述

2.2.反汇编

由于ELF文件无法被当作普通文本打开,如果希望查看一个ELF文件包含的指令和函数,需要用到反汇编
在这里插入图片描述
使用objdump -S将其反汇编并且将C语言源代码混合显示出来;在这里插入图片描述

3.nasm编译器,汇编代码

3.1.安装nasm

在网上下载一个安装包,把它复制到虚拟机的文件夹中,然后在虚拟机里解压,在终端输入以下代码:
cd 复制的那个文件夹
tar zxvf (nasm-2.14rc16.tar.gz )括号内为你所下载的nasm的版本。
查看是否安装成功
nasm -version
在这里插入图片描述

3.2.进行一个汇编实例

编译汇编hello.asm文件;生成目标文件
在这里插入图片描述
用ld链接器把hello.o生成可执行程序hello。./hello查看运行结果,如下图。
在这里插入图片描述
查看文件大小
在这里插入图片描述

三.如何借助第三方库函数完成代码设计

1.courses的主要函数功能和几个基本函数

1.1.以initscr()开始,以endwin();结束

WINDOW *initscr(void);  //初始化
  int endwin(void);    // 退出curses, 返回OK/ERR

1.2.输出到屏幕

int addch(const chtype ch); //当前位置add ch
int addchstr(chtype *const str);//当前位置add str
int printw(char *format,…); //类printf,格式化输出
int refresh(void);//刷新物理屏,更改逻辑屏,必须刷新才能在物理屏上面显示
int box(WINDOW *ptr, chtype v_ch, chtype h_ch);    //围绕ptr指定窗口画方框,垂直/水平方向字符v_ch/h_ch
int insch(chtype ch);      //插入ch,右移
int insertln(void);     //插入空白行,下移
int delch(void);     //删除光标位置字符,左移
int deleteln(void)   //删除当前行,上移
int beep(void);     //发声
int flash(void);           //闪屏

1.3.从屏幕读取

chtype inch(void);   //读取光标位置字符
  int instr(char *string);    //读取字符串到str,到NUL,不总是被支持
  int innstr(char *str, int n_of_str);  //读取n个字符到str,或NUL,不总是被支持

(4).清除屏幕
  int erase(void);    //清屏,以空白字符填充
  int clear(void);    //清屏
  int clrtobot(void);    //清至屏幕结尾
  int clr

2.感受远古时代BBS

在window10中打开“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS 。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.安装corses

输入下图中的第一行的程序:
在这里插入图片描述
头文件和库文件安装到以下目录:
在这里插入图片描述

4.Linux下C语言编译实现贪吃蛇游戏

事先创建一个snake目录
创建一个可执行文件snake1
在这里插入图片描述
执行snake1
在这里插入图片描述
结果如图:

在这里插入图片描述

四.总结

学习了动态库和静态库文件的建立和使用,用静态和动态库链接生成可执行文件,ELF文件的格式还有各种GCC命令的使用,汇编语言的格式,nasm的安装和使用,体验了即将绝迹的远古时代的BBS。安装curses库,最后实现了一个贪吃蛇小游戏。
贪吃蛇的代码:

#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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值