GCC编译器背后的故事
一、用GCC生成.a静态库和.so动态库
1、创建一个 test2 文件夹,并在该文件夹中创建三个子程序 hello.h、hello.c 和 main.c(这里还需要下vim)

解决后,在vim中输入我们创建的三个字程序代码
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif //HELLO_H
#include <stdio.h>
#include "hello.h"
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
如下(程序main.c为例)

2、后将hello.h编译成.o文件,但是这里需要安装gcc

(安装过程同上面vim差不多,有点多这里就不放出来了)

3、由 .o文件创建静态库,并在程序中使用
a…o文件创建静态库

b. 在程序中使用静态库
方法一
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 程序(看程序运行时,是否需要该静态库)

结果:静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。
4、由 .o文件创建动态库,并在程序中使用
a. .o文件创建动态库

b. 在程序中使用动态库
方法一
gcc -o hello main.c -L. -lmyhello
方法二
gcc main.c libmyhello.so -o hello

(原因:程序在运行时, 会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行)
解决方法:将文件 libmyhello.so 移动到目录/usr/lib 中

二、静态库与动态库生成执行文件大小比较
1、创建一个 test3文件夹,并在该文件夹中分别创建子程序 sub1.h、sub1.c、sub2.h、sub2.c、main.c
mkdir test3
cd test3
vim sub1.h
vim sub1.c
vim sub2.h
vim sub2.c
vim main.c
然后在vim输入他们的程序代码,同上
#ifndef SUB1_H
#define SUB1_H
float x2x(int a, int b);
#endif //SUB1_H
#include"sub1.h"
float x2x(int a,int b)
{
return a+b;
}
#ifndef SUB2_H
#define SUB2_H
float x2y(int a, int b);
#endif //SUB2_H
#include"sub2.h"
float x2y(int a,int b)
{
return a*b;
}
#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. 在程序中使用静态库

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

b. 在程序中使用动态库

(在这之前,显示了一次错误,说找不到目录,然后再试了一遍,就可以了)
d. 两个可执行文件大小的比较
gcc -static main.c libsub1.a libsub2.a -o main1 # 重新由静态库生成
size main1
ldd main1
size main2
ldd main2

三、gcc编译器是怎么编译的
1、创建一个 test0 文件夹,并在该文件夹中创建一个 hello.c 程序
代码如下
#include<stdio.h>
int main (void)
{
printf("Hello World!\n");
return 0;
}
2、程序的编译过程
a. 预编译(将源文件 hello.c 文件预处理生成 hello.i)
b. 编译(将预处理生成的 hello.i 文件编译生成汇编程序 hello.s)
c. 汇编(将编译生成的 hello.s 文件汇编生成目标文件 hello.o)
d. 链接(分为静态链接和动态链接,生成可执行文件)
如下图

e. 用 size 查看文件大小,ldd链接了那些动态库

3、ELF 文件的分析
(1) .text:已编译程序的指令代码段
(2) .rodata:ro 代表 read only,即只读数据(譬如常数 const)
(3) .data:已初始化的 C 程序全局变量和静态局部变量
(4) .bss:未初始化的 C 程序全局变量和静态局部变量
(5) .debug:调试符号表,调试器用此段的信息帮助调试

b. 反汇编 ELF
objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来:

4、在ubuntu中下载安装nasm,对示例汇编代码“hello.asm”编译生成可执行程序,并与上述用C代码的编译生成的可执行程序大小进行对比
a. 安装nasm编译器
下载 NSAM 软件包:链接: link.
输入sudo apt install nasm

输入nasm -version

b. 编译汇编 hello.asm文件,并于C代码的编译生成的程序大小进行对比
hello.asm 内容如下:

编译
nasm -f elf64 hello.asm
链接
ld -s -o hello hello.o

c. 汇编与C代码的编译生成的可执行程序大小对比

四、了解实际程序是如何借助第三方库函数完成代码设计
(一)、以游客身份体验一下即将绝迹的远古时代的BBS
在win10下,打开控制面板——>程序——>启用或关闭Windows功能,启动"telnet client" 和"适用于Linux的Windows子系统"如图

b. 打开一个 cmd命令行窗口,输入如下命令: telnet bbs.newsmth.net

2、Linux 环境下C语言编译实现贪吃蛇游戏
a. 了解Linux 系统中终端程序最常用的光标库(curses)
initscr(): initscr() 是一般 curses 程式必须先呼叫的函数, 一但这个函数被呼叫之后, 系统将根据终端机的形态并启动 curses 模式
endwin(): curses 通常以呼叫 endwin() 来结束程式. endwin() 可用来关闭curses 模式, 或是暂时的跳离 curses 模式
refresh(): refresh() 为 curses 最常呼叫的一个函式
move(y,x): 将游标移动至 x,y 的位置
echochar(ch)/addch(ch): 显示某个字元
b. Ubuntu18.04 安装curses库
可通过 whereis 命令头文件和库文件都被安装到哪些目录中:
c. Linux 环境下C语言编译实现贪吃蛇游戏
mkdir testSnake
cd testSnake
vim mysnake.c
gcc mysnake.c -lcurse -o mysnake
./mtsnake
./mysnake 运行该程序,效果如下:

五、参考链接
链接: link.
952

被折叠的 条评论
为什么被折叠?



