GCC编译器的常用命令及使用

    • 一、用gcc生成静态库和动态库
      • (1)编辑生成子程序hello.h、hello.c、main.c
      • (2)将hello.c编译成.o文件
      • (3)由.o文件创建静态库
      • (4)在程序中使用静态库
      • (5)由.o文件创建动态库文件
      • (6)在程序中使用动态库
    • 二、linux下静态库.a和.so库文件的生成和使用
      • (1)静态库.a文件的生成与使用
      • (2)共享库.so文件的生成和使用
    • 三、GCC常用命令
      • (1)C语言程序编译
      • (2)多个程序文件的编译
      • (3)检错
      • (4)链接
      • (5)ELF文件
      • (6)使用nasm汇编编译器编译hello.asm
    • 四、第三方库函数curses
      • 用C语言编写一个贪吃蛇游戏

一、用gcc生成静态库和动态库

在创建库函数之前,先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

(1)编辑生成子程序hello.h、hello.c、main.c

先创建一个目录,用来保存用到的文件

xh@ubuntu:~$ mkdir test1
xh@ubuntu:~$ cd test1
xh@ubuntu:~/test1$ 

然后用vim、nano或gedit等文本编辑器生成所需要的3个文件。

xh@ubuntu:~/test1$ vim hello.c
#include<stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n",name);
}

利用vim编写完hello.c文件程序后,按esc键,输入:wq保存并退出;
在终端里输入nano hello.h编辑生成hello.h文件,写完之后按ctrl+X保存退出即可;

xh@ubuntu:~/test1$ nano hello.h
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif//HELLO_H

接着在终端里输入gedit main.c,创建编辑main.c文件;

xh@ubuntu:~/test1$ gedit main.c
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}

终端里面输入ls就可以看到我们刚刚建立的三个文件。

xh@ubuntu:~/test1$ ls
hello.c  hello.h  main.c

(2)将hello.c编译成.o文件

利用gcc命令编译hello.c

xh@ubuntu:~/test1$ gcc -c hello.c

在unbuntu终端运行ls命令看看是否生成了hello.o文件

xh@ubuntu:~/test1$ ls
hello.c  hello.h  hello.o  main.c

可以看到已经生成了hello.o文件

(3)由.o文件创建静态库

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库名就是libmyhello.a。在创建静态库时,需要注意这点。创建静态库用ar命令。在终端下输入ar -crv libmyhello.a hello.o将创建静态库文件libmyhello。

xh@ubuntu:~/test1$ ar -crv libmyhello.a hello.o
a - hello.o

同样在终端运行ls命令查看结果

xh@ubuntu:~/test1$ ls
hello.c  hello.h  hello.o  libmyhello.a  main.c

(4)在程序中使用静态库

先生成目标程序hello,生成目标程序hello可有3种方式:
方法一:

xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello

终端输入ls命令可以查看到目标程序hello已生成

xh@ubuntu:~/test1$ ls
hello  hello.c  hello.h  hello.o  libmyhello.a  main.c

自定义的库时,main.c还可放在-L.和-lmyhello之间,但是不能放在他俩之后,否则会提示myhello没定义。

方式二:
终端输入gcc main.c libmyhello.a -o hello

xh@ubuntu:~/test1$ gcc main.c libmyhello.a -o hello

方式三:
先在终端输入gcc -c main.c生成main.o;

xh@ubuntu:~/test1$ gcc -c main.c
xh@ubuntu:~/test1$ ls
hello  hello.c  hello.h  hello.o  libmyhello.a  main.c  main.o

再输入gcc -o hello mian.o libmyhello.a生成可执行文件。

xh@ubuntu:~/test1$ gcc -o hello main.o libmyhello.a

终端里输入./hello就可以运行hello程序;

xh@ubuntu:~/test1$ ./hello
Hello everyone!

我们删除静态文件试试公用函数hello是否真的连接到目标文件hello中了。

xh@ubuntu:~/test1$ rm libmyhello.a
xh@ubuntu:~/test1$ ls
hello  hello.c  hello.h  hello.o  main.c  main.o
xh@ubuntu:~/test1$ ./hello
Hello everyone!

由上可以看出,程序照常运行,静态库中的公用函数已经连接到目标文件中了。

(5)由.o文件创建动态库文件

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件名扩展为.so。例如:我们将创建的动态库命名为myhello,则动态库文件名就是libmyhello.so。用gcc来创建动态库。
在系统命令提示符下输入gcc -shared -fPIC -o libmyhello.so hello.o得到动态库文件libmyhello.so;

xh@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
/usr/bin/ld: hello.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
hello.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status

可以看到这里出现了错误,这时的解决方法是将前面的gcc -c hello.c换成gcc -fPIC -c hello.c;

xh@ubuntu:~/test1$ gcc -fPIC -c hello.c
xh@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
xh@ubuntu:~/test1$ ls
hello  hello.c  hello.h  hello.o  libmyhello.so  main.c  main.o

由上述命令结果可知,这时再执行前面生成动态文件的命令将不会出错。

(6)在程序中使用动态库

先用gcc命令生成目标文件

xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello

然后输入./hello运行程序。

xh@ubuntu:~/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

哦!出错了。看看错误提示,原来是找不到动态文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则提示错误并终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试试。

xh@ubuntu:~/test1$ mv libmyhello.so /usr/lib
mv: cannot move 'libmyhello.so' to '/usr/lib/libmyhello.so': Permission denied

可以看到又出现了问题,这时我们可以尝试在终端输入sudo su,输入密码,切换到root用户下,再输入cp libmyhello.so /usr/lib命令,将libmyhello.so复制到/usr/lib目录之下,最后输入./hello执行程序。

xh@ubuntu:~/test1$ sudo su
[sudo] password for xh: 
root@ubuntu:/home/xh/test1# cp libmyhello.so /usr/lib
root@ubuntu:/home/xh/test1# ./hello
Hello everyone!

可以看到,这时已经成功输出结果。
回过头来,我们可以发现使用静态库和动态库编译生成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?
首先我们先删除除.c和.h外的所有文件,利用rm 文件名命令来实现;

xh@ubuntu:~/test1$ rm libmyhello.so
xh@ubuntu:~/test1$ ls
hello.c  hello.h  main.c  main.o
xh@ubuntu:~/test1$ rm main.o
xh@ubuntu:~/test1$ ls
hello.c  hello.h  main.c

再来创建静态库文件libmyhello.a和动态文件libmyhello.so.

xh@ubuntu:~/test1$ ls
hello.c  hello.h  main.c
xh@ubuntu:~/test1$ gcc -fPIC -c hello.c
xh@ubuntu:~/test1$ ls
hello.c  hello.h  hello.o  main.c
xh@ubuntu:~/test1$ ar -cr libmyhello.a hello.o
xh@ubuntu:~/test1$ ls
hello.c  hello.h  hello.o  libmyhello.a  main.c
xh@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
xh@ubuntu:~/test1$ ls
hello.c  hello.h  hello.o  libmyhello.a  libmyhello.so  main.c

接着,我们运行gcc命令来使用函数库myhello生成目录文件hello,并运行程序hello。

xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
xh@ubuntu:~/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

可以看到上诉程序运行时有误,可以根据错误提示得出,当静态库和动态库同名时,gcc命令将优先使用动态库,默认去连/usr/lib/lib等目录的动态库,将文件libmyhello.so复制到目录/usr/lib中即可。

xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
xh@ubuntu:~/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
xh@ubuntu:~/test1$ sudo su
root@ubuntu:/home/xh/test1# cp libmyhello.so /usr/lib
root@ubuntu:/home/xh/test1# exit
exit
xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
xh@ubuntu:~/test1$ ls
hello  hello.c  hello.h  hello.o  libmyhello.a  libmyhello.so  main.c
xh@ubuntu:~/test1$ ./hello
Hello everyone!

二、linux下静态库.a和.so库文件的生成和使用

首先创建一个目录

xh@ubuntu:~/test1$ mkdir test2
xh@ubuntu:~/test1$ cd test2

然后用vim、nano或gedit等文本编辑器编辑生成所需要的四个文件x2x.c、x2y.c、x2.h、main.c。

xh@ubuntu:~/test1/test2$ vim x2x.c
#include<stdio.h>
float x2x(int a,int b)
{
float sum;
sum=a+b;
print("a+b=%.2f\n",sum);
}
#include<stdio.h>
float x2y(int m,int n)
{
float mul;
mul=m*n;
printf("m*n=%.2f\n",mul);
}
#ifndef x2_H
#define x2_H
float x2x(int,int);
float x2y(int,int);
#endif
#include<stdio.h>
#include "x2.h"
int main()
{
x2x(3,4);
x2y(5,6);
exit(0);
}

(1)静态库.a文件的生成与使用

首先生成目标文件,即.o文件;

xh@ubuntu:~/test2$ gcc -c x2x.c x2y.c
xh@ubuntu:~/test2$ ls
main.c  x2.h  x2x.c  x2x.o  x2y.c  x2y.o

然后生成静态库.a文件;

xh@ubuntu:~/test2$ ar crv libafile.a x2x.o x2y.o
a - x2x.o
a - x2y.o
xh@ubuntu:~/test2$ ls
libafile.a  main.c  x2.h  x2x.c  x2x.o  x2y.c  x2y.o

使用.a文件,创建可执行程序;

root@ubuntu:/home/xh/test2# gcc -o main main.c libafile.a
root@ubuntu:/home/xh/test2# ./main
a+b=7.00
m*n=30.00

在终端命令行里输入ls -l main即可查看生成的目标程序main的大小。

root@ubuntu:/home/xh/test2# ls -l main
-rwxr-xr-x 1 root root 8720 Oct 16 17:02 main

(2)共享库.so文件的生成和使用

首先生成目标文件,即.o文件;

root@ubuntu:/home/xh/test2# gcc -c -fPIC x2x.c x2y.c

生成共享库.so文件;

xh@ubuntu:~/test2$ gcc -shared -fpic -o libsofile.so x2x.o x2y.o
xh@ubuntu:~/test2$ ls
libafile.a  libsofile.so  main  main.c  main.o  x2.h  x2x.c  x2x.o  x2y.c  x2y.o

使用.so库文件,创建可执行程序;

xh@ubuntu:~/test2$ gcc -o main main.c libsofile.so
xh@ubuntu:~/test2$ ./main
./main: error while loading shared libraries: libsofile.so: cannot open shared object file: No such file or directory

这里出现了错误,运行ldd main,查看情况;

xh@ubuntu:~/test2$ ldd mainlinux-vdso.so.1 =>  (0x00007fff83bdb000)libsofile.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0787635000)/lib64/ld-linux-x86-64.so.2 (0x00007f07879ff000)

发现确实找不到.so文件。这是由于linux自身系统设定的原因,其只在/lib和/usr/lib下搜索对应的.so文件,只需将.so文件复制到其路径下即可;

xh@ubuntu:~/test2$ sudo su
[sudo] password for xh: 
root@ubuntu:/home/xh/test2# cp libsofile.so /usr/lib
root@ubuntu:/home/xh/test2# ./main
a+b=7.00
m*n=30.00

可以看到程序现在能正常运行了。
在终端命令行里输入ls -l main即可查看生成的目标程序main的大小。

root@ubuntu:/home/xh/test2# ls -l main
-rwxrwxr-x 1 xh xh 8624 Oct 16 17:29 main

将动态库生成的可执行程序和静态库相比较,发现动态库生成的可执行程序要小一些。

三、GCC常用命令

GCC即GNU C Compiler,GCC不仅支持C语言,它还支持Ada语言、C++语言、Java语言等。

(1)C语言程序编译

下面举一个C语言例子:

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

这个程序一步到位的命令是gcc test5.c -o test5
实质上,上述编译过程是分四个阶段进行的,即预处理(也称为预编译)、编译、汇编和连接。

使用gcc -E test5.c -o test5.igcc -E test5.c进行预编译;

xh@ubuntu:~$ gcc -E test5.c -o test5.i
xh@ubuntu:~$ ls
a.out      Downloads         hello    main1.c   mian     Pictures  sub1.o     test1  test5    test.c
Desktop    examples.desktop  hello.c  main1.o   mkefile  Public    sub.c      test2  test5.c  Videos
Documents  func.h            main     makefile  Music    sub1.c    Templates  test3  test5.i

此时生成test5.i文件
预处理之后,可直接对生成的test5.i文件编译,生成汇编代码:

xh@ubuntu:~$ gcc -S test5.i -o test5.s
xh@ubuntu:~$ ls
a.out      Downloads         hello    main1.c   mian     Pictures  sub1.o     test1  test5    test5.s
Desktop    examples.desktop  hello.c  main1.o   mkefile  Public    sub.c      test2  test5.c  test.c
Documents  func.h            main     makefile  Music    sub1.c    Templates  test3  test5.i  Videos

gcc的-S选项,表示在程序编译期间,在生成汇编代码之后,停止,-o输出汇编代码文件。
将生产的汇编代码test5.s,用gas汇编器将其编译成目标文件:

xh@ubuntu:~$ gcc -c test5.s -o test5.o
xh@ubuntu:~$ ls
a.out      Downloads         hello    main1.c   mian     Pictures  sub1.o     test1  test5    test5.o  Videos
Desktop    examples.desktop  hello.c  main1.o   mkefile  Public    sub.c      test2  test5.c  test5.s
Documents  func.h            main     makefile  Music    sub1.c    Templates  test3  test5.i  test.c

gcc连接器是gas提供的,负责将程序中的目标文件与所需的所以附加文件的目标文件连接起来,最终生成可执行文件。

xh@ubuntu:~$ gcc test5.o -o test5
xh@ubuntu:~$ ls
a.out      Downloads         hello    main1.c   mian     Pictures  sub1.o     test1  test5    test5.o  Videos
Desktop    examples.desktop  hello.c  main1.o   mkefile  Public    sub.c      test2  test5.c  test5.s
Documents  func.h            main     makefile  Music    sub1.c    Templates  test3  test5.i  test.c

运行可执行文件。

xh@ubuntu:~$ ./test5
Hello World!

(2)多个程序文件的编译

假设有一个由test6.c和test7.c两个源文件组成的程序,为了对他们进行编译,并最终生成可执行程序test,可使用gcc test6.c test7.c -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)链接

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

  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

(5)ELF文件

1、ELF文件的段
ELF 文件位于 ELF Header 和 Section Header Table 之间的都
是段(Section)。一个典型的 ELF 文件包含下面几个段:
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
可以使用 readelf -S 查看其各个 section 的信息如下:
在这里插入图片描述

xh@ubuntu:~$ readelf -S hello
There are 31 section headers, starting at offset 0x19d8:Section Headers:[Nr] Name              Type             Address           OffsetSize              EntSize          Flags  Link  Info  Align[ 0]                   NULL             0000000000000000  000000000000000000000000  0000000000000000           0     0     0[ 1] .interp           PROGBITS         0000000000400238  00000238000000000000001c  0000000000000000   A       0     0     1[ 2] .note.ABI-tag     NOTE             0000000000400254  000002540000000000000020  0000000000000000   A       0     0     4[ 3] .note.gnu.build-i NOTE             0000000000400274  000002740000000000000024  0000000000000000   A       0     0     4[ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298000000000000001c  0000000000000000   A       5     0     8[ 5] .dynsym           DYNSYM           00000000004002b8  000002b80000000000000060  0000000000000018   A       6     1     8[ 6] .dynstr           STRTAB           0000000000400318  00000318000000000000003d  0000000000000000   A       0     0     1[ 7] .gnu.version      VERSYM           0000000000400356  000003560000000000000008  0000000000000002   A       5     0     2[ 8] .gnu.version_r    VERNEED          0000000000400360  000003600000000000000020  0000000000000000   A       6     1     8[ 9] .rela.dyn         RELA             0000000000400380  000003800000000000000018  0000000000000018   A       5     0     8[10] .rela.plt         RELA             0000000000400398  000003980000000000000030  0000000000000018  AI       5    24     8[11] .init             PROGBITS         00000000004003c8  000003c8000000000000001a  0000000000000000  AX       0     0     4[12] .plt              PROGBITS         00000000004003f0  000003f00000000000000030  0000000000000010  AX       0     0     16[13] .plt.got          PROGBITS         0000000000400420  000004200000000000000008  0000000000000000  AX       0     0     8[14] .text             PROGBITS         0000000000400430  000004300000000000000182  0000000000000000  AX       0     0     16[15] .fini             PROGBITS         00000000004005b4  000005b40000000000000009  0000000000000000  AX       0     0     4[16] .rodata           PROGBITS         00000000004005c0  000005c00000000000000010  0000000000000000   A       0     0     4[17] .eh_frame_hdr     PROGBITS         00000000004005d0  000005d00000000000000034  0000000000000000   A       0     0     4[18] .eh_frame         PROGBITS         0000000000400608  0000060800000000000000f4  0000000000000000   A       0     0     8[19] .init_array       INIT_ARRAY       0000000000600e10  00000e100000000000000008  0000000000000000  WA       0     0     8[20] .fini_array       FINI_ARRAY       0000000000600e18  00000e180000000000000008  0000000000000000  WA       0     0     8[21] .jcr              PROGBITS         0000000000600e20  00000e200000000000000008  0000000000000000  WA       0     0     8[22] .dynamic          DYNAMIC          0000000000600e28  00000e2800000000000001d0  0000000000000010  WA       6     0     8[23] .got              PROGBITS         0000000000600ff8  00000ff80000000000000008  0000000000000008  WA       0     0     8[24] .got.plt          PROGBITS         0000000000601000  000010000000000000000028  0000000000000008  WA       0     0     8[25] .data             PROGBITS         0000000000601028  000010280000000000000010  0000000000000000  WA       0     0     8[26] .bss              NOBITS           0000000000601038  000010380000000000000008  0000000000000000  WA       0     0     1[27] .comment          PROGBITS         0000000000000000  000010380000000000000035  0000000000000001  MS       0     0     1[28] .shstrtab         STRTAB           0000000000000000  000018cc000000000000010c  0000000000000000           0     0     1[29] .symtab           SYMTAB           0000000000000000  000010700000000000000648  0000000000000018          30    47     8[30] .strtab           STRTAB           0000000000000000  000016b80000000000000214  0000000000000000           0     0     1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), l (large)I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)

2、反编译ELF
由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包
含的指令和数据,需要使用反汇编的方法。
使用 objdump -D 对其进行反汇编如下:

xh@ubuntu:~$ objdump -S hellohello:     file format elf64-x86-64Disassembly of section .init:00000000004003c8 <_init>:4003c8:	48 83 ec 08          	sub    $0x8,%rsp4003cc:	48 8b 05 25 0c 20 00 	mov    0x200c25(%rip),%rax        # 600ff8 <_DYNAMIC+0x1d0>4003d3:	48 85 c0             	test   %rax,%rax4003d6:	74 05                	je     4003dd <_init+0x15>4003d8:	e8 43 00 00 00       	callq  400420 <__libc_start_main@plt+0x10>4003dd:	48 83 c4 08          	add    $0x8,%rsp4003e1:	c3                   	retq   Disassembly of section .plt:00000000004003f0 <puts@plt-0x10>:4003f0:	ff 35 12 0c 20 00    	pushq  0x200c12(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>4003f6:	ff 25 14 0c 20 00    	jmpq   *0x200c14(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>4003fc:	0f 1f 40 00          	nopl   0x0(%rax)0000000000400400 <puts@plt>:400400:	ff 25 12 0c 20 00    	jmpq   *0x200c12(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>400406:	68 00 00 00 00       	pushq  $0x040040b:	e9 e0 ff ff ff       	jmpq   4003f0 <_init+0x28>0000000000400410 <__libc_start_main@plt>:400410:	ff 25 0a 0c 20 00    	jmpq   *0x200c0a(%rip)        # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>400416:	68 01 00 00 00       	pushq  $0x140041b:	e9 d0 ff ff ff       	jmpq   4003f0 <_init+0x28>Disassembly of section .plt.got:0000000000400420 <.plt.got>:400420:	ff 25 d2 0b 20 00    	jmpq   *0x200bd2(%rip)        # 600ff8 <_DYNAMIC+0x1d0>400426:	66 90                	xchg   %ax,%axDisassembly of section .text:0000000000400430 <_start>:400430:	31 ed                	xor    %ebp,%ebp400432:	49 89 d1             	mov    %rdx,%r9400435:	5e                   	pop    %rsi400436:	48 89 e2             	mov    %rsp,%rdx400439:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp40043d:	50                   	push   %rax40043e:	54                   	push   %rsp40043f:	49 c7 c0 b0 05 40 00 	mov    $0x4005b0,%r8400446:	48 c7 c1 40 05 40 00 	mov    $0x400540,%rcx40044d:	48 c7 c7 26 05 40 00 	mov    $0x400526,%rdi400454:	e8 b7 ff ff ff       	callq  400410 <__libc_start_main@plt>400459:	f4                   	hlt    40045a:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)0000000000400460 <deregister_tm_clones>:400460:	b8 3f 10 60 00       	mov    $0x60103f,%eax400465:	55                   	push   %rbp400466:	48 2d 38 10 60 00    	sub    $0x601038,%rax40046c:	48 83 f8 0e          	cmp    $0xe,%rax400470:	48 89 e5             	mov    %rsp,%rbp400473:	76 1b                	jbe    400490 <deregister_tm_clones+0x30>400475:	b8 00 00 00 00       	mov    $0x0,%eax40047a:	48 85 c0             	test   %rax,%rax40047d:	74 11                	je     400490 <deregister_tm_clones+0x30>40047f:	5d                   	pop    %rbp400480:	bf 38 10 60 00       	mov    $0x601038,%edi400485:	ff e0                	jmpq   *%rax400487:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)40048e:	00 00 400490:	5d                   	pop    %rbp400491:	c3                   	retq   400492:	0f 1f 40 00          	nopl   0x0(%rax)400496:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)40049d:	00 00 00 00000000004004a0 <register_tm_clones>:4004a0:	be 38 10 60 00       	mov    $0x601038,%esi4004a5:	55                   	push   %rbp4004a6:	48 81 ee 38 10 60 00 	sub    $0x601038,%rsi4004ad:	48 c1 fe 03          	sar    $0x3,%rsi4004b1:	48 89 e5             	mov    %rsp,%rbp4004b4:	48 89 f0             	mov    %rsi,%rax4004b7:	48 c1 e8 3f          	shr    $0x3f,%rax4004bb:	48 01 c6             	add    %rax,%rsi4004be:	48 d1 fe             	sar    %rsi4004c1:	74 15                	je     4004d8 <register_tm_clones+0x38>4004c3:	b8 00 00 00 00       	mov    $0x0,%eax4004c8:	48 85 c0             	test   %rax,%rax4004cb:	74 0b                	je     4004d8 <register_tm_clones+0x38>4004cd:	5d                   	pop    %rbp4004ce:	bf 38 10 60 00       	mov    $0x601038,%edi4004d3:	ff e0                	jmpq   *%rax4004d5:	0f 1f 00             	nopl   (%rax)4004d8:	5d                   	pop    %rbp4004d9:	c3                   	retq   4004da:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)00000000004004e0 <__do_global_dtors_aux>:4004e0:	80 3d 51 0b 20 00 00 	cmpb   $0x0,0x200b51(%rip)        # 601038 <__TMC_END__>4004e7:	75 11                	jne    4004fa <__do_global_dtors_aux+0x1a>4004e9:	55                   	push   %rbp4004ea:	48 89 e5             	mov    %rsp,%rbp4004ed:	e8 6e ff ff ff       	callq  400460 <deregister_tm_clones>4004f2:	5d                   	pop    %rbp4004f3:	c6 05 3e 0b 20 00 01 	movb   $0x1,0x200b3e(%rip)        # 601038 <__TMC_END__>4004fa:	f3 c3                	repz retq 4004fc:	0f 1f 40 00          	nopl   0x0(%rax)0000000000400500 <frame_dummy>:400500:	bf 20 0e 60 00       	mov    $0x600e20,%edi400505:	48 83 3f 00          	cmpq   $0x0,(%rdi)400509:	75 05                	jne    400510 <frame_dummy+0x10>40050b:	eb 93                	jmp    4004a0 <register_tm_clones>40050d:	0f 1f 00             	nopl   (%rax)400510:	b8 00 00 00 00       	mov    $0x0,%eax400515:	48 85 c0             	test   %rax,%rax400518:	74 f1                	je     40050b <frame_dummy+0xb>40051a:	55                   	push   %rbp40051b:	48 89 e5             	mov    %rsp,%rbp40051e:	ff d0                	callq  *%rax400520:	5d                   	pop    %rbp400521:	e9 7a ff ff ff       	jmpq   4004a0 <register_tm_clones>0000000000400526 <main>:400526:	55                   	push   %rbp400527:	48 89 e5             	mov    %rsp,%rbp40052a:	bf c4 05 40 00       	mov    $0x4005c4,%edi40052f:	e8 cc fe ff ff       	callq  400400 <puts@plt>400534:	b8 00 00 00 00       	mov    $0x0,%eax400539:	5d                   	pop    %rbp40053a:	c3                   	retq   40053b:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)0000000000400540 <__libc_csu_init>:400540:	41 57                	push   %r15400542:	41 56                	push   %r14400544:	41 89 ff             	mov    %edi,%r15d400547:	41 55                	push   %r13400549:	41 54                	push   %r1240054b:	4c 8d 25 be 08 20 00 	lea    0x2008be(%rip),%r12        # 600e10 <__frame_dummy_init_array_entry>400552:	55                   	push   %rbp400553:	48 8d 2d be 08 20 00 	lea    0x2008be(%rip),%rbp        # 600e18 <__init_array_end>40055a:	53                   	push   %rbx40055b:	49 89 f6             	mov    %rsi,%r1440055e:	49 89 d5             	mov    %rdx,%r13400561:	4c 29 e5             	sub    %r12,%rbp400564:	48 83 ec 08          	sub    $0x8,%rsp400568:	48 c1 fd 03          	sar    $0x3,%rbp40056c:	e8 57 fe ff ff       	callq  4003c8 <_init>400571:	48 85 ed             	test   %rbp,%rbp400574:	74 20                	je     400596 <__libc_csu_init+0x56>400576:	31 db                	xor    %ebx,%ebx400578:	0f 1f 84 00 00 00 00 	nopl   0x0(%rax,%rax,1)40057f:	00 400580:	4c 89 ea             	mov    %r13,%rdx400583:	4c 89 f6             	mov    %r14,%rsi400586:	44 89 ff             	mov    %r15d,%edi400589:	41 ff 14 dc          	callq  *(%r12,%rbx,8)40058d:	48 83 c3 01          	add    $0x1,%rbx400591:	48 39 eb             	cmp    %rbp,%rbx400594:	75 ea                	jne    400580 <__libc_csu_init+0x40>400596:	48 83 c4 08          	add    $0x8,%rsp40059a:	5b                   	pop    %rbx40059b:	5d                   	pop    %rbp40059c:	41 5c                	pop    %r1240059e:	41 5d                	pop    %r134005a0:	41 5e                	pop    %r144005a2:	41 5f                	pop    %r154005a4:	c3                   	retq   4005a5:	90                   	nop4005a6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)4005ad:	00 00 00 00000000004005b0 <__libc_csu_fini>:4005b0:	f3 c3                	repz retq Disassembly of section .fini:00000000004005b4 <_fini>:4005b4:	48 83 ec 08          	sub    $0x8,%rsp4005b8:	48 83 c4 08          	add    $0x8,%rsp4005bc:	c3                   	retq   

(6)使用nasm汇编编译器编译hello.asm

在终端命令行内输入sudo apt-get install nasm安装nasm编译器。
编写一个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         ; 调用内核功能

使用nasm -f win64 hello.asm命令进行编译;
进而生成可执行程序进行运行;

xh@ubuntu:~$ touch hello.asm
xh@ubuntu:~$ gedit hello.asm
xh@ubuntu:~$ nasm -f elf64 hello.asm
xh@ubuntu:~$ ld -s -o hello hello.o
xh@ubuntu:~$ ./hello
Hello, world!

利用size+文件名即可查看其文件大小。

xh@ubuntu:~$ size hellotext	   data	    bss	    dec	    hex	filename34	     14	      0	     48	     30	hello

再查看c代码里面编译生成的目标程序的大小:

xh@ubuntu:~$ size hellotext	   data	    bss	    dec	    hex	filename1182	    552	      8	   1742	    6ce	hello

可以发现用nasm汇编编译器生成的目标文件更节省空间。

四、第三方库函数curses

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

当对使用curse函数库的程序进行编译时,必须在程序中包含头文件curses.h,需要在命令行中用-lcurses选项对curses函数库进行链接,所有的curses程序必须从初始函数initscr开始,以函数endwin结束。函数initscr在一个程序中只能调用一次。

在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在ubuntu中安装cueses库,需要用到sudo apt-get install libncurses5-dev命令。安装完成后我们可以用find / -name curses.h命令查看头文件和库文件所在的目录:

xh@ubuntu:~$ sudo su
root@ubuntu:/home/xh# find / -name curses.h
/usr/include/curses.h
find: ‘/run/user/1000/gvfs’: Permission denied
xh@ubuntu:~$ find / -name term.h
find: ‘/etc/cups/ssl’: Permission denied
find: ‘/etc/ssl/private’: Permission denied
find: ‘/etc/polkit-1/localauthority’: Permission denied
find: ‘/proc/tty/driver’: Permission denied
find: ‘/proc/1/task/1/fd’: Permission denied
find: ‘/proc/1/task/1/fdinfo’: Permission denied
find: ‘/proc/1/task/1/ns’: Permission denied
find: ‘/proc/1/fd’: Permission denied
find: ‘/proc/1/map_files’: Permission denied
find: ‘/proc/1/fdinfo’: Permission denied
find: ‘/proc/1/ns’: Permission denied
find: ‘/proc/2/task/2/fd’: Permission denied
find: ‘/proc/2/task/2/fdinfo’: Permission denied
/usr/include/term.h

有许多目录因为权限的原因无法遍历。
那么我们可以换一下试试,用whereis代替find:

xh@ubuntu:~$ whereis / -name curses.h
: /usr/bin/. /usr/sbin/. /usr/lib/. /bin/. /sbin/. /etc/. /lib/. /lib64/. /usr/games/. /usr/local/bin/. /usr/local/sbin/. /usr/local/etc/. /usr/local/lib/. /usr/local/games/. /usr/include/. /usr/local/. /usr/libexec/. /usr/share/. /usr/share/man/hy/. /usr/share/man/bs/. /usr/share/man/cy/. /usr/share/man/shn/. /usr/share/man/sl/. /usr/share/man/nb/. /usr/share/man/ms/. /usr/share/man/kk/. /usr/share/man/man2/. /usr/share/man/ps/. /usr/share/man/man9/. /usr/share/man/fr.ISO8859-1/. /usr/share/man/fr_CA/. /usr/share/man/man8/. /usr/share/man/si/. /usr/share/man/pl/. /usr/share/man/eo/. /usr/share/man/el/. /usr/share/man/uk/. /usr/share/man/my/. /usr/share/man/ku/. /usr/share/man/bg/. /usr/share/man/lt/. /usr/share/man/ro/. /usr/share/man/tr/. /usr/share/man/sq/. /usr/share/man/zh_HK/. /usr/share/man/man4/. /usr/share/man/io/. /usr/share/man/man1/. /usr/share/man/es/. /usr/share/man/et/. /usr/share/man/fo/. /usr/share/man/ta/. /usr/share/man/hu/. /usr/share/man/ne/. /usr/share/man/gl/. /usr/share/man/eu/. /usr/share/man/gd/. /usr/share/man/zh_TW/. /usr/share/man/pt_BR/. /usr/share/man/fa/. /usr/share/man/it/. /usr/share/man/hi/. /usr/share/man/pa/. /usr/share/man/ce/. /usr/share/man/lv/. /usr/share/man/sv/. /usr/share/man/ca/. /usr/share/man/man3/. /usr/share/man/fy/. /usr/share/man/se/. /usr/share/man/he/. /usr/share/man/bn/. /usr/share/man/sr/. /usr/share/man/ug/. /usr/share/man/uz/. /usr/share/man/man7/. /usr/share/man/bo/. /usr/share/man/ru/. /usr/share/man/en_GB/. /usr/share/man/fr.UTF-8/. /usr/share/man/id/. /usr/share/man/pt/. /usr/share/man/az/. /usr/share/man/th/. /usr/share/man/ar/. /usr/share/man/man5/. /usr/share/man/ca@valencia/. /usr/share/man/en_CA/. /usr/share/man/cs/. /usr/share/man/ko/. /usr/share/man/vi/. /usr/share/man/oc/. /usr/share/man/fi/. /usr/share/man/mhr/. /usr/share/man/de/. /usr/share/man/nn/. /usr/share/man/zh_CN/. /usr/share/man/ast/. /usr/share/man/hr/. /usr/share/man/sk/. /usr/share/man/fr/. /usr/share/man/km/. /usr/share/man/te/. /usr/share/man/nl/. /usr/share/man/en_AU/. /usr/share/man/ml/. /usr/share/man/da/. /usr/share/man/be/. /usr/share/man/man6/. /usr/share/man/ja/. /usr/share/info/. /usr/src/linux-headers-4.15.0-120-generic/.tmp_versions /usr/src/linux-headers-4.15.0-120-generic/.config /usr/src/linux-headers-4.15.0-120-generic/. /usr/src/linux-headers-4.15.0-112-generic/.tmp_versions /usr/src/linux-headers-4.15.0-112-generic/.config /usr/src/linux-headers-4.15.0-112-generic/. /usr/src/linux-headers-4.15.0-112/. /usr/src/linux-headers-4.15.0-120/.

可以看到,whereis可以访问。

用C语言编写一个贪吃蛇游戏

完整代码:http://www.linuxidc.com/Linux/2011-08/41375.htm
将代码在linux ubuntu中写入.c文件,在终端输入gcc snake.c -lcurses -o snake命令生成目标可执行程序;
最后输入./snake即可运行。
在这里插入图片描述

在这里插入图片描述

总结:gcc编译器使用广泛,命令也比较多样,静态库和动态库在linux里面都是比较重要的一个概念,关于curses应用比较复杂,要想搞懂,还需要多费一些时间。在编写命令过程中也是在不断尝试,遇到报错也不要慌,遇到了就是赚到,这次想办法解决了,下次就能轻松解决。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平