Linux中的gcc编译以及相关库的操作
一、可执行程序组装步骤
**一个源程序经以下步骤:(1)预处理、(2)汇编、(3)编译(4)链接形成最终的二进制可执行程序。我们知道GCC编译源代码生成最终可执行的二进制程序,GCC后台隐含执行了四个阶段步骤。
1)预处理(Pre-processing)
在该阶段,编译器将C源代码中的包含的头文件如stdio.h编译进来,用户可以使用gcc的选项”-E”进行查看。
用法:#gcc -E file.c -o file.i
作用:将file.c预处理输出file.i文件。
2)编译阶段(Compiling)
第二步进行的是编译阶段,在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译 成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,只生成汇编代码。
选项 -S
用法:[root]# gcc –S –o file.i file.s
作用:将预处理输出文件file.i编译成file.s文件。
3)汇编阶段(Assembling)
汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码.
选项 -c
用法:[root]# gcc –c –o file.s file.o
作用:将汇编输出文件file.s编译输出file.o文件。
4)链接阶段(Link)
在成功编译之后,就进入了链接阶段。
无选项链接
用法:[root]# gcc –o file.o file
作用:将编译输出文件file.o链接成最终可执行文件hello
二、用gcc生成.a静态库和.so动态库
1、编辑生成程序 hello.h、hello.c 和 main.c
(1)创建一个目录,保存本次练习文件,并进入该目录
(2)用vim/nano编译器,创建hello.h文件
代码:
#ifndef HELLO_H
#define HELLO_H
Void hello(const char *name);
#endif //HELLO_H
(3创建hello.c文件
代码:
#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
(3)创建main.c文件
代码:
#include "hello.h"
int main()
{
hello("everyone"); return 0;
}
2、将 hello.c 编译成.o 文件
(1)无论静态库,还是动态库,都是由.o 文件创建的。因此,我们必须将源程序 hello.c 通过 g cc 先编译成.o 文件。在系统提示符下键入以下命令得到 hello.o 文件。
(2)运行 ls 命令看看是否生存了 hello.o 文件
在 ls 命令结果中,我们看到了 hello.o 文件,下面我们先来看看如何创建静态库,以及使用它.
3、由.o 文件创建静态库
静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为 myhello,则静态库文件名就是 libmyhello.a。在创建和使用静态库时, 需要注意这点。
创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
运行 ls 命令查看是否生成文件libmyhello.a
观察结果中有 libmyhello.a
到此静态库就制作完成了,接下来我们来看如何使用它内部的函数
4、在程序中使用静态库
在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明静态库名,gcc 将会从静态库中将公用函数连接到目标文件中。
注意,gcc 会在静态库名前加上前缀 lib,然后追加扩展名.a 得到的静态库文件名来查找静态库文件。
在上面编写的程序 main.c 中,我们包含了静态库的头文件 hello.h,然后在主程序 main 中直接调用公用函数 hello。下面先生成目标程序 hello,然后运行 hello 程序看看结果如何。
方法一:
自定义的库时,main.c 还可放在-L.和 –lmyhello 之间,但是不能放在它俩之后,否则会提示 myhello 没定义。
用ls命令查看是否生成可执行文件。
方法二:
先生成main.o文件
在生成可执行文件并输出结果
方法三:
5、.o文件创建动态库
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其文件扩展名为.so。例如:我们将创建的动态库名为 myhello,则动态库文件名就是 libmyh ello.so。用 gcc 来创建动态库。
(1)创建动态库,并用ls命令看是否成功建立动态库。
(2)在程序中使用动态库
在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。我们先运行 gcc 命令生成目标文件,再运行它看看结果
这里报错了,是因为找不到动态库文件 libmyhello.so。程序在运行时, 会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件 libmyhello.so 复制到目录/usr/lib 中,再试试。
这里复制移动需要获得用户权限,所以命令前加sudo.
运行
三、静态库.a与.so库文件的生成和使用
1.创建一个新目录test2
2.用vi/vim编译所需要的四个文件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 <stdlib.h>
#include "A.h"
int main()
{ print1(1);
print2("test"); exit(0);
}
3.静态库.a 文件的生成与使用
(1)、生成目标文件(xxx.o)
(2)、生成静态库.a 文件
(3)、使用.a 库文件,创建可执行程序(若采用此种方式,需保证生成的.a 文件与.c 文件保存在同一目录下,
即都在当前目录下)
1.共享库.so文件的生成与使用
(1)生成目标文件(xxx.o) (此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so文件时会出错)
(2)、生成共享库.so 文件
(3)、使用.so 库文件,创建可执行程序
此时发现报错运行 ldd test,查看链接情况,
发现确实是找不到对应的.so 文件。
这是由于 linux 自身系统设定的相应的设置的原因,即其只在/lib and /usr/lib 下搜索对应的.so 文件,故需将对应 so 文件拷贝到对应路径。
四、编写程序生成动态库和静态库的练习
1、静态库练习
(1)、编写一个x2x函数(数的求和),一个x2y函数(数的除法),main函数代码将调用x2x和x2y;
将这3个函数分别写成单独的3个 .c文件。
(2)分别创建
sub1.c:
#include<stdio.h>
int x2x(int x,int y)
{
int a=x+y;
return a;
}
sub2.c
#include<stdio.h>
int x2y(int x,int y)
{
int a=x*y;
return a;
}
sub.h:
#ifndef
#define
int x2x(int x,int y);
int x2y(int x,int y);
#endif
main.c:
#include<stdio.h>
#include"sub.h"
int main()
{
int a=2,b=1;
printf("a+b= %d \n",2xx(a,b));
printf("a*b= %d \n",x2y(a,b));
}
(3)编译sub1.c、sub2.c文件为.o文件
(4)生成静态库
(5)用静态库执行main.c文件,并对比大小
2、编写程序生成动态库
(1)、编写一个x2x函数(数的求和),一个x2y函数(数的除法),main函数代码将调用x2x和x2y;
将这3个函数分别写成单独的3个 .c文件。
(2)分别创建sub1.c,sub2.c,sub.h,main.c
(3)编译sub1.c、sub2.c文件为.o文件
(4)生成动态库
(5)将对应 so 文件拷贝到对应路径,
(6)运行并查看动态库生成文件的大小
五、GCC常用命令
1、gcc命令下各选项的含义
-E:仅作预处理,不进行编译、汇编和链接
-S:仅编译到汇编语言,不进行汇编和链接
-c:编译、汇编到目标代码(也就是计算机可识别的二进制)
-o:执行命令后文件的命名
-g:生成调试信息
-w:不生成任何警告
-Wall:生成所有的警告
2.GCC编译C源码四个步骤:
预处理-----> 编译 ----> 汇编 ----> 链接
3.常用命令
1)、ar
用于创建静态链接库。为了便于初学者理解,在此介绍动态库与静态库 的概念:
2)、ld
用于链接。
3)、as
用于汇编。
4)、ldd
可以用于查看一个可执行程序依赖的共享库。
5)、size
查看执行文件中各部分的大小。
6)、addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码 行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对 应的源代码位置。
更多GCC常用命令
六、as汇编编译器
1.ubuntu中下载安装nasm
2.用nasm对示例代码“hello.asm”编译生成可执行程序
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)、使用nasm -f elf64 hello.asm,会生成一个hello.o文件,使用ld -s -o hello hello.o ,生成一个可执行文件hello,执行./hello输出
2.编写“hello world”C代码程序大小与hello.asm进行对比
(1)创建hello.c文件
#include<stdio.h>
int main()
{
printf("hello world");
return 0;
}
七、Linux中的第三方库函数
1、光标库(curses)的主要函数
从屏幕读取:
chtype inch(void); //返回光标位置字符
int instr(char *string); //读取字符到string所指向的字符串中
int innstr(char *string, int numbers);//读取numbers个字符到string所指向的字符串中
窗口移动和更新屏幕:
int mvwin(WINDOW *win, int new_y, int new_x); //移动窗口
int wrefresh(WINDOW *win);
int wclear(WINDOW *win);
int werase(WINDOW *win);
//类似于上面的refresh, clear, erase,但是此时针对特定窗口操作,而不是stdcur
int touchwin(WINDOW *win); //指定该窗口内容已改变、
//下次wrefresh时,需重绘窗口。利用该函数,安排要显示的窗口
int scrollok(WINDOW *win, bool flag); //指定是否允许窗口卷屏
int scroll(WINDOW *win); //把窗口内容上卷一行
2.以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)
1)在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。
2)打开一个cmd命令行窗口(win+R,输入cmd),命令行输入 telnet bbs.newsmth.net(我的貌似不行。。。)
2.curses库的安装
(1)在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库
3.4、gcc编译生成一个终端游戏
弹球游戏
贪吃蛇
将代码复制下来按编程步骤进行就行