编译器背后的故事

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

1,用编辑器生成例子程序 hello.h,hello.c,main.c

(1)创建一个作业目录

在这里插入图片描述
(2)进入该目录
在这里插入图片描述
(3)创建hello.h文件,并且输入代码

#ifndef HELLO_H 
#define HELLO_H
Void hello(const char *name); 
#endif //HELLO_H

(4)创建hello.c文件并输入代码
在这里插入图片描述

#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}

(5)创建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文件创建静态库

(1)静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为 myhello,则静态库文件名就是 libmyhello.a。在创建和使用静态库时, 需要注意这点。
创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
在这里插入图片描述
(2)运行ls查看是否生成对应程序
在这里插入图片描述
(3)ls命令中有libmyhello.a。

4,在程序中使用静态库

注意,gcc 会在静态库名前加上前缀 lib,然后追加扩展名.a 得到的静态库文件名来查找静态库文件。
(1)方法一:
在这里插入图片描述
运行查看结果
在这里插入图片描述
方法二:
在这里插入图片描述
查看结果
在这里插入图片描述
方法三:
先生成main.o,在生成可执行文件
在这里插入图片描述
在这里插入图片描述
查看结果
在这里插入图片描述
(2)我们删除静态库文件试试公用函数 hello 是否真的连接到目标文件 hello 中。
在这里插入图片描述
(3)运行 ls 命令查看是否生成文件libmyhello.a,删除后运行hello:
在这里插入图片描述
程序照常运行,静态库中的公用函数已经连接到目标文件中了。
我们继续看如何在 Linux 中创建动态库。我们还是从.o 文件开始。

二,静态库.a与.so库文件的生成与使用

1,回到上个目录,创建新的目录test2

在这里插入图片描述

2,然后用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的四个文件 A1.c 、 A2.c、 A.h、test.c

(1)创建A1.c并输入代码
在这里插入图片描述

#include <stdio.h> 
void print1(int arg){
printf("A1 print arg:%d\n",arg);
}

(2)创建A2.c并敲入代码
在这里插入图片描述

#include <stdio.h>
 void print2(char *arg){
printf("A2 printf arg:%s\n", arg);
}

(3)创建A.h并敲入代码
在这里插入图片描述

#ifndef A_H
 #define A_H 
 void print1(int);
void print2(char *);
#endif

(4)创建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 文件保存在同一目录下,
即都在当前目录下)
在这里插入图片描述

4,共享库.so 文件的生成与使用

(1)生成目标文件(xxx.o) (此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so文件时会出错)

在这里插入图片描述
(2)生成共享库.so 文件
在这里插入图片描述
(3)使用.so 库文件,创建可执行程序

在这里插入图片描述
运行 ldd test,查看链接情况,发现确实是找不到对应的.so 文件。
这是由于 linux 自身系统设定的相应的设置的原因,即其只在/lib and /usr/lib 下搜索对应的.so 文件,故需将对应 so 文件拷贝到对应路径。
在这里插入图片描述
(4)输入账号密码后,再次执行./test,即可成功运行。在这里插入图片描述

5,编写程序生成动态库和静态库的练习并比较大小

(1)静态库的练习并记录大小
1>、编写一个x2x函数,一个x2y函数(功能自定),main函数代码将调用x2x和x2y;将这3个函数分别写成单独的3个 .c文件。
创建main.h文件,并敲入代码:
在这里插入图片描述

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

创建sub1.c函数,并敲入代码
在这里插入图片描述

#include<stdio.h>
float x2x(int a,int b)
{
        float c=0;
        c=a+b;
        return c;
}

创建sub2.c函数,并敲入代码

在这里插入图片描述

float x2y(int a,int b)
{
        float c=0;
        c=a/b;
        return c;
}

创建main.c函数,并输入代码
在这里插入图片描述

#include<stdio.h>
#include"main.h"
void main()
{
        int a=8,b=4;
        printf("%d\n",x2x(a,b));
        printf("%d\n",x2y(a,b));
}

2>、用gcc分别编译为3个.o 目标文件

在这里插入图片描述
3>、将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件

在这里插入图片描述
4>、用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序并记录文件的大小

在这里插入图片描述
我们会看到有警告。但可以不予理会
在这里插入图片描述
查看静态库生成的文件大小

在这里插入图片描述
在这里插入图片描述
(2)动态库练习并记录大小
1>、生成动态库

在这里插入图片描述
2>、使用.so 库文件,创建可执行程序

在这里插入图片描述
会有警告,但可以不理会
再次执行./main3,即可成功运行
在这里插入图片描述
查看生成文件的大小
在这里插入图片描述
通过比较发现静态库要比动态库要小很多,生成的可执行文件大小也存在较小的差别。

三,Linux GCC常用命令

1.简介:

GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理C语言。经过了这么多年的变换,GCC 很快地扩展,变得
可处理 C++。后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective-C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection),总的一句话说就是无处不在。

2,gcc命令下各选项的含义

E:仅作预处理,不进行编译、汇编和链接
-S:仅编译到汇编语言,不进行汇编和链接
-c:编译、汇编到目标代码(也就是计算机可识别的二进制)
-o:执行命令后文件的命名
-g:生成调试信息
-w:不生成任何警告
-Wall:生成所有的警告

3,gcc编译的四个步骤

预处理:gcc -E Test.c -o Test.i
编译: gcc -S Test.i -o Test.s
汇编: gcc -c Test.s -o Test.o
链接生成可执行文件: gcc Test.o -o Test

4,gcc常用的编译代码

(1)ar
用于创建静态链接库。为了便于初学者理解,在此介绍动态库与静态库 的概念:
如果要将多个.o 目标文件生成一个库文 件,则存 在两种类型的库,一种是静态库,另一种是动态库。
在 windows 中静态库是 以 .lib 为 后缀 的文 件 ,共享库 是以 .dll 为 后缀 的 文 件 。在 linux 中静 态库是以 .a 为 后 缀 的 文 件 , 共 享 库 是 以 .so 为 后 缀 的文件。
静 态 库 和 动 态 库 的 不 同 点 在 于 代 码 被 载 入 的 时 刻 不 同 。 静 态 库 的 代 码 在 编 译 过 程 中 已 经 被 载 入 可 执 行 程 序 , 因 此 体 积 较 大 。 共 享 库 的 代 码 是 在 可 执 行 程 序 运 行 时 才 载 入 内 存 的 , 在 编 译 过 程 中 仅 简 单 的 引 用 , 因 此 代 码 体 积 较 小 。 在 Linux 系 统 中 , 可 以 用 ldd 命 令 查 看 一 个 可 执 行 程 序 依 赖 的 共 享 库。
如 果 一 个 系 统 中 存 在 多 个 需 要 同 时 运 行 的 程 序 且 这 些 程 序 之 间 存 在 共 享 库,那么采用动态库的形式将更节省内存。
(2)、ld
用于链接。
(3)、as
用于汇编。
(4)、ldd
可以用于查看一个可执行程序依赖的共享库。
(5)、size
查看执行文件中各部分的大小。
(6)、addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码 行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对 应的源代码位置。
这里只介绍一部分,其余的可以自行翻阅专门的书籍查阅。

四,as汇编编译器

(由于本机自带nasm软件,就不写安装的过程了)

1,简介:

as汇编编译器针对的是AT&T汇编代码风格,Intel风格的汇编代码则可以用nasm汇编编译器编译生成执行程序

2,生成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         ; 调用内核功能

3,使用nasm -f elf64 hello.asm,会生成一个hello.o文件

执行./hello输出
在这里插入图片描述
我们会看到有一个警告,但是这个警告不会影响我们程序的运行,可以不用管它

4,使用ld -s -o hello hello.o ,生成一个可执行文件hello

在这里插入图片描述

5,执行./hello输出

在这里插入图片描述

6,查看hello文件的大小

在这里插入图片描述

五,Linux中的第三方库函数

1、Linux 系统中终端程序最常用的光标库(curses)

(1)光标库(curses)的主要函数功能
curses函数库能够优化光标的移动并最小化需要对屏幕进行的刷新,从而也减少了必须向字符终端发送的字符数目。
(2)一些基本函数名称及功能
从屏幕读取:

chtype inch(void);  //返回光标位置字符
int instr(char *string);  //读取字符到string所指向的字符串中
int innstr(char *string, int numbers);//读取numbers个字符到string所指向的字符串中

清除屏幕:

int erase(void);//在屏幕的每个位置写上空白字符
int clear(void);//使用一个终端命令来清除整个屏幕,相当于vi内的Ctrl+L
//内部调用了clearok来执行清屏操作,(在下次调用refresh时可以重现屏幕原文)

int clrtobot(void);//清除光标位置到屏幕结尾的内容
int clrtoeol(void);//清除光标位置到该行行尾的内容

窗口移动和更新屏幕

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);   //把窗口内容上卷一行

窗口优化屏幕刷新

int wnoutrefresh(WINDOW *window_ptr);
//The wnoutrefresh subroutine determines which parts of the terminal may need updating.
	
int doupdate(void);
//The doupdate subroutine sends to the terminal the commands to perform any required changes.

2、体验即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)

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

(3)启用 “telnet client” 和"适用于Linux的Windows子系统"
在这里插入图片描述
在这里插入图片描述
(4)打开一个cmd窗口
在这里插入图片描述
(5)命令行输入 telnet bbs.newsmth.net,即可体验(有的电脑可能需要重启过后才能开启执行命令)
(6)courses的安装
在这里插入图片描述
头文件(比如curses.h)和库文件的安装目录
curses函数库的头文件和库文件就被分别安装在/usr/include/和/usr/lib/下

3,创建一个小游戏,比如贪吃蛇

找到相应的代码,创建一个新的文件,利用curses编译代码
编译的命令是gcc mysnake1.0.c -lcurses -o mysnake1.0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值