Linux系统编程-文件IO、标准库IO、刷新、缓冲模式
一、标准I/O
- I/O就是输入和输出,输入输出是在开发中非常常用的操作,通过输入和输出,用户和程序能够进行交互。比如用户通过键盘输入一些数据,输入的数据被程序处理完,然后将结果显示在输出设备(显示器,终端等)。
- 标准I/O由ANSIC标准定义,C标准中定义了C库,标准I/O就是C库中用来输入和输出的函数。标准I/O在系统调用的上一层多加了一个缓冲区,通过缓冲机制减少了系统调用,实现更高的效率。
1.1、文件类型
在linux系统下,文件类型可以被分为
文件类型 | 符号 |
---|---|
常规文件 | r |
普通文件 | - |
目录文件 | d |
字符设备文件 | c |
块设备文件 | b |
管道文件 | p |
套接字文件 | s |
符号链接文件 | | |
1.2、标准IO-流
1.2.1、FILE
- 标准IO用一个结构体类型来存放打开的文件的相关信息
- 标准I/O的所有操作都是围绕FILE来进行
1.2.2、流(stream)
- FILE又被称为流(stream)
- 分为文本流和二进制流
1.2.3、流的缓冲形式
- 全缓冲:缓冲区中无数据或无空间或者关闭流时才执行实际的IO操作(默认打开的文件都属于此类,最大缓冲1024个字节)
- 行缓冲:输入输出中遇到换行符时,进行IO操作(流与终端关联时就是典型的行缓冲)
- 无缓冲:不进行缓冲,数据直接读写。
- 当我们打开一个FILE流指针时,标准IO会预定义并且打开3个流:标准输入流,标准输出流,标准错误流。
1.2.4、标准I/O –stdin,stdout,stderr
标准输入流 | 0 | STDIN_FILENO | stdin |
标准输出流 | 1 | STDOUT_FILENO | stdout |
标准错误流 | 2 | STDERR_FILENO | stderr |
1.3、流的打开和关闭、错误信息
1.3.1、打开与关闭
FILE *fopen (const char *path, const char *mode);
成功时返回流指针;出错时返回NULL
path :要打开的文件路径及文件名
mode :文件打开方式
r或者rb | 只读,文件必须存在 |
r+或者r+b | 可读写,文件必须存在 |
w或者wb | 只写(擦写),文件不存在则创建 |
w+或者w+b | 可读写(擦写),文件不存在则创建 |
a或者ab | 只写(追加写),文件不存在则创建 |
a+或者a+b | 可读写(追加写),文件不存在则创建 |
注意:每个选项加入b字符,是用来告诉函数库打开的是二进制文件,而非纯文本文件,不过在Linux |
标准I/O – fopen – 新建文件权限
fopen() 创建的文件访问权限是0666(rw-rw-rw-)
Linux系统中umask设定会影响文件的访问权限,其规则为(0666 & ~umask)
Root用户是 022 普通用户是002
用户可以通过umask函数或者命令修改相关设定
int fclose(FILE *stream);
- fclose()调用成功返回0,失败返回EOF,并设置errno
- 流关闭时自动刷新缓冲中的数据并释放缓冲区
- 当一个程序正常终止时,所有打开的流都会被关闭。
- 流一旦关闭后就不能执行任何操作
- 当调用该函数时,*stream必须是存在的文件,否则会出现段错误
FILE* fp;
if((fp = fopen("filepath", "打开方式"))==NULL);//打开失败会返回NULL,因此要进行判断
{
perror("fopen");//由perror对打开文件失败进行说明
return -1;
}
//文件操作
fclose(fp);//当流关闭时,会自动刷新缓冲区中的数据,并且释放缓冲区
1.3.2、错误信息
extern int errno;//存放错误编号
char* strerror(int errno);//根据错误编号返回对应的错误信息
void perror(const char*s);//先输出字符串,再输出错误号对应的信息
//错误信息处理1
FILE* fp
if((fp = fopen("filepath", "打开方式"))==NULL);//打开失败会返回NULL,因此要进行判断
{
perror("fopen");//由perror对打开文件失败进行说明
return -1;
}
//文件操作
fclose(fp);
//错误信息处理2
FILE* fp
if((fp = fopen("filepath", "打开方式"))==NULL);//打开失败会返回NULL,因此要进行判断
{
printf("fopen:%s\n", strerror(errno));
return -1;
}
//文件操作
fclose(fp);
1.4、按字符输入输出
按字符输入函数:
函数原型:int getc(FILE *stream);
int fgetc(FILE *stream);
int getchar( );
stream:要输入的文件流
函数返回值:成功:读取的字符;失败:EOF
成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1),
getchar()等同于fgetc(stdin)
getc和fgetc区别是一个是宏一个是函数
注意事项:
- 函数返回值是int类型不是char类型,主要是为了扩展返回值的范围。
- stdin 也是FILE*的指针,是系统定义好的,指向的是标准输入(键盘输入)
- 打开文件后读取,是从文件开头开始读。读完一个后读写指针会后移。读写注意文件位置!
- 调用getchar会阻塞,等待你的键盘输入
#include <stdio.h>
int main (int agrc, char * agrv[]) {
FILE *fp;
fp = fopen("1.txt","r+");
int rec;
if (fp == NULL) {
perror("fopen: ");
return 0;
}
rec = fgetc(fp);
printf("Get char : %c\n", rec);
return 0;
}
输出:w
函数原型:int putc(int c , FILE *sream);
int fputc(int c , FILE *sream);
int putchar(int c);
函数返回值:成功:输出的字符c;失败:EOF
成功时返回写入的字符;出错时返回EOF
putchar©等同于fputc(c, stdout)
注意事项:
- 返回和输入参数都是int类型
- 遇到这种错误:Bad file descriptor, 很可能是文件打开的模式错误(只读模式去写,只写模式去读)
#include <stdio.h>
int main (int agrc, char * agrv[]) {
FILE *fp;
fp = fopen("1.txt","r+");
int rec;
if (fp == NULL) {
perror("fopen: ");
return 0;
}
int wrc = 'w';
rec = fputc(wrc,fp);
if (rec == -1) {
perror("fputc:");
fclose(fp);
return 0;
}
putchar(wrc);
return 0;
}
1.5、按行输入输出
函数原型:char *gets(char *s);
char *fgets(char *s , int size , FILE *stream);
s :存放输入字符串的缓冲区首地址
size :输入的字符串长度
stream :对应的流
函数返回值:成功:s;失败或到达文件末尾:NULL
成功时返回s,到文件末尾或出错时返回NULL
遇到’\n’或已输入size-1个字符时返回,总是包含’\0’
注意事项:
- gets函数已经被淘汰,因为会导致缓冲区溢出
- fgets 函数第二个参数,输入的数据超出size,size-1个字符会保存到缓冲区,最后添加’\0’,如果输入数据少于size-1,后面会添加换行符。
#include <stdio.h>
int main (int agec, char * agev[]) {
FILE *fp;
char buff[100];
char *ret;
fp = fopen("1.txt","a+");
if (fp == NULL) {
perror("fopen:");
return 0;
}
ret = fgets(buff, 100, fp);
if (ret == NULL) {
perror("fgets");
fclose(fp);
return 0;
}
printf("buff = %s\n",ret);
return 0;
}
函数原型:int puts(const char *s);
int fputs(const char *s , FILE *stream);
s :存放输出字符串的缓冲区首地址
stream :对应的流
函数返回值:成功:非负数;失败:EOF
成功时返回非负整数;出错时返回EOF
注意事项:
- puts将缓冲区s中的字符串输出到stdout,并追加’\n’
- fputs将缓冲区s中的字符串输出到stream,不追加 ‘\n’
#include <stdio.h>
int main (int agec, char * agev[]) {
FILE *fp;
char buff[100];
int retn;
fp = fopen("1.txt","a+");
if (fp == NULL) {
perror("fopen:");
return 0;
}
retn = fputs("HEllO WORLD!!!!", fp);
if (retn == -1) {
perror("fputs:");
return 0;
}
printf("Hello world!\n");
fclose(fp);
return 0;
}
1.6、按对象读写
函数原型:int fread(void *buf , size_t size , int nmemb , FILE *stream);
buf : 存放读取的缓冲区
size :读取的每个记录的大小
nmemb :读取的记录数
stream :要读取的文件流
函数返回值:成功:实际读取的数目;失败:EOF
fwrite()函数:
所需头文件:#include<stdio.h>
函数原型:int fwrite(void *buf , size_t size , int nmemb , FILE *stream);
buf : 存放写入的缓冲区
size :写入的每个记录的大小
nmemb :写入的记录数
stream :要写入的文件流
函数返回值:成功:实际写入的数目;失败:EOF
注意事项:
- 文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。
- 解决办法:移动指针,流的定位或关闭文件,重新打开
#include <stdio.h>
#include <stdlib.h>
struct student {
char name[16];
int age;
char sex[8];
};
int main (int agrc, char * agrv[]) {
FILE *fp;
size_t ret;
struct student stu[2] = {
"zhangsan", 61, "male",
"zhangcuihua", 49, "woman"
};
struct student stu2[2];
fp = fopen("student.bin", "w");//打开文件
if (fp == NULL) {
perror("fopen");
return 0;
}
ret = fwrite(&stu, sizeof(stu), 1, fp);//读入文件
if (ret == -1) {
perror("fwrite");
goto end;
} else {
printf("fwrite struct student success\n");
}
fclose(fp);//关闭文件
fp = fopen("student.bin", "r");//重新打开文件
if (fp == NULL) {
perror("fopen");
goto end;
}
ret = fread(&stu2, sizeof(stu2), 1, fp);//读出文件
if (ret == -1) {
perror("fread");
goto end;
}
printf("name = %s, age = %d, sex = %s\n", stu2[0].name, stu2[0].age, stu2[0].sex);
printf("name = %s, age = %d, sex = %s\n", stu2[1].name, stu2[1].age, stu2[1].sex);
end:
fclose(fp);
return 0;
}
1.7、流的刷新和定位
流的刷新
当缓冲区满时,或者遇到换行符时,流的缓冲区会刷新->写到实际的文件中
当关闭流时,缓冲区会刷新->写到实际的文件中
通过fflush函数,强制刷新缓冲区
成功时返回0;出错时返回EOF
Linux下只能刷新输出缓冲区,输入缓冲区丢弃
int fflush(FILE *fp);
流的定位
当一个流打开的时候,内部有一个读写位置pos,我们在对流进行定位时,实际上就是在定位这个pos
*打开流时,pos=0
*每当读写一个位置,pos会自动加1
fseek 参数whence参数:SEEK_SET/SEEK_CUR/SEEK_END
SEEK_SET 从距文件开头 offset 位移量为新的读写位置
SEEK_CUR:以目前的读写位置往后增加 offset 个位移量
SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量
offset参数:偏移量,可正可负
注意事项:
- 文件的打开使用a模式 fseek无效
- rewind(fp) 相当于 fseek(fp,0,SEEK_SET);
- 这三个函数只适用2G以下的文件
long ftell(FILE *stream);
long fseek(FILE *stream, long offset, int whence);
void rewind(FILE *stream);
#include <stdio.h>
int main (int argv, char *argc[]) {
FILE *fp;
fp = fopen("1.txt", "w");
if (fp == NULL) {
perror("fopen");
return 0;
}
char ch1[] = "abcdefgh";
char ch2[] = "!!!";
fwrite(&ch1, sizeof(ch1)-1 , 1, fp);
// printf("current fp = %d\n", (int)ftell(fp)); //输出: 6
// rewind(fp);
// printf("After rewind fp = %d\n", (int)ftell(fp));// 输出:0
fseek(fp, 2, SEEK_SET);//输出: ab!!!fgh
// fseek(fp, -2, SEEK_CUR);// 输出: abcdef!!!
fseek(fp, 2, SEEK_END);// 输出:abcdefgh !!!
fwrite(&ch2, sizeof(ch2)-1 , 1, fp);
fclose(fp);
return 0;
}
1.8、格式化输入输出
1.8.1、 格式化输出(fprintf、sprintf)
int fprintf(FILE *stream, const char *fmt, …); 将内容输出到文件流上面
int sprintf(char *s, const char *fmt, …); 将内容输出到字符串上面在这里插入代码片
成功时返回输出的字符个数;出错时返回EOF
#include <stdio.h>
int main(int argv, char *argc[]) {
FILE *fp;
fp = fopen("ftext.txt", "w");
if (fp == NULL) {
perror("fopen");
return 0;
}
int year = 2000;
int month = 10;
int day = 19;
fprintf(fp, "%d-%d-%d", year, month, day);
fclose(fp);
return 0;
}
#include <stdio.h>
int main(int argv, char *argc[]) {
char buf[100] = {0};
int year = 2000;
int month = 10;
int day = 19;
sprintf(buf, "%d-%d-%d", year, month, day);
printf("%s\n",buf);
return 0;
}
1.8.2、 格式化输入(fscanf、sscanf)
int fscanf(FILE *stream, const char *format, …); 从文件流里面取出指定的变量
int sscanf(const char *str, const char *format, …); 从字符串里面取出指定的变量
#include <stdio.h>
int main (int argv, char *argc[]) {
int year = 2000;
int month = 10;
int day = 19;
FILE *fp;
fp = fopen("ftext.txt", "w");
if (fp == NULL) {
perror("fopen");
return 0;
}
fscanf(fp, "%d-%d-%d", &year, &month, &day);
printf("%d-%d-%d\n",year, month, day);
fclose(fp);
return 0;
}
#include <stdio.h>
int main (int argv, char *argc[]) {
char buf[100] = {0};
int year = 2000;
int month = 10;
int day = 19;
sprintf(buf, "%d-%d-%d", year, month, day);
printf("%s\n",buf);
int syear;
int smonth;
int sday;
sscanf(buf, "%d-%d-%d", &syear, &smonth, &sday);
printf("%d-%d-%d\n",syear, smonth, sday);
return 0;
}
二、文件I/O
2.1、标准IO与文件IO的区别
1、文件IO又称为低级磁盘IO,遵循POSIX相关标准,任何支持POSIX标准的操作系统上,都支持文件IO。明显的特点是头文件#include<stdio.h>
2、标准IO又称为高级磁盘IO,遵循ANSIC相关标准,只要开发环境中有标准C库,都支持标准IO。头文件为#include<unistd.h>
- Linux中使用的是glibc,它是标准C库的超集,不仅包含POSIX标准定义的函数,还包含ANSIC中定义的函数,所以在Linux下,既可以使用标准IO,也可以使用文件IO。
- 文件IO并没有缓冲机制,并且是通过一个文件描述符来表示一个打开的文件。可以访问linux中所有类型的文件。
- 通过文件IO读写文件时,每次操作都会执行相关系统调用。这样处理的好处是可以直接读写实际文件,坏处是频繁地系统调用会增加系统开销;而标准IO可以看做是在文件IO的基础上封装了缓冲机制,先读写缓冲区,必要时再访问实际文件,从而减少系统调用次数。
- 文件IO中用文件描述符表示一个打开的文件,可以访问不同类型的文件(如普通文件、设备文件和管道文件)。而标准IO中用FILE(流)表示一个打开的文件,通常只用来访问普通文件。
2.2、文件目录打开,读取,关闭
2.2.1、目录打开
#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd); 使用文件描述符,要配合open函数使用
DIR是用来描述一个打开的目录文件的结构体类型
成功时返回目录流指针;出错时返回NULL
2.2.2、读取目录
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
struct dirent是用来描述目录流中一个目录项的结构体类型
包含成员char d_name[256] 参考帮助文档
成功时返回目录流dirp中下一个目录项;
出错或到末尾时时返回NULL
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported by all filesystem types */
char d_name[256]; /* filename */
};
#include <dirent.h>
#include <stdio.h>
int main(int argc,char **argv){
DIR* dp;
struct dirent *dt;
dp=opendir("/mnt/hgfs/share/newIOP/");
if(dp == NULL){
perror("opendir");
return 0;
}
while((dt=readdir(dp))!=NULL){
printf("%s\n",dt->d_name);
}
closedir(dp);
}
2.2.3、关闭目录
#include <dirent.h>
int closedir(DIR *dirp);
成功时返回0;出错时返回EOF
2.3、文件IO(概念、打开、读、写、关闭)
什么是文件IO,又称系统IO,系统调用,是操作系统提供的API接口函数。
POSIX接口 (了解)
注意:文件IO不提供缓冲机制
2.3.1、文件I/O – open 打开
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
成功时返回文件描述符;出错时返回EOF
打开文件时使用两个参数
创建文件时第三个参数指定新文件的权限,(只有在建立新文件时有效)此外真正建文件时的权限会受到umask 值影响,实际权限是mode-umaks
可以打开设备文件,但是不能创建设备文件(创建设备mknode 驱动部分会讲)
pathname是要打开或者创建的文件名。
flags 文件打开时候的选项,
O_RDONLY | 以只读方式打开文件 |
O_WRONLY | 以只写方式打开文件 |
O_RDWR | 以读、写方式打开文件 |
O_CREAT | 如果该文件不存在,就创建一个新文件,并用第三个参数为其设置权限 |
O_EXCL | 如果使用O_CREAT时文件存在,则可返回错误信息,这一参数可测试文件是否存在 |
O_NOCTTY | 使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端 |
O_TRUNC | 如文件已经存在,那么打开文件时,先删除原有文件中数据 |
O_APPEND | 已添加方式打开文件,所以对文件的写操作,都是在文件末尾进行 |
mode 被打开文件的存取权限,为8进制表示法。
umask概念:
umask :用来设定文件或目录的初始权限
文件和目录的真正初始权限
文件或目录的初始权限 = 文件或目录的最大默认权限 - umask权限
例子
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main (int argv, char *argc[]) {
int fd;
fd = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if(fd < 0){
perror("open");
return 0;
}
}
2.3.2、文件I/O – read 读取
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
成功时返回实际读取的字节数;出错时返回EOF
读到文件末尾时返回0
buf是接收数据的缓冲区
count不应超过buf大小
2.3.3、文件I/O – write写入
#include <unistd.h>
ssize_t write(int fd, void *buf, size_t count);
成功时返回实际写入的字节数;出错时返回EOF
buf是发送数据的缓冲区
count不应超过buf大小
2.3.4、文件I/O – lseek定位
#include <unistd.h>
off_t lseek(int fd, off_t offset, intt whence);
成功时返回当前的文件读写位置;出错时返回EOF
参数offset和参数whence同fseek完全一样
SEEK_SET 文件的起始位置
SEEK_CUR 文件当前读写位置
SEEK_END 文件结束位置
2.3.5、文件I/O – close关闭
#include <unistd.h>
int close(int fd);
成功时返回0;出错时返回EOF
程序结束时自动关闭所有打开的文件
文件关闭后,文件描述符不再代表文件
2.3.6、例子
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
int fd;
int ret;
char buf[32] = "hello world";
char buf2[32] = {0};
fd = open("1.txt", O_RDWR | O_CREAT | O_APPEND, 0666);//打开文件
if(fd < 0) {
perror("open");
return 0;
}
ret = write(fd, buf, strlen(buf)); //将buf写入fd文件当中
if (ret < 0) {
perror("write");
goto END;
}
lseek(fd, 0, SEEK_SET);//文件流定位到开始
ret = read(fd, buf2, 32);//读取文件到buf2当中
if (ret < 0) {
perror("read");
goto END;
}
printf("read buf2 = %s\n", buf2);
END:
close(fd);
return 0;
}
三、修改文件属性
3.1、修改文件权限chmod粘滞位
chmod/fchmod函数用来修改文件的访问权限:
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
#include <stdio.h>
#include <sys/stat.h>
int main(int argc,char **argv){
int ret;
ret = chmod("temp",0444); //将temp文件修改成0444八进制对应的 r--r--r--
if(ret<0){
perror("chmod");
return 0;
}
}
3.2、获取文件属性
获取文件属性(stat)
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
stat结构体
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
> mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
文件类型—— st_mode
通过系统提供的宏来判断文件类型:
S_IFMT 0170000 文件类型的位遮罩
S_ISREG(st_mode) 0100000 是否常规文件
S_ISDIR(st_mode) 0040000 是否目录
S_ISCHR(st_mode) 0020000 是否字符设备
S_ISBLK(st_mode) 0060000 是否块设备
S_ISFIFO(st_mode) 0010000 是否FIFO文件
S_ISLNK(st_mode) 0120000 是否链接文件
S_ISSOCK(st_mode) 0140000 是否SOCKET文件
文件访问权限 – st_mode
通过系统提供的宏来获取文件访问权限:
S_IRUSR 00400 bit:8 所有者有读权限
S_IWUSR 00200 7 所有者拥有写权限
S_IXUSR 00100 6 所有者拥有执行权限
S_IRGRP 00040 5 群组拥有读权限
S_IWGRP 00020 4 群组拥有写权限
S_IXGRP 00010 3 群组拥有执行权限
S_IROTH 00004 2 其他用户拥有读权限
S_IWOTH 00002 1 其他用户拥有写权限
S_IXOTH 00001 0 其他用户拥有执行权限
四、动态、静态库的使用
4.1、静态链接库
4.1.1、特点
在代码编译(链接)阶段会把静态库中的相关代码复制到可执行文件中。那么在运行时就无需静态库了。因此在程序运行时也无需加载库,运行的速度也会更快,同时也会占用更多的内存。
还有一个重要的点,当静态库发生更新之后,所有使用静态库的代码重新编译(链接)。
4.1.2、静态库创建
1、编写静态库源码
2、编译gcc -c hello.c
3、创建静态库ar crs libhello.a hello.o
4、可以查看库中包含的函数nm libhello.a
4.1.3、静态库使用
5、调用静态库
6、编译gcc test.c -o test -L. -lhello,注意这里需要增加-L. -lhello,L.表示要增加的搜索路径为当前路径(因为我们没有将库放到lib这样的默认路径下,系统是找不到这个库的),l后紧跟的是要链接的库的名称。
7、执行代码./test。
此时即使我们删除了libhello.a静态库,程序依然可以运行,就是因为库已经被复制一份了。
4.2、动态链接库
4.2.1、动态库特点
编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码程序不包含库中代码,尺寸小多个程序可共享同一个库程序运行时需要加载库库升级方便,无需重新编译程序
使用更加广泛。
4.2.2、动态库创建
1、编写库的源码
2、编译生成目标文件gcc -c -fPIC hello.c -Wall,这样就生成了hello.o
3、生成hello动态库文件gcc -shared -o libhello.so.1 hello.o
4、为动态库文件创建符号链接文件ln -s libhello.so.1 libhello.so
4.2.3、动态库创建
5、调用动态库(为了省去调用函数时需要进行函数声明,我们可以编写一个头文件,从而调引入头文件)
#include<stdio.h>
#include"hello.h"
int main()
{
hello();
return 0;
}
//hello.h
void hello(void);
6、编译test.c 并链接动态库gcc test.c -o -L. -lhello,这里的hello为动态库名,需要和创建的头文件hello.h同名
(7)运行代码./test
若在运行时提示加载动态库错误,我们可以去系统目录下增加一个配置文件。
sudo vim /etc/ld.so.conf.d/my_conf