gcc编译器背后的故事

一、用gcc生成.a静态库和.so动态库

1.创建一个a文件夹,然后在a文件夹中创建三个子程序a.h、a.c以及main.c
代码如下:

mkdir a
cd a
vim a.h
vim a.c
vim main.c

实例显示:
在这里插入图片描述
子程序a.h内容:

#ifndef A_H
#define A_H
void a(const char *name)
#endif

子程序a.c内容:

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

子程序main.c内容:

#include"a.h"
int main()
{
a("World");
return 0;
}

2.将a.c编译成为a.o文件

gcc -c a.c
ls

结果如下:
在这里插入图片描述可以看到已经有a.o文件了
3.由a.o文件创建静态库,并使用
(1)创建静态库

ar -crv libmya.a a.o
ls

结果如下:
在这里插入图片描述
(2)在程序中使用静态库

gcc main.c libmya.a -o a

然后运行程序

./a

结果如下:
在这里插入图片描述
4.由a.o文件创建动态库,并在程序中使用
(1)a.o文件创建动态库

gcc -shared -fPIC -o libmya.so a.o
ls

结果如下:
在这里插入图片描述
(2)在程序中使用动态库

gcc main.c libmya.so -o a

结果如下:
在这里插入图片描述
运行时报错,因为程序在运行时会在/usr/lib和/lib等目录中找不到需要的动态库文件。

解决方法:将libmya.so文件移动至目录/usr/lib中就行了。
程序如下:

sudo mv libmya.so /usr/lib

然后运行结果如下:
在这里插入图片描述

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

1.创建另外一个文件夹b,然后在文件夹b中分别创建子程序sub1.h、sub1.c、sub2.h、sub2.c、main.c

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

sub1.h内容如下:

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

sub1.c内容如下:

#include"sub1.h"
float x2x(int a,int b)
{
return a+b;
}

sub2.h内容如下:

#ifndef SUB2_H
#difine SUB2_H
float x2y(int a,int b);
#endif

sub2.c内容如下:

#include"sub2.h"
float x2y(int a,int b)
{
return a*b;
}

main.c内容如下:

#include<stdio.h>
#include"sub1.h"
#include"sub2.h"
int main()
{
int a=4,b=5;
printf("%d + %d =%f\n",a,b,x2x(a,b));
printf("%d * %d =%f\n",a,b,x2y(a,b));
}

2.用静态库文件进行链接,生成可执行文件
(1)将sub1.c、sub2.c文件分别编译成sub1.o、sub2.o文件

gcc -c sub1.c sub2.c
ls 

结果如下:
在这里插入图片描述(2)创建静态库

ar -crv libsub1.a sub1.o
ar -crv libsub2.a sub2.o
ls

在这里插入图片描述
(3)在程序中使用静态库

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

结果如下:
在这里插入图片描述
3.用动态库文件进行链接,生成可执行文件
(1)创建动态库

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

结果如下:
在这里插入图片描述
(2)使用动态库

gcc main.c libsub1.so libsub2.so -o main2
sudo mv libsub1.so /usr/lib
sudo mv libsub2.so /usr/lib
./main2

结果如下:
在这里插入图片描述
4.两个可执行文件大小的比较

gcc -static main.c libsub1.a libsub2.a -o main1	
size main1
ldd main1
size main2
ldd main2

结果如下:
在这里插入图片描述

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

1.创建一个test文件夹,然后在test文件夹中创建一个a.c程序

mkdir test
cd test
vim a.c

a.c内容如下:

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

2.编译过程
(1)预编译(将源文件 a.c 文件预处理生成 a.i)

gcc -E a.c -o a.i

(2)编译(将预处理生成的 a.i 文件编译生成汇编程序 a.s)

gcc -S a.i -o a.s

(3)汇编(将编译生成的 a.s 文件汇编生成目标文件 a.o)

gcc -c a.s -o a.o

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

gcc a.c -o a //动态链接
gcc -static a.c -o a //静态链接

结果如下:
在这里插入图片描述
3.ELF文件的分析
(一)一个典型的 ELF 文件包含下面几个段
(1) .text:已编译程序的指令代码段
(2) .rodata:ro 代表 read only,即只读数据(譬如常数 const)
(3) .data:已初始化的 C 程序全局变量和静态局部变量
(4) .bss:未初始化的 C 程序全局变量和静态局部变量
(5) .debug:调试符号表,调试器用此段的信息帮助调试

readelf -S a //查看各个section的信息

如下:
在这里插入图片描述
(二)反汇编ELF
objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来:

gcc -o a -g a.c
objdump -S a

显示如下:
在这里插入图片描述
在这里插入图片描述
4.在Ubuntu中下载安装nasm,对示例汇编代码“hello.asm”编译生成可执行程序,并与上述用C代码的编译生成的可执行程序大小进行对比
4.1安装nams
输入nasm

nasm

显示如下:
在这里插入图片描述说明Ubuntu中没有安装nasm

在这里插入图片描述说明Ubuntu中已经安装了nasm

如果没有安装nasm,输入代码sudo apt install nasm

sudo apt install nasm

结果如下:
在这里插入图片描述
4.2编译汇编 hello.asm文件
(1)创建hello.asm文件

vim hello.asm

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
ld -s -o hello hello.o
./hello

结果如下:
在这里插入图片描述
4.3汇编与C代码的编译生成的可执行程序大小对比
汇编生成的可执行程序大小
在这里插入图片描述
c代码生成的可执行程序大小

四、了解实际程序是如何借助第三方库函数完成代码设计

1.光标库(curses)的常见函数功能

nitscr()	 	初始化curses库和ttty

endwin()	        关闭curses并重置tty

move(y,x)		将光标移动至x,y的位置

getyx(win,y,x)	得到目标游标的位置

clear()and erase()	将整个屏幕清楚

2.体验一下即将绝迹的远古时代的 BBS
在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和 “适用于Linux的Windows子系统”(后面会使用),然后重启。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.Linux 环境下C语言编译实现贪吃蛇游戏
(1)安装curses库

sudo apt-get install libncurses5-dev

在这里插入图片描述

通过 whereis 命令头文件和库文件都被安装到哪些目录中

whereis curses.h

在这里插入图片描述
(2)编译实现贪吃蛇游戏
代码参考
Linux 环境下C语言编译实现贪吃蛇游戏
创建Snake文件,然后在Snake文件中创建snake.c程序

mkdir Snake
cd Snake
vim snake.c

在这里插入图片描述

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值