概述
一、阅读、理解和学习材料“用gcc生成静态库和动态库.pdf”请在Linux系统(Ubuntu)下如实仿做一遍。
1、首先创建一个作业目录,进入该目录。
wwyq0601@ubuntu:~$ mkdir test1
wwyq0601@ubuntu:~$ cd test1
2、创建hello.h,hello.c和main.c文件。
#include<stdio.h>
void hello(const char*name)
{ printf("Hello%s!n",name); }
#include<stdio.h>
void hello(const char*name)
{ printf("Hello%s!n",name); }
#include"hello.h"
int main()
{
hello("everyone");
return 0;
}
3、将源程序hello.c通过gcc编译成.o文件,并用ls命令检查是否生成。
wwyq0601@ubuntu:~/test1$ gcc -c hello.c
wwyq0601@ubuntu:~/test1$ ls
hello.c hello.h hello.o main.c
4、接下来创建静态库,用ar命令,创建静态库文件libmyhello.a,并用ls命令检查是否成功。
wwyq0601@ubuntu:~/test1$ ar -crv libmyhello.a hello.o
a - hello.o
wwyq0601@ubuntu:~/test1$ ls
hello.c hello.h hello.o libmyhello.a main.c
5、创建完成后进行静态库中的程序使用,结果如下。
wwyq0601@ubuntu:~/test1$ gcc -o hello main.c -L -lmyhello
wwyq0601@ubuntu:~/test1$ ./hello
Helloeveryone!
6、运行成功,再创建一个动态库文件看看,并用ls命令检查。
wwyq0601@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
wwyq0601@ubuntu:~/test1$ ls
hello hello.c hello.h hello.o libmyhello.a libmyhello.so main.c
7、成功,在程序中运行动态库,首先用gcc命令生成目标文件,
wwyq0601@ubuntu:~/test1$ gcc -o hello main.c -L -lmyhello
wwyq0601@ubuntu:~/test1$ ./hello
Helloeveryone!
二、静态库.a与.so库文件的生成与使用
并在第一次作业的程序代码基础进行改编,除了x2x函数之外,再扩展写一个x2y函数(功能自定),main函数代码将调用x2x和x2y ;将这3个函数分别写成单独的3个 .c文件,并用gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序,记录文件的大小。将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件, 然后用 gcc将 main函数的目标文件与此动态库文件进行链接,生成最终的可执行程序,记录文件的大小,并与之前做对比。
1、退出test1文件,创建新的test2文件。
wwyq0601@ubuntu:~/test1$ cd
wwyq0601@ubuntu:~$ mkdir test2
wwyq0601@ubuntu:~$ cd test2
2、用nano文本编辑器编辑生成所需文件:A1.c,A2.c,A.h,test.c,代码如下。
#include <stdio.h>
void print1(int arg){
printf("A1 print arg:%dn",arg);
}
#include <stdio.h>
void print2(char *arg){
printf("A2 printf arg:%sn", arg);
}
#ifndef A_H
#define A_H
void print1(int);
void print2(char *);
#endif
#include <stdlib.h>
#include "A.h"
int main()
{ print1(1);
print2("test"); exit(0);
}
3、静态库.a文件的生成与使用
①生成目标文件,并检查
wwyq0601@ubuntu:~/test2$ gcc -c -fPIC A1.c A2.c
wwyq0601@ubuntu:~/test2$ ls
A1.c A1.o A2.c A2.o A.h test.c
②生成静态库.a文件
wwyq0601@ubuntu:~/test2$ ar crv libafile.a A1.o A2.o
a - A1.o
a - A2.o
③使用.a库文件,创建可执行程序,结果如下。
wwyq0601@ubuntu:~/test2$ gcc -o test test.c libafile.a
wwyq0601@ubuntu:~/test2$ ./test
A1 print arg:1
A2 printf arg:test
4、共享库.so文件的生成与使用
①生成目标文件
wwyq0601@ubuntu:~/test2$ gcc -c -fPIC A1.c A2.c
wwyq0601@ubuntu:~/test2$ ls
A1.c A1.o A2.c A2.o A.h libafile.a test test.c
②生成共享库.so文件
wwyq0601@ubuntu:~/test2$ gcc -shared *.o -o libsofile.so
③使用.so库文件创建可执行程序,得出结果。
wwyq0601@ubuntu:~/test2$ gcc -o test test.c libsofile.so
wwyq0601@ubuntu:~/test2$ ./test
A1 print arg:1
A2 printf arg:test
5、编写程序生成静态库文件及可执行程序,记录文件大小。
①创建四个所需文件如下,并分别输入代码。
wwyq0601@ubuntu:~$ nano main.h
wwyq0601@ubuntu:~$ nano sub1.c
wwyq0601@ubuntu:~$ nano sub2.c
wwyq0601@ubuntu:~$ nano main.c
#ifndef MAIN_H
#define MAIN_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif
#include<stdio.h>
float x2x(int a,int b)
{
float c=0;
c=a+b;
return c;
}
float x2y(int a,int b)
{
float c=0;
c=a/b;
return c;
}
#include<stdio.h>
#include"main.h"
void main()
{
int a=8,b=4;
printf("%dn",x2x(a,b));
printf("%dn",x2y(a,b));
}
②用gcc将sub1和sub2编译为.o文件,再将x2x,x2y目标文件用ar工具生成最后与静态库进行链接,生成可执行性文件,再用-la查看文件大小。
wwyq0601@ubuntu:~$ gcc -c sub1.c sub2.c
wwyq0601@ubuntu:~$ ar crv libsub.a sub1.o sub2.o
a - sub1.o
a - sub2.o
wwyq0601@ubuntu:~$ gcc -o main main.c libsub.a
wwyq0601@ubuntu:~$ ./main
4
4
wwyq0601@ubuntu:~$ ls -la
total 160
-rw-rw-r-- 1 wwyq0601 wwyq0601 2752 Oct 15 08:54 libsub.a
drwx------ 3 wwyq0601 wwyq0601 4096 Oct 15 05:25 .local
-rwxrwxr-x 1 wwyq0601 wwyq0601 8720 Oct 15 08:55 main
-rw-rw-r-- 1 wwyq0601 wwyq0601 139 Oct 15 08:50 main.c
-rw-rw-r-- 1 wwyq0601 wwyq0601 85 Oct 15 08:47 main.h
6、编写程序生成动态库文件及可执行程序,记录文件大小。
①生成动态库,使用.so库文件创建可执行程序。
wwyq0601@ubuntu:~$ gcc -shared -fPIC -o libsub.so sub1.o sub2.o
wwyq0601@ubuntu:~$ gcc -o main3 main.c libsub.so
main.c: In function ‘main’:
②此时报错提示找不到该文件,则需我们将对应的so文件拷贝到对应路径,用sudo实现
wwyq0601@ubuntu:~$ sudo cp libsub.so /usr/lib
③成功后执行./main3即可,结果如下
wwyq0601@ubuntu:~$ ./main3
4
4
④最后查看动态库生成文件的大小
wwyq0601@ubuntu:~$ ls -la
total 180
-rwxrwxr-x 1 wwyq0601 wwyq0601 7928 Oct 15 09:07 libsub.so
-rwxrwxr-x 1 wwyq0601 wwyq0601 8720 Oct 15 08:55 main
-rwxrwxr-x 1 wwyq0601 wwyq0601 8680 Oct 15 09:07 main3
-rw-rw-r-- 1 wwyq0601 wwyq0601 139 Oct 15 08:50 main.c
-rw-rw-r-- 1 wwyq0601 wwyq0601 85 Oct 15 08:47 main.h
7、对比发现libsub.a与libsun.so的文件大小相差很大,说明静态库生成的可执行性文件比动态库生成的要小,有一定区别。
三、Gcc不是一个人在战斗。
请说明gcc编译工具集中各软件的用途,了解EFF文件格式,汇编语言格式。阅读、理解和学习材料“Linux GCC常用命令.pdf”和“GCC编译器背后的故事.pdf”,如实仿做一遍。
1、gcc命令下各选项的含义
-E:仅作预处理,不进行编译、汇编和链接
-S:仅编译到汇编语言,不进行汇编和链接
-c:编译、汇编到目标代码(也就是计算机可识别的二进制)
-o:执行命令后文件的命名
-g:生成调试信息
-w:不生成任何警告
-Wall:生成所有的警告
2、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
3、gcc常用的编译代码
①ar
用于创建静态链接库。为了便于初学者理解,在此介绍动态库与静态库 的概念:
(1)如果要将多个.o 目标文件生成一个库文 件,则存 在两种类型的库,一种是静态库,另一种是动态库。
(2)在 windows 中静态库是 以 .lib 为 后缀 的文 件 ,共享库 是以 .dll 为 后缀 的 文 件 。在 linux 中静 态库是以 .a 为 后 缀 的 文 件 , 共 享 库 是 以 .so 为 后 缀 的文件。
(3) 静 态 库 和 动 态 库 的 不 同 点 在 于 代 码 被 载 入 的 时 刻 不 同 。 静 态 库 的 代 码 在 编 译 过 程 中 已 经 被 载 入 可 执 行 程 序 , 因 此 体 积 较 大 。 共 享 库 的 代 码 是 在 可 执 行 程 序 运 行 时 才 载 入 内 存 的 , 在 编 译 过 程 中 仅 简 单 的 引 用 , 因 此 代 码 体 积 较 小 。 在 Linux 系 统 中 , 可 以 用 ldd 命 令 查 看 一 个 可 执 行 程 序 依 赖 的 共 享 库。
(4)如 果 一 个 系 统 中 存 在 多 个 需 要 同 时 运 行 的 程 序 且 这 些 程 序 之 间 存 在 共 享 库,那么采用动态库的形式将更节省内存。
②ld
用于链接。
③as
用于汇编。
④ldd
可以用于查看一个可执行程序依赖的共享库。
⑤size
查看执行文件中各部分的大小。
⑥addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码 行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对 应的源代码位置。
四、as汇编编译器针对的是AT&T汇编代码风格,Intel风格的汇编代码则可以用nasm汇编编译器编译生成执行程序。
请在ubuntu中下载安装nasm,对示例代码“hello.asm”编译生成可执行程序,并与“hello world”C代码的编译生成的程序大小进行对比。
1、下载nasm
wwyq0601@ubuntu:~$ sudo apt install nasm
2、使用nasm对hello.asm文件编译生成可执行程序,输入代码。
wwyq0601@ubuntu:~$ nano 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、生成hello文件并运行。
wwyq0601@ubuntu:~$ nano hello.asm
wwyq0601@ubuntu:~$ nasm -f elf64 hello.asm
wwyq0601@ubuntu:~$ ld -s -o hello hello.o
wwyq0601@ubuntu:~$ ./hello
Hello, world!
4、与“hello world”C代码编译生成的程序大小进行对比
wwyq0601@ubuntu:~$ size hello
text data bss dec hex filename
34 14 0 48 30 hello
wwyq0601@ubuntu:~$ nano helloworld.c
wwyq0601@ubuntu:~$ gcc helloworld.c
wwyq0601@ubuntu:~$ ./a.out
Hello,worldwwyq0601@ubuntu:~$ size a.out
text data bss dec hex filename
1184 552 8 1744 6d0 a.out
对比得nasm编译得到的文件比gcc编译的文件小。
五、了解Linux 系统中终端程序最常用的光标库(curses)的主要函数功能,写出几个基本函数名称及功能
1、curse的主要函数功能
curses函数库能够优化光标的移动并最小化需要对屏幕进行的刷新,从而也减少了必须向字符终端发送的字符数目。
2、部分常用函数名称及功能
①从屏幕读取
//返回光标位置字符
int instr(char *string); //读取字符到string所指向的字符串中
int innstr(char *string, int numbers);//读取numbers个字符到string所指向的字符串中
②移动光标
int move(int new_y, int new_x); //移动stdcsr的光标位置
int leaveok(WINDOW *window_ptr,bool leave_flag);
//设置一个标志,用于控制在屏幕刷新后curses将物理光标
③窗口优化屏幕更新
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
六、体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
①在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口
②命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS
七、在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库,请说明 头文件(比如curses.h)和库文件都被安装到哪些目录中
1、安装curses库
sudo apt-get install libncurses5-dev
2、头文件(比如curses.h)和库文件都被安装到/usr/include和/usr/lib下。
八、用gcc编译生成终端游戏
首先建立一个文件,linux环境下C语言编写小游戏贪吃蛇,,参考以上代码,编译时使用该编译命令:gcc mysnake1.0.c -lcurses -o mysnake1.0,最后运行生成的可执行文件
最后
以上就是听话百合为你收集整理的论GCC和库函数们是如何组装执行程序的的全部内容,希望文章能够帮你解决论GCC和库函数们是如何组装执行程序的所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复