2020-10-16

编译器背后的故事

1,创建一个 test1 文件夹,并在该文件夹中创建三个子程序 hello.h、hello.c 和 main.c

mkdir test1	# 创建test1文件夹
cd test1 # 进入该文件
vim hello.h	# 编辑hello.h
vim hello.c	# 编辑hello.c
vim main.c	# 编辑main.c

在这里插入图片描述
程序 hello.h 内容如下:

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

程序 hello.c 内容如下:

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

程序 main.c 内容如下:

#include "hello.h" 
int main() 
{
	hello("everyone"); 
	return 0; 
}

2,将 hello.c 编译成 .o文件

,
在这里插入图片描述
3,由 .o文件创建静态库,并在程序中使用

1.o文件创建静态库
在这里插入图片描述
2. 在程序中使用静态库

gcc -o hello main.c -L. -lmyhello
gcc main.c libmyhello.a -o hello
gcc -o main.c	# 先生成 main.o
gcc -o hello main.o libmyhello.a

./hello,执行程序:
在这里插入图片描述
删除 libmyhello静态库,再次执行 hello 程序

rm libmyhello.a	# 删除libmyhello.a
./hello	# 运行hello程序

在这里插入图片描述
4 .o文件创建动态库,并在程序中使用

a. .o文件创建动态库

动态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为 .so。如:libmyhello.so

gcc -shared -fPIC -o libmyhello.so hello.o	# 生成动态库
ls	# 查看

在这里插入图片描述
b. 在程序中使用动态库

1gcc -o hello main.c -L. -lmyhello
2gcc main.c libmyhello.so -o hello

但运行 hello程序时将会报错(在/usr/lib 中找不到该库文件)
再次生成以下 libmyhello.a 静态库
仍然报错
在这里插入图片描述
将文件 libmyhello.so 移动到目录/usr/lib 中可以解决这问题
在这里插入图片描述

二、动态库和静态库生成可执行文件大小的对比

创建一个 test2文件夹,并在该文件夹中分别创建子程序 sub1.h、sub1.c、sub2.h、sub2.c、main.c

mkdir test2
cd test2
vim sub1.h
vim sub1.c
vim sub2.h
vim sub2.c
vim main.c

分别建立成:
1,

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

2,

#include"sub1.h"

float x2x(int a, int b){
	return a + b;	//相加

}
3,

#ifndef SUB2_H
#define SUB2_H 
float x2y(int a, int b);
#endif //SUB2_H
4#include"sub2.h"

float x2y(int a, int b){
    return a * b;	//相乘

}
5,

#include<stdio.h>
#include"sub1.h"
#include"sub2.h"

int main(){
	int a = 2, b = 3;
	printf("%d + %d = %f\n", a, b, x2x(a, b));
	printf("%d × %d = %f\n", a, b, x2y(a, b));
	return 0;
}

2

用静态库文件进行链接,生成可执行文件
a. 将 sub1.c、sub2.c 编译成 .o文件
在这里插入图片描述
b. .o文件创建静态库在这里插入图片描述
c. 在程序中使用静态库

``gcc main.c libsub1.a libsub2.a -o main1
./main1

在这里插入图片描述

3,

用动态库文件进行链接,生成可执行文件
a. .o文件创建动态库

gcc -shared -fPIC -o libsub1.so sub1.o
gcc -shared -fPIC -o libsub2.so sub2.o
ls

在这里插入图片描述
b. 在程序中使用动态库

gcc main.c libsub1.so libsub2.so -o main2
#将文件 libsub1.so、libsub2.so 移动到目录/usr/lib 中
sudo mv libsub1.so /usr/lib
sudo mv libsub2.so /usr/lib
./main2

在这里插入图片描述
d. 两个可执行文件大小的比较

gcc -static main.c libsub1.a libsub2.a -o main1	# 重新由静态库生成
size main1
ldd main1
size main2
ldd main2

在这里插入图片描述

三、gcc编译器是怎么编译的

在这里插入图片描述
a. 预编译(将源文件 hello.c 文件预处理生成 hello.i)

gcc -E hello.c -o hello.i

1
b. 编译(将预处理生成的 hello.i 文件编译生成汇编程序 hello.s)

gcc -S hello.i -o hello.s

1
c. 汇编(将编译生成的 hello.s 文件汇编生成目标文件 hello.o)

gcc -c hello.s -o hello.o

as -c hello.s -o hello.o

d. 链接(分为静态链接和动态链接,生成可执行文件)

gcc hello.c -o hello

gcc -static hello.c -o hello

在这里插入图片描述
e. 用 size 查看文件大小,ldd链接了那些动态库
在这里插入图片描述

3,ELF 文件的分析

a. 一个典型的 ELF 文件包含下面几个段

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

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

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

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

(5) .debug:调试符号表,调试器用此段的信息帮助调试

readelf -S hello

在这里插入图片描述
b. 反汇编 ELF
objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来:
在这里插入图片描述
在ubuntu中下载安装nasm,对示例汇编代码“hello.asm”编译生成可执行程序,并与上述用C代码的编译生成的可执行程序大小进行对比
命令直接下载NASMsudo apt-get install nasm
查看是否安装成功:
在这里插入图片描述
编译汇编 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         ; 调用内核功能

在这里插入图片描述
c. 汇编与C代码的编译生成的可执行程序大小对比在这里插入图片描述
四、Linux 环境下C语言编译实现贪吃蛇游戏
a. 了解Linux 系统中终端程序最常用的光标库(curses)

1,initscr(): initscr() 是一般 curses 程式必须先呼叫的函数, 一但这个函数被呼叫之后, 系统将根据终端机的形态并启动 curses 模式
2,endwin(): curses 通常以呼叫 endwin() 来结束程式. endwin() 可用来关闭curses 模式, 或是暂时的跳离 curses 模式
3,refresh(): refresh() 为 curses 最常呼叫的一个函式
4,move(y,x): 将游标移动至 x,y 的位置
5,echochar(ch)/addch(ch): 显示某个字元

Ubuntu18.04 安装curses库

sudo apt-get install libncurses5-dev

查看 curses库安装位置
在这里插入图片描述
贪吃蛇代码链接进入www.linuxidc.com/Linux/2011-08/41375.htm
在这里插入图片描述
./mysnake运行。
在这里插入图片描述
总结:让我这个纯小白,更加了解了,Ubuntu。同时了解了如何用 gcc 生成静态库和动态库。
参考博主:在下摸鱼怪。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个 SQL 语句,用于向借阅表中插入数据。该表包含以下字段:借阅编号、读者编号、书籍编号、借阅日期、归还日期、借阅状态。每条数据表示一次借阅记录。其中借阅编号、读者编号、书籍编号、借阅日期和借阅状态是必填项,归还日期为可选项,如果借阅状态为“已还”则必须填写归还日期。 具体插入的数据如下: - 借阅编号:100001,读者编号:123413,书籍编号:0001,借阅日期:2020-11-05,归还日期:NULL,借阅状态:借阅 - 借阅编号:100002,读者编号:223411,书籍编号:0002,借阅日期:2020-9-28,归还日期:2020-10-13,借阅状态:已还 - 借阅编号:100003,读者编号:321123,书籍编号:1001,借阅日期:2020-7-01,归还日期:NULL,借阅状态:过期 - 借阅编号:100004,读者编号:321124,书籍编号:2001,借阅日期:2020-10-09,归还日期:2020-10-14,借阅状态:已还 - 借阅编号:100005,读者编号:321124,书籍编号:0001,借阅日期:2020-10-15,归还日期:NULL,借阅状态:借阅 - 借阅编号:100006,读者编号:223411,书籍编号:2001,借阅日期:2020-10-16,归还日期:NULL,借阅状态:借阅 - 借阅编号:100007,读者编号:411111,书籍编号:1002,借阅日期:2020-9-01,归还日期:2020-9-24,借阅状态:已还 - 借阅编号:100008,读者编号:411111,书籍编号:0001,借阅日期:2020-9-25,归还日期:NULL,借阅状态:借阅 - 借阅编号:100009,读者编号:411111,书籍编号:1001,借阅日期:2020-10-08,归还日期:NULL,借阅状态:借阅
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值