GCC与as汇编编译器以及第三方库函数的代码设计

一、GCC的常用命令和GCC编译器背后的故事

1 简单编译

示例:

//test.c
#include <stdio.h> 
int main(void) 
{
printf("Hello World!\n"); 
return 0; 
}

该程序的一步到位编译命令是:gcc test.c -o test
在这里插入图片描述
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译 (Compilation)、汇编 (Assembly)和连接(Linking)。

1.1 预处理

命令:gcc -E test.c -o test.igcc -E test.c
可以输出 test.i 文件中存放着 test.c 经预处理之后的代码。打开 test.i 文件,看一看,就明白了。后 面那条指令,是直接在命令行窗口中输出预处理后的代码.
gcc 的-E 选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将 stdio.h 文件中的内容插入到 test.c 中了。
在这里插入图片描述
在这里插入图片描述

1.2 编译为汇编代码(Compilation)

预处理之后,可直接对生成的 test.i 文件编译,生成汇编代码:
命令:gcc -S test.i -o test.s
-S 选项,表示在程序编译期间,在生成汇编代码后,停止,
-o 输出汇编代码文件。
在这里插入图片描述

1.3 汇编(Assembly)

对于上一小节中生成的汇编代码文件 test.s,gas 汇编器负责将其编译为目标文件,如下:
命令:gcc -c test.s -o test.o
在这里插入图片描述

1.4 连接(Linking)

gcc 连接器是 gas 提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生 成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的 test.o,将其与C标准输入输出库进行连接,最终生成程序 test
命令:gcc test.o -o test
命令:./test
在这里插入图片描述

2 多个程序文件的编译

通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用 GCC 能够很好地管理 这些编译单元。假设有一个由 test1.c 和 test2.c 两个源文件组成的程序,为了对它们进行编译,并 最终生成可执行程序 test可以使用这条命令:

gcc test1.c test2.c -o test

如果同时处理的文件不止一个,GCC 仍然会按照预处理、编译和链接的过程依次进行。如果深究起 来,上面这条命令大致相当于依次执行如下三条命令:

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 -pedantic

编译选项并不能保证被编译程序与 ANSI/ISO C 标准的完全兼容,它仅仅只能用来帮助 Linux 程序员离这个目标越来越近。或者换句话说,-pedantic 选项能够帮助程序员发现一些不符合 ANSI/ISO C 标准的代码,但不是全部,事实上只有 ANSI/ISO C 语言标准中要求进行编译器诊断的 那些情况,才有可能被 GCC 发现并提出警告。
除了-pedantic 之外,GCC 还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W 开头,其中最有价值的当数-Wall 了,使用它能够使 GCC 产生尽可能多的警告信息。

gcc -Wall illcode.c -o illcode

GCC 给出的警告信息虽然从严格意义上说不能算作错误,但却很可能成为错误的栖身之所。一个优 秀的 Linux 程序员应该尽量避免产生警告信息,使自己的代码始终保持标准、健壮的特性。所以将 警告信息当成编码错误来对待,是一种值得赞扬的行为!所以,在编译程序时带上-Werror 选项,那 么 GCC 会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改,如下:

 gcc -Werror test.c -o test 

4 库文件连接

开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助许多函数库的支 持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(so、或 lib、dll)的集合。虽然 Linux 下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,Windows 所使用的库文件主要放在 Visual Stido 的目录下的 include 和 lib,以及系统文件夹下。但也有的时候,我们要用的库不再这些目录下,所以 GCC 在编译时必须用自己 的办法来查找所需要的头文件和库文件。
例如我们的程序 test.c 是在 linux 上使用 c 连接 mysql,这个时候我们需要去 mysql 官网下载 MySQL Connectors 的 C 库,下载下来解压之后,有一个 include 文件夹,里面包含 mysql connectors 的头 文件,还有一个 lib 文件夹,里面包含二进制 so 文件 libmysqlclient.so
其中 inclulde 文件夹的路径是/usr/dev/mysql/include,lib 文件夹是/usr/dev/mysql/lib

4.1 编译成可执行文件

首先我们要进行编译 test.c 为目标文件,这个时候需要执行

gcc –c –I /usr/dev/mysql/include test.c –o test.o  
4.2 链接

最后我们把所有目标文件链接成可执行文件:

gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test 

Linux 下的库文件分为两大类分别是动态链接库(通常以.so 结尾)和静态链接库(通常以.a 结尾), 二者的区别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。

4.3 强制链接时使用静态链接库

默认情况下, GCC 在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链 接库,如果需要的话可以在编译时加上-static 选项,强制使用静态链接库。 在/usr/dev/mysql/lib 目录下有链接时所需要的库文件 libmysqlclient.so 和 libmysqlclient.a,为了让 GCC 在链接时只用到静态链接库,可以使用下面的命令:

gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test 

静态库链接时搜索路径顺序:

  1. ld 会去找 GCC 命令中的参数-L
  2. 再找 gcc 的环境变量 LIBRARY_PATH
  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初 compile gcc 时写在程序内的 动态链接时、执行时搜索路径顺序:
    1. 编译目标代码时指定的动态库搜索路径
    2. 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径
    3. 配置文件/etc/ld.so.conf 中指定的动态库搜索路径
    4. 默认的动态库搜索路径/lib
    5. 默认的动态库搜索路径/usr/lib
      有关环境变量:
      LIBRARY_PATH 环境变量:指定程序静态链接库文件搜索路径
      LD_LIBRARY_PATH 环境变量:指定程序动态链接库文件搜索路径

5 后续

由于GCC编译器背后的故事的操作和GCC常用命令的操作一样,都是按着资料一步一步的操作就好,由于时间原因,我就没有写详细的过程了。下面是我给出的资料链接以及提取码,
链接:https://pan.baidu.com/s/1rcpyn-WFtmEWXxm7ubbjAg
提取码:xcdp
链接:https://pan.baidu.com/s/18psYq-7pKwPbbLf4mkC0fA
提取码:u0j4

二、nasm的安装与练习

1 在ubuntu中下载安装nasm

命令:

sudo apt install nasm

在这里插入图片描述
发现没有nasm这个包。
解决方法:在http://www.nasm.us/pub/nasm/releasebuilds/2.10.07/下载nasm-2.10.07.tar.gz
在这里插入图片描述
然后再把它复制到Ubantu里,
在这里插入图片描述

依次输入命令下面命令就安装成功了

1 | tar -xvf nasm-2.10.07.tar.gz
2 | cd nasm-2.10.07
3 | ./configure
4 | make
5 | sudo make install

2 对示例代码“hello.asm”编译生成可执行程序并比较文件大小

2.1 创建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         ; 调用内核功能
        

在这里插入图片描述
2.2 使用命令

nasm -f elf64 hello.asm

生成hello.o文件
2.3 使用命令

ld -s -o hello hello.o

生成可执行文件hello
2.4 执行
在这里插入图片描述
2.5 查看hello.asm的大小并与“hello world”C代码的编译生成的程序大小进行对比
查看nasm命令生成文件的大小

size hello

在这里插入图片描述
"hello world"C代码编译生成的程序
在这里插入图片描述
查看大小
在这里插入图片描述
显然,nasm对代码“hello.asm”编译生成可执行程序比“hello world”C代码的编译生成的程序小。

第三方库函数的代码设计

1 什么是curses?(百度百科)

curses是一个函数开发包,专门用来进行UNIX下终端环境下的屏幕界面处理以及I/O处理。通过这些函数库,C和C++程序就可以控制终端的视屏显示以及输入输出。

2 curses的主要函数功能

curses 函数库的名称来自它所提供的功能,它能够优化光标的移动并减少需要对屏幕进行的刷新,因此它也减少了必须向字符终端发送的字符数目。

3 curses几个基本函数名称及功能

1、initscr():初始化curses库和ttty,在开始curses编程之前,必须使用initscr()这个函数来开启curses模式。

2、move(y,x);将光标移动至(x,y)处。

3、refresh();我们写入的内容首先会在系统缓冲区中,经过refresh()函数刷新之后才能够显示到屏幕上。

4、echo()和noecho();输入的字符显示和不显示在终端上。可以用密码输入。

5、输出:

addch();显示一个字符

addstr();显示一串字符串

printw(“格式”,变量);显示指定格式的内容,类似于printf();

mvaddstr();移动到某个位置输出。

6、输入:

getch();输入一个字符

getstr();输入一串字符串

scanw("%s",s);;按指定格式输入内容,类似于scanf();

mvaddstr();移动到某个位置输入。

4 在 win10 系统中,以游客身份体验一下即将绝迹的远古时代的 BBS

在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net

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

5 安装curses库

5.1 在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库
在这里插入图片描述
由于没有包,所以需要自己下载安装。
在官网上去下载自己想要的ncurses.tar.gz压缩文件,然后再把压缩文件复制到Ubantu里面,然后进行解压
在这里插入图片描述
在这里插入图片描述

tar -xvf ncurses-6.0.tar.gz

然后依次运行下面的命令就安装成功了

1 | cd ncurses-6.0
2 | ./configure
3 | make
4 | sudo make install

5.2 curses函数库的头文件安装在/usr/include/,库文件安装在/usr/lib/

6 Linux 环境下C语言编译实现贪吃蛇游戏

相应代码在此链接:http://www.linuxidc.com/Linux/2011-08/41375.htm
创建一个新的文件夹mysnake1.0,将相应的代码粘贴上去,然后输入下面的命令

cc mysnake1.0.c -lcurses -o mysnake1.c
./mysnake1.0

最后我们就可玩游戏了

总结

这篇文章主要写了GCC与as汇编编译器以及第三方库函数的代码设计,最后那个贪吃蛇游戏,有一丢丢的小问题,望读者们自行找寻解决方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
纯c读写ini配置文件 用c/c++读写ini配置文件有不少第三方的开源库,如iniparser、libini、rwini、UltraLightINIParser等,但都不理想,往往代码较大、功能较弱、 接口使用不方便。尤其在大小写处理、前后空格、各种注释、跨平台换行符支持、带引号字符串处理、无section操作、原格式保持等方面存在问题。 现将本人精心制作的ini读写程序源码奉献给大家,纯c编写,简洁好用。支持windows和linux。 主要特点: 1、支持;和#注释符号,支持行尾注释。 2、支持带引号'或"成对匹配的字符串,提取时自动去引号。引号中可带其它引号或;#注释符。 3、支持无section或空section(名称为空)。 4、支持10、16、8进制数,0x开头为16进制数,0开头为8进制。 5、支持section、key或=号前后带空格。 6、支持\n、\r、\r\n或\n\r换行格式。 7、不区分section、key大小写,但写入时以新串为准,并保持其大小写。 8、新增数据时,若section存在则在该节最后一个有效数据后添加,否则在文件尾部添加。 9、支持指定key所在整行删除,即删除该键值,包括注释。 10、可自动跳过格式错误行,修改时仍然保留。 11、修改时保留原注释:包括整行注释、行尾注释(包括前面空格)。 12、修改时保留原空行。以上三点主要是尽量保留原格式。 不足之处: 1、不支持单key多value(逗号分割),只能一次性提取后自行处理。 2、不支持同名重复section和key。(重复section可视为错误,重复key则可能造成分歧) 3、不能提取所有section或key名称。 使用只需两个文件inirw.h、inirw.c,另有测试程序和工程文件,支持windows和linux。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值