Linux系统中关于gcc和库函数的粗略运用
一、gcc的可执行程序的组装
1、先创建n个.c文件
2、用gcc将这些文件编译成.o目标文件,可以用ls查看是否生成
3、用ar生成.a静态库文件
4、用gcc将主函数的目标文件与此静态库文件进行连接
5、重新生成.o文件,否则不能生成可执行文件
6、生成.so动态库文件
7、用gcc连接生成可执行文件。
二、用gcc生成静态库和动态库
用gcc生成静态库和动态库.pdf”和“静态库.a与.so库文件的生成与使用.pdf”,请在Linux系统(Ubuntu)下如实仿做一遍。
1、编辑生成程序hello.h、hello.c、main.c
1、创建一个目录test1,并保存
2、用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的 3 个文件
注:hello.c是函数的源程序,hello.h是函数的头文件。main.c是测试库文件的主程序,在主程序中调用了公用函数hello。
注:在这里,我选择的是vim编辑。
(1)用vim编辑hello.h程序
①先创建一个.h文件
②输入代码,并保存
注:保存时,要先按esc键,然后输入:wq,不要忘记有冒号。
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif//HELLO_H
(2)用vim编辑hello.c文件
①先创建一个.c文件
②输入代码,并保存
#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
(3)用vim编辑main.c文件
①创建main.c文件
②输入代码,并保存
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
2、将 hello.c 编译成.o 文件。
(1)将源程序 hello.c 通过 gcc 先编译成.o 文件。在系统提示符下键入以下命令得到 hello.o 文件。
无论静态库,还是动态库,都是由.o 文件创建的。
gcc -c hello.c
(2)在目录test1中,输入ls,检查是否形成.o文件。
3、由.o 文件创建静态库。
注:静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为.a。在创建和使用静态库时,需要注意这点。创建静态库用 ar 命令。
(1)在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
ar -crv libmyhello.a hello.o
(2)同样的方法,在目录test1下,输入ls,检查是否有文件libmyhello.a
4、在程序中使用静态库。
1、生成目标程序hello。
注:有三种方法。
第一种,直接输入gcc -o hello main.c -L. -lmyhello。
第二种,先生成main.o文件,然后生成可执行文件gcc -o hello main.o libmyhello.a。
第三种,直接输入gcc main.c libmyhello.a -o hello。
(方法1)先输入gcc -o hello main.c -L. -lmyhello。然后输出./hello,查看结果。
gcc -o hello main.c -L. -lmyhello
(方法2)
①先生成main.o文件(gcc -c main.c),然后生成可执行文件(gcc -o hello main.o libmyhello.a。)。
gcc -o hello main.o libmyhello.a
** 注:动态库连接时也可以这样做。**
② 可以删除静态库文件试试公用函数 hello 是否真的连接到目标文件 hello 中。
可以看出,程序照常运行,证明静态库中的公用函数已经连接到目标文件中了。
(方法3)直接输入gcc main.c libmyhello.a -o hello,然后输入./hello,查看结构。
gcc main.c libmyhello.a -o hello
5、由.o 文件创建动态库文件
在系统提示符下键入以下命令得到动态库文件 libmyhello.so,然后输入ls,检查是否生成动态库文件。
gcc -shared -fPIC -o limyhello.so hello.o
6、在程序中使用动态库
1、运行gcc命令生成目标文件。
gcc -o hello main.c -L. -lmyhello
2、可以看出,运行成功。
三、静态库和动态库的使用
1、创建一个目录,并编辑所需要的文件
1、创建目录test2,并保存
2、用 vim文本编辑器编辑生成所需要的四个文件 A1.c 、 A2.c、 A.h、test.c 。
(1)编辑文件A1.c
①创建A1.c文件
②输入代码,并保存
#include <stdio.h>
void print1(int arg){
printf("A1 print arg:%d\n",arg);
}
(2)编辑文件A2.c
①创建A2.c文件
②输入代码,并保存
#include <stdio.h>
void print2(char *arg){
printf("A2 printf arg:%s\n", arg);
}
(3)编辑文件A.h
①创建A.h文件
②输入代码,并保存
#ifndef A_H
#define A_H
void print1(int);
void print2(char *);
#endif
(4)编辑文件test.c
①创建test.c文件
②输入代码,并保存
#include <stdlib.h>
#include "A.h"
int main(){
print1(2);
print2("test");
exit(0);
}
2、静态库.a 文件的生成与使用
1、生成目标.o文件
2、生成静态库.a文件
3、使用.a 库文件,创建可执行程序
注:若采用此种方式,需保证生成的.a 文件与.c 文件保存在同一目录下,即都在当前目录。
gcc -o test test.c libafile.a
3、共享库.so 文件的生成与使用
1、生成.o目标文件。
注:此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so文件时会出错。
2、生成共享库.so文件
gcc -shared *.o -o libsofile.so
3、使用.so文件,创建可执行程序。
(1)出现错误,运行ldd test进行连接检查,发现确实是找不到对应的.so 文件。这是由于 linux 自身系统设定的相应的设置的原因。
(2)故需将对应 so 文件拷贝到对应路径。
注:需要输入密码。
sudo cp libsofile.so /usr/lib
注:/usr的前面需要有空格,否则会报错。
三、静态库和动态库的练习
1、静态库的练习
1、简单创建一个x2x函数,一个x2y函数,main函数代码调用x2x与x2y。
(1)创建hello.h头文件。
#ifndef HELLO_H
#define HELLO_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif
(2)创建x2x子函数,在sub1.c文件内。
#include<stdio.h>
float x2x(int a,int b)
{
float x=0;
x=a+b;
return a;
}
(3)创建x2y子函数,在sub2.c文件内。
#include<stdio.h>
float x2y(int a,int b)
{
float y=0;
y=a*b;
return y;
}
(4)创建main.c主程序。
#include<stdio.h>
void main()
{
int a,b;
a=5;
b=6;
printf("%d\n",x2x(a,b));
printf("%d\n",x2y(a,b));
}
2、将sub1.c,sub2.c编译成.o文件。
3、由.o 文件创建静态库。
键入以下命令将创建静态库文件libmysub.a。
4、在程序中使用静态库。
直接键入下列命令
2、动态库的练习
1、由.o 文件创建动态库文件。
直接键入下列命令
gcc -shared -fPIC -o libmy.so sub1.o sub2.o
2、在程序中使用动态库。
注:这里报错,因为连接时索然是当前目录的动态库,但运行时,是在/usr/lib中去找,因此,需要将.so文件复制到/usr/lib中去。
3、记录文件的大小
1、静态文件大小
2、动态文件大小。
四、Linux中gcc的常用命令
1、简介
2、编译的过程步骤
下面是给出的简单程序。
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
以下四个步骤是依次进行的。
1、预处理:将写好的.c文件进行预处理。
** gcc -E test.c -o test.i 或者 gcc -E test.c**
2、编译:将预处理生成的.i文件进行编译,生成汇编代码。
gcc -S test.i -o test.s
3、汇编:将编译生成的.s文件,会变成目标文件。
gcc -c test .s -o test.o
4、连接:将汇编生成的.o文件,与标准输入出入库进行连接,最终生成test程序。
gcc test.o -o test
3、gcc中部分命令的代表符号
-E:只进行预编译,让编译器在预处理后停止,并输出预处理结果。
-S:只编译不汇编,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。
-c:只编译不连接,生成的都是目标文件,
-Wall:开启编译器几乎所有常用的警告。
-g:表示在省城的目标文件中带调试信息。
4、gcc常用的编译
1、addr2line:用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。
2、as:主要用于汇编。
3、ld:主要用于连接。
4、ar:主要用于创建静态库。
5、ldd:用于查看一个可执行程序以来的共享库。
6、objcopy:将一种对象文件翻译成另一种格式。
7、objdump:主要的作用是反汇编。
8、readelf:显示有关 ELF 文件的信息。
9、size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等。
五、as汇编编译器
1、在ubuntu系统中下载安装nasm
输入命令 sudo apt install nasm
2、对示例代码“hello.asm”编译生成可执行程序
1、创建一个hello.asm文件
2、输入代码
; 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.o文件。
nasm -f elf64 hello,asm
4、生成可执行程序。
ld -s -o hello hello.o
5、执行程序。
3、与c代码的编译生成的程序进行对比
1、c代码的大小
2、nasm的大小
六、Linux的第三方库函数
1、最常用的光标库(courses)
1、光标库(curses)的功能
优化光标的移动并减少对屏幕进行的花心,减少了必须向字符终端发送的字符数目。
2、基本函数名称及功能
(1)initscr():打开courses模式,进入终端
WINDOW *initscr(void);
(2)endwin():关闭窗口
int endwin(void); //成功返回OK,失败返回ERR。
(3)stdscr noecho():关闭回显 让键盘输入字符,不显示在终端上。
int noecho(void);
(4)echo():显示字符。
int echo(void);
(5)清理屏幕
int erase(void); //在屏幕位置写上空白字符
int clear(void);
int clrtobot(void); //清除当前光标所在行下面的所有行,包括当前光标所在行的光标位置右边直到行尾的内容
int clrtoeol(void); //清除当前光标所在行的光标位置右边直到行尾的内容。
(6)keypad(WINDOW *, bool):指定窗口 激活功能键 上下左右 F1 F2等。
int keypad(WINDOW *window_ptr,bool keypad_on);
(7)start_color():打开color模式 。
bool has_colors(void);
int start_color(void);
int init_pair(short pair_number,short foreground,short background);
int COLOR_PAIR(int pair_number);
int pair_content(short pair_number,short *foreground,short *background);
int init_color(short color_number,short red,short green,short blue);
(8)scroll():指定窗口 接受字符 超过一行自动写入下一行。
int scroll(WINDOW *window_ptr);
(9)键盘输入
int getch(void);
int getstr(char *string)
int getnstr(char *string,int number_of_characters);
int scanw(char *format,...);
2、即将绝迹的远古时代的 BBS(一个用键盘光标控制的终端程序)
在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net。
1、按下键盘上的windows+R键,然后在运行界面输入cmd,
2、
3、按下键盘上的windows+R键,然后在运行界面输入cmd,回车
4、在出现的命令行窗口输入下列命令
telnet bbs.newsmth.net
5、就会弹出下列的终端程序。
6、输入提示的使用代号:guest,进入后体验。
3、在ubuntu中安装curses库
1、输入命令,然后安装
2、头文件(比如curses.h)和库文件都分别安装在/usr/include/和/usr/lib/中
4、体验Linux 环境下C语言编译实现游戏。
1、先创建一个目录,将代码复制粘贴到文档中
2、弹球游戏的代码来源.
3、形成.o文件之后,输入./tanqiu.o命令,弹出下列窗口,输入n。
4、然后开始体验游戏。
七、gcc操作的tips。
1、问:静态库制作完了,如何使用它内部的函数呢?
答:只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明静态库名,gcc 将会从静态库中将公用函数连接到目标文件中。注意,gcc 会在静态库名前加上前缀 lib,然后追加扩展名.a 得到的静态库文件名来查找静态库文件。
2、动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其文件扩展名为.so
3、连接静态库时,gcc 会在静态库名前加上前缀 lib,然后追加扩展名.a 得到的静态库文件名来查找静态库文件。
4、自定义的库时,main.c 还可放在-L.和 –lmyhello 之间,但是不能放在它俩之后,否则会提示 myhello 没定义,但是是系统的库时,如 g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。
八、总结
这次实验总的来说,过程比较简单,但是需要深入的理解。
九、参考
1、course库函数的理解
https://blog.csdn.net/qq_43279579/article/details/109064812.
2、courses的部分函数代码
https://blog.csdn.net/jlfzhz/article/details/5836132.