【1】i/o
本质就是输入输出函数,也是读写函数
【2】系统调用和库函数
系统调用:
使用函数控制linux内核,linux内核来操作硬件
库函数:
库函数的本质还是系统调用,只不过需要在内存当中开辟一块空间(缓冲区),从而减少系统调用的次数
【3】io分类
文件io:
就是系统调用,例如:open、read、write
移植性比较差
标准io:
就是库函数,例如:printf、scanf
移植性比较好
【4】标准I/O
(1)概念
不仅在UNIX系统,在很多操作系统上都实现了标准I/O库,标准I/O库由ANSI C标准说明。标准I/O库处理很多细节,如缓存分配、以优化长度执行I/O等,这样使用户不必关心如何选择合适的块长度。标准I/O在系统调用函数基础上构造的,它便于用户使用。标准I/O库及其头文件stdio.h为底层I/O系统调用提供了一个通用的接口。标准io本质还是系统调用,只不多在执行系统调用前,在内存当中开辟一块区域(缓冲区)用于减少系统调用的次数。
(2)缓冲区的分类
机制:将要操作的数据先放在缓冲区中,如果刷新缓冲区,就立即执行系统调用, 如果没有缓冲区,数据会一直存储在缓冲区中。
①行缓冲:对于终端操作采用的缓冲区
缓存区大小: 1024字节(1K)
刷新缓存 :程序正常结束、缓存区满、 ’\n’ 、使用fflush函数
②全缓冲:对于文件操作采用的缓冲区
缓存区大小:4096字节(4K)
刷新缓存 :程序正常结束、缓存区满、使用fflush函数
③无缓冲:对于终端操作采用的缓冲区
#include <stdio.h>
int main(int argc, const char *argv[])
{
//标准io在执行的时候都需要刷新缓冲区,如果不刷新没有办法执行相应操作
//printf("hello world");
//如何刷新缓冲区
//方法1:使用\n
//printf("hello world\n");
//方法2:当前程序正常结束
//printf("hello world");
//return 0;
//方法3:使用fflush( )函数
//printf("hello world");
//fflush(stdout);
//方法4:当缓冲区满的时候,可以刷新缓冲区
int i;
for(i = 1; i <= 300; i++)
{
printf("%03d ", i);
}
while(1)
;
return 0;
}
(3)ctags的创建和使用
ctags是在指定目录文件里面建立索引文件,用于查找指定内容(宏定义、重命名、结构体)的具体位置。
① 创建索引文件
在/usr/include目录里面执行sudo ctags -R,会在当前目录下生成tags的索引文件。
测试:vim -t FILE
②将当前索引文件设置为全局
在家目录下的.vimrc文件里面添加set tags+=/usr/include/tags
③ 基本操作指令
vim -t *** 查找对应内容的位置
输入编号可以执行到达执行数据的文件的位置,如果编号较多,可以按
两下esc再输入编号
ctrl+] 追代码
ctrl+t 返回上一级
(4)文件指针(流指针)
FILE结构体:
每一个使用标准io操作的文件都会通过一个结构体来标识当前文件的信息,
这个结构体的类型名为FILE,标准io操作文件是通过当前结构体的指针变量
来操作的,称之为流或者流指针,流就是用来标识和操作文件的。
typedef struct _IO_FILE FILE;
FILE * 流指针的类型
当进程创建或者开启的时候,会先创建三个流指针
stdin 标准输入 从终端输入
stdout 标准输出 从终端输出
stderr 标准出错 从终端出错输出
(5)库函数
①fopen() 打开或者创建文件
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
功能:打开或者创建一个文件,得到一个流指针
参数:
path:文件名,可以添加路径,默认不添加为当前路径
mode:文件的访问权限
r 以只读的方式打开文件,定位到文件起始位置
r+ 以读写的方式打开文件,定位到文件起始位置
w 以只写的方式打开文件,如果文件不存在则创建,如果存在则清空,定位到文件起始位置
w+ 以读写的方式打开文件,如果文件不存在则创建,如果存在则清空,定位到文件起始位置
a 以只写的方式打开文件,如果文件不存在则创建,如果存在则追加,定位到文件末尾位置
a+ 以读写的方式打开文件,如果文件不存在则创建,如果存在则追加,定位到文件末尾位置
返回值:
成功:流指针
失败:NULL
include <stdio.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
//使用fopen打开或者创建文件
FILE *fp;
if((fp = fopen("file.txt", "r")) == NULL)
{
//errno:是一个变量,用于获取函数调用错误后的错误码
printf("errno = %d\n", errno);
//使用perror函数打印函数调用失败的错误信息
perror("fail to fopen");
return -1;
}
//使用fclose关闭流指针
fclose(fp);
return 0;
}
②perror( ) 输出函数调用失败的错误信息
#include <stdio.h>
void perror(const char *s);
功能:输出函数调用失败的错误信息
参数:
s:提示字符串
返回值:无
errno 在errno.h定义的全局变量,用于获取函数调用错误后的错误码,可以在errno.h里面找到对用错误码的错误信息
③fclose( ) 关闭一个流指针
#include <stdio.h>
int fclose(FILE *fp);
功能:关闭一个流指针
参数:
fp:指定的流
返回值:
成功:0
失败:EOF
④fprintf( ) 向一个文件写数据
#include <stdio.h>
int fprintf(FILE *restrict stream, const char *restrict format, ...);
功能:向一个文件写数据
参数:
stream:指定的文件的流指针
format:同printf的参数
返回值:
成功:输出的字符的个数
失败:-1
#include <stdio.h>
int main(int argc, const char *argv[])
{
//向终端输出数据
fprintf(stdout, "hello world\n");
char name[] = "zhangsan";
fprintf(stdout, "My name is %s\n", name);
//向文件写入数据
FILE *fp;
if((fp = fopen("file.txt", "w")) == NULL)
{
perror("fail to fopen");
return -1;
}
fprintf(fp, "hello world\n");
char n[] = "zhangsan";
fprintf(fp, "My name is %s\n", n);
return 0;
}
⑤freopen( ) 对于一个已有的流进行重定向
FILE *freopen(const char *restrict pathname,
const char *restrict type, FILE* restrict fp)
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("nihao beijing\n");
FILE *fp;
//将标准输出的数据重定向写入一个文件里面
if((fp = freopen("file.txt", "a", stdout)) == NULL)
{
perror("fail to freopen");
return -1;
}
printf("nihao beijing\n");
return 0;
}
⑥字符的读写函数
1 – fgetc( )
#include <stdio.h>
int fgetc(FILE *stream);
功能:从一个文件读取一个字符
参数:
stream:指定的文件的流指针
返回值:
成功:读取到的字符的ascii码值
失败:EOF
如果读取到文件的末尾,会返回EOF
#include <stdio.h>
int main(int argc, const char *argv[])
{
//从终端获取一个字符
#if 0
int c;
//c = getchar();
c = fgetc(stdin);
printf("[%c] -- %d\n", c, c);
#endif
if(argc < 2)
{
fprintf(stderr, "Usage: %s filename\n", argv[0]);
return 1;
}
//从文件里面读取一个字符
FILE *fp;
if((fp = fopen(argv[1], "r")) == NULL)
{
perror("fail to fopen");
return -1;
}
#if 0
int c = fgetc(fp);
printf("[%c] -- %d\n", c, c);
c = fgetc(fp);
printf("[%c] -- %d\n", c, c);
c = fgetc(fp);
printf("[%c] -- %d\n", c, c);
#endif
//文件的每一行的结尾处都有一个换行符
//fgetc可以读取到换行符
int c;
while((c = fgetc(fp)) != EOF)
{
printf("[%c] -- %d\n", c, c);
}
fclose(fp);
return 0;
}
2 – fputc( )
#include <stdio.h>
int fputc(int c, FILE *stream);
功能:向一个文件写入一个字节的数据
参数:
c:要写入的字符
stream:指定的文件的流指针
返回值:
成功:写入的字符
失败:EOF
#include <stdio.h>
int main(int argc, const char *argv[])
{
//向终端写入一个字符
#if 0
putchar('a');
putchar(122);
char c = 'W';
putchar(c);
putchar(10);
#endif
#if 0
fputc('T', stdout);
fputc(100, stdout);
char a = 'U';
fputc(a, stdout);
fputc(10, stdout);
#endif
//向文件写入一个字符
FILE *fp;
if((fp = fopen("file.txt", "r+")) == NULL)
{
perror("fail to fopen");
return -1;
}
fputc('h', fp);
fputc('e', fp);
fputc('l', fp);
fputc('l', fp);
fputc('o', fp);
fputc('N', fp);
fputc('O', fp);
//当文件写完数据后,此时的文件的偏移量是文件的末尾处,所以如果读取数据的话读取不到
#if 0
int c;
while((c = fgetc(fp)) != EOF)
{
printf("%c - ", c);
}
putchar(10);
#endif
fclose(fp);
return 0;
}
⑦ 字符串读写函数
1 – fgets( )
#include <stdio.h>
char *fgets(char *restrict s, int n, FILE *restrict stream);
功能:从一个文件读取一串字符
参数:
s:保存读取的数据
n:读取的字节数
stream:指定的文件的流指针
返回值:
成功:读取的数据
失败:NULL
当读取到文件末尾时,fgets返回NULL
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//从终端读取数据
#if 0
char s[32] = {};
//gets(s);
//fgets从终端读取字符串时,
//如果读取的内容小于第二个参数值,则会将换行符也读到数组里面
//如果读取的内容大于第二个参数值,则只会获取到第二个参数-1个字节,最后一个位置补\0
fgets(s, 6, stdin);
//取消读取到的换行符
//s[strlen(s) - 1] = '\0';
printf("s = %s\n", s);
#endif
//从文件获取数据
//如果读取的内容大于fgets的第二个参数,则只能读取第二个参数-1个字节,最后一个位置补\0
//如果设置的第二个参数要读取的数据查过文件一行内容,则只会读取一行,当读取到行结束符时函数结束
FILE *fp;
if((fp = fopen("file.txt", "r")) == NULL)
{
perror("fail to fopen");
return -1;
}
char s[32];
fgets(s, 32, fp);
printf("s = [%s]\n", s);
fgets(s, 32, fp);
printf("s = [%s]\n", s);
fclose(fp);
return 0;
}
2 – fputs( )
#include <stdio.h>
int fputs(const char *restrict s, FILE *restrict stream);
功能:向一个文件写入的数据
参数:
s:要写入文件的数据
stream:指定的文件的流指针
返回值:
成功:写入的数据的字符数
失败:EOF
#include <stdio.h>
int main(int argc, const char *argv[])
{
//向终端写入数据
#if 0
//puts函数自带换行
//puts("hello world");
//fputs函数不自带换行符
fputs("hello world\n", stdout);
#endif
//向文件写入数据
FILE *fp;
if((fp = fopen("file.txt", "w")) == NULL)
{
perror("fail to fopen");
return -1;
}
fputs("nihao beijing\n", fp);
fputs("nihao beijing\n", fp);
fclose(fp);
return 0;
}
⑧读写流
1 – fread( )
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从一个文件读取数据
参数:
ptr:保存读取的内容
size:每次读取的字节数
nmemb:读取的次数
stream:指定的文件的流指针
返回值:
成功:读取的对象数(次数)
失败:EOF
#include <stdio.h>
typedef struct{
int a;
char b;
char c[32];
}mystu;
int main(int argc, const char *argv[])
{
//从文件获取数据
FILE *fp;
if((fp = fopen("file.txt", "r")) == NULL)
{
perror("fail to fopen");
return -1;
}
#if 0
char s[32] = {};
int n = 0;
n = fread(s, 1, 100, fp);
printf("s = %s\n", s);
printf("n = %d\n", n);
#endif
mystu a;
fread(&a, 40, 1, fp);
printf("%d - %c - %s\n", a.a, a.b, a.c);
return 0;
}
2 – fwrite( )
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:向一个文件写数据
参数:
ptr:要写的内容
size:每次写入数据的字节数
nmemb:写入的次数
stream:指定的文件的流指针
返回值:
成功:写入的对象数(次数)
失败:EOF
#include <stdio.h>
typedef struct{
int a;
char b;
char c[32];
}mystu;
int main(int argc, const char *argv[])
{
//向文件写数据
FILE *fp;
if((fp = fopen("file.txt", "w")) == NULL)
{
perror("fail to fopen");
return -1;
}
//char s[] = "hello world";
//int s[4] = {10, 20, 30, 40};
mystu s = {100, 'w', "hello world"};
fwrite(&s, 40, 1, fp);
return 0;
}
⑨ fseek( ),rewind()
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:设置文件指针的位置
参数:
stream:指定的文件的流指针
offset:偏移量
whence:相对位置
SEEK_SET 文件起始位置
SEEK_CUR 文件指针当前位置
SEEK_END 文件末尾后的位置
返回值:
成功:0
失败:-1
void rewind(FILE *stream);
功能:将文件指针定位到文件起始位置
参数:
stream:指定的文件的流指针
返回值:
无
rewind(fp) <==> fseek(fp, 0, SEEK_SET)
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp;
//如果文件的访问权限设置为a或者a+,则写对应的文件指针的位置不能改变,只能在文件末尾。但是读没有限制
if((fp = fopen("file.txt", "a+")) == NULL)
{
perror("fail to fopen");
return -1;
}
fputs("0123456789", fp);
//获取文件的偏移量
printf("offset = %ld\n", ftell(fp));
//设置文件指针的位置
fseek(fp, -6, SEEK_END);
printf("offset = %ld\n", ftell(fp));
char s[32] = {};
while(fgets(s, 32, fp) != NULL)
{
printf("s = %s\n", s);
}
printf("offset = %ld\n", ftell(fp));
fseek(fp, 2, SEEK_SET);
fputs("abcd", fp);
fclose(fp);
return 0;
}
⑩ ftell( )
#include <stdio.h>
long ftell(FILE *stream);
功能:获取当前文件指针的位置
参数:
stream:指定的文件的流指针
返回值:
成功:获取的文件指针的位置
失败:-1
【5】文件IO
(1)概念
文件io本质是系统调用,用户通过应用层的函数调用linux内核的函数来操作硬件,文件io通过文件描述符来标识和操作文件。
(2)文件描述符
对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。
当读、写一个文件时,用open或creat返回的文件描述符标识该文件,将其作为参数传送给read或write。文件描述符用于标识和操作文件。当进程创建或者开启的时候,系统会自动创建三个文件描述符
0 标准输入 STDIN_FILENO
1 标准输出 STDOUT_FILENO
2 标准出错 STDERR_FILENO
(3)系统调用函数
① open()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:打开或者创建一个文件,得到一个文件描述符
参数:
pathname:文件名
flags:文件标志位
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 可读可写
O_APPEND 以追加的方式打开文件
O_CREAT 当文件不存在则创建
O_EXCL 一般与O_CREAT一起用,表示如果文件存在则open函数执行失败
O_TRUNC 如果文件存在则清空
mode:如果使用O_CREAT,则必须使用第三个参数
一般使用八进制数设置文件权限,例如0664
返回值:
成功:文件描述符
失败:-1
标准io文件权限 ----------------------------文件io文件权限
r -------------------------------------------------O_RDONLY
r+ -----------------------------------------------O_RDWR
w ------------------------------------------------O_WRONLY | O_CREAT | O_TRUNC, 0664
w+ ----------------------------------------------O_RDWR | O_CREAT | O_TRUNC, 0664
a ------------------------------------------------O_WRONLY | O_CREAT | O_APPEND, 0664
a+ ----------------------------------------------O_RDWR | O_CREAT | O_APPEND, 0664
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd;
//使用open函数打开或者创建文件
//if((fd = open("file.txt", O_RDONLY)) < 0)
//if((fd = open("file.txt", O_RDONLY | O_CREAT, 0664)) < 0)
//if((fd = open("file.txt", O_RDONLY | O_CREAT | O_TRUNC, 0664)) < 0)
if((fd = open("file.txt", O_RDONLY | O_CREAT | O_APPEND, 0664)) < 0)
{
perror("fail to open");
return -1;
}
//关闭文件描述符
close(fd);
return 0;
}
② close( )
#include <unistd.h>
int close(int fd);
功能:关闭一个文件描述符
参数:
fd:指定的文件描述符
返回值:
成功:0
失败:-1
③ read( )
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从一个文件里面读取数据
参数:
fd:指定的文件描述符
buf:保存读取的数据
count:想要读取的字节数
返回值:
成功:实际读取的字节数
失败:-1
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
//从终端读取数据
#if 0
char s[32] = {};
//如果输入的数据小于read的第三个参数,则会将换行符也保存在第二个参数里面
read(0, s, 32);
printf("s = %s\n", s);
#endif
//从文件读取数据
int fd;
if((fd = open("file.txt", O_RDONLY)) < 0)
{
perror("fail to open");
return -1;
}
char buf[32] = {};
ssize_t bytes;
//read从文件里面读取数据时,不会按照行来读取,根据第三个参数读取数据,也会将每一行的结束符读取到
//read函数从文件里面读取数据,第三个参数表示每次想读取的字节数,而返回值表示实际读取的字节数
bytes = read(fd, buf, 32);
printf("buf = %s\n", buf);
printf("bytes = %d\n", bytes);
return 0;
}
④ write( )
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向一个文件写数据
参数:
fd:指定的文件描述符
buf:要写入的数据
count:写入数据的长度``
返回值:
成功:写入的字节数
失败:-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//向终端写数据
#if 0
char s[12] = "hello world";
ssize_t bytes;
bytes = write(1, s, sizeof(s));
printf("bytes = %d\n", bytes);
#endif
//向文件写数据
int fd;
if((fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664)) < 0)
{
perror("fail to open");
return -1;
}
char s[32] = "hello world\nnihao beijing";
write(fd, s, strlen(s));
return 0;
}
⑤ lseek( )
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:设置和获取文件的偏移量
参数:
fd:指定的文件描述符
offset:偏移量
whence:相对位置
SEEK_SET 文件起始位置
SEEK_CUR 文件当前位置
SEEK_END 文件末尾位置
返回值:
成功:当前文件的位置
失败:-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//向文件写数据
int fd;
if((fd = open("file.txt", O_RDWR | O_CREAT | O_TRUNC, 0664)) < 0)
{
perror("fail to open");
return -1;
}
char s[32] = "0123456789";
write(fd, s, strlen(s));
//获取当前文件的偏移量
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
//设置文件的偏移量
lseek(fd, 5, SEEK_SET);
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
char buf[32] = {};
read(fd, buf, 32);
printf("buf = %s\n", buf);
lseek(fd, 3, SEEK_SET);
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
write(fd, "abcd", 4);
return 0;
}
【6】获取文件信息和目录的操作
(1)使用stat( )获取文件信息
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
功能:获取文件的信息
参数:
path:文件名,可以添加路径,默认不添加为当前路径
buf:文件信息结构体
struct stat {
dev_t st_dev; /* ID of device containing file / 文件所在设备的ID
ino_t st_ino; / inode number / inode节点号
mode_t st_mode; / protection / 模式(文件类型、访问权限)
nlink_t st_nlink; / number of hard links / 链向此文件的连接数(硬连接)
uid_t st_uid; / user ID of owner / 用户id
gid_t st_gid; / group ID of owner / 组id
dev_t st_rdev; / device ID (if special file) / 设备号,针对设备文件
off_t st_size; / total size, in bytes / 文件大小,字节为单位
blksize_t st_blksize; / blocksize for file system I/O / 系统块的大小
blkcnt_t st_blocks; / number of 512B blocks allocated / 文件所占块数
time_t st_atime; / time of last access / 最近存取时间
time_t st_mtime; / time of last modification / 最近修改时间
time_t st_ctime; / time of last status change */ 文件创建的时间
};
返回值:
成功:0
失败:-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
//使用stat函数获取文件的信息
struct stat s;
if(stat(argv[1], &s) == -1)
{
perror("fail to stat");
return -1;
}
printf("dev_num = %d\n", (int)s.st_dev);
printf("inode_num = %d\n", (int)s.st_ino);
printf("hard_link_num = %d\n", s.st_nlink); //获取当前文件硬链接文件的个数(包括文件本身)
printf("uid = %d\n", s.st_uid);
printf("gid = %d\n", s.st_gid);
printf("file_size = %ld\n", s.st_size);
return 0;
}
(2)目录的操作
①opendir( )
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开一个目录文件,返回一个目录流指针
参数:
name:目录文件的名字
返回值:
成功:目录流指针
失败:NULL
② readdir( )
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
功能:读取目录的信息
参数:
dirp:目录流指针,opendir的返回值
返回值:
成功:保存目录信息的结构体
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
失败:NULL
如果读取完目录的信息,也返回NULL
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, const char *argv[])
{
DIR *dirp;
struct dirent *dir_ent;
//打开目录文件
if((dirp = opendir(argv[1])) == NULL)
{
perror("fail to opendir");
return -1;
}
//获取目录中的信息
//readdir调用一次只获取一个文件的信息
while((dir_ent = readdir(dirp)) != NULL)
{
printf("%d -- %s\n", (int)dir_ent->d_ino, dir_ent->d_name);
}
return 0;
}
【7】静态库和动态库
(1)库的概念
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
(2)库的分类
静态库和共享库(动态库)
静态库在程序编译时会被连接到目标代码中,程序运行时将
不再需要该静态库,因此体积较大。
动态库在程序编译时并不会被连接到目标代码中,而是在程
序运行是才被载入,因此在程序运行时还需要动态库存在,
因此代码体积较小。
共享库的好处是,不同的应用程序如果调用相同的库,那么在
内存里只需要有一份该共享库的实例。
(3)静态库(static library)的创建
静态库对函数库的链接是放在编译时期(compile time)完成的。
程序在运行时与函数库再无瓜葛,移植方便
浪费空间和资源,因为所有相关的对象文件(object file)与
牵涉到的函数库(library)被链接合成一个可执行文件(executable file)
1)生成制作库文件所需的功能函数的可重定向文件
gcc -c fun.c -o fun.o
2)生成静态库文件
ar crs libfun.a fun.o
lib 表示库文件,一般不能省略
fun 表示库的名字
.a 表示静态库
3)静态库生效
gcc main.c -o main -L. –lfun
-L 指定库的路径
-l 指定库的名字
(4)共享库(动态库)( dynamic library )
动态库把对一些库函数的链接载入推迟到程序运行的时期(runtime)。
可以实现进程之间的资源共享。
将一些程序升级变得简单。
甚至可以真正做到链接载入完全由程序员在程序代码中控制。
1)生成制作库文件所需的功能函数的可重定向文件
gcc -fPIC -c fun.c -o fun.o
-fPIC 生成与位置无关的代码(生成的动态库文件在内存上
可以自动寻找一块合适的内存区域)
2)生成动态库文件
① gcc -shared fun.o -o libfun.so
.so 表示动态库文件
②gcc main.c -o main -L. -lfun
为了让执行程序顺利找到动态库,有三种方法 :
① 把库拷贝到/usr/lib或者/lib目录下。
② 在LD_LIBRARY_PATH环境变量中加上库所在路径。
③ 添加/etc/ld.so.conf.d/*.conf文件,把库所在的路径加到文件末尾,
并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见