Linux下标准I/O和文件I/O

一、标准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

标准输入流0STDIN_FILENOstdin
标准输出流1STDOUT_FILENOstdout
标准错误流2STDERR_FILENOstderr

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值