.文件IO

1.标准IO

文件基础

概念:       一组相关数据的有序集合

文件类型:

        常规文件    r  

        目录文件    d

        字符设备文件    c

        块设备文件    b

         管道文件    p

        套接字文件    s

        符号链接文件    l

标准I/O – 介绍

标准I/O由ANSI C标准定义  

主流操作系统上都实现了C库  

标准I/O通过缓冲机制减少系统调用,实现更高的效率

缓冲机制是一种解决速度不匹配问题的方法,通过开辟一块存储区域作为缓冲区,并根据中断机制来进行数据的输入输出操作。当输入和输出共用同一片内存时,为了避免数据冲突,输入进程将数据拷贝到缓冲区,而输出进程从缓冲区中取走数据。这样可以有效地避免数据的冲突,并提高CPU的利用率。缓冲机制的实现可以采用队列或链表等数据结构来进行数据的存储和删除操作。

IO的概念

   I input    输入设备  比如键盘鼠标都是Input设备

   O output    输出设备  比如显示器

   优盘,网口,既是输入也是输出

标准I/O – 流

FILE  

        标准IO用一个结构体类型来存放打开的文件的相关信息  

        标准I/O的所有操作都是围绕FILE来进行

流(stream)  

         FILE又被称为流(stream)    

        文本流/二进制流

就是数据的流,在程序中就是一个结构体。

Windows 和linux的换行符区别

   Windows是\r\n

   Linux 是\n

标准I/O – 流的缓冲类型

为了减少操作IO设备的次数,提高运行效率,在内存里面设置的缓冲区

全缓冲:缓冲区满才输出

 行缓冲:遇到换行符输出

无缓冲:数据直接写入文件,流不进行缓冲

Sleep函数:是释放cpu给其他应用程序使用的库函数。使用的头文件是#include <unistd.h>

三种标准IO  :

标准输入流键盘

0

STDIN_FILENO

stdin

标准输出流显示器

1

STDOUT_FILENO

stdout

标准错误流

2

STDERR_FILENO

stderr

2.文件的打开和关闭概念

打开就是占用资源

关闭就是释放资源

文件的打开函数

FILE *fopen (const char *path, const char *mode);

Path: 普通文件当前路径不需要加目录,其他要使用完整的路径

Mode:

返回值:出错返回NULL,所以使用fopen函数必须判断是否为空

fopen() 创建的文件访问权限是0666(rw-rw-rw-)

Linux系统中umask设定会影响文件的访问权限,其规则为(0666 & ~umask)

Root用户是 022 普通用户是002 用户可以通过umask函数或者命令修改相关设定

文件打开的模式(非常重要)

“r” 或 “rb”

以只读方式打开文件,文件必须存在。

“r+” 或 ”r+b”

以读写方式打开文件,文件必须存在。

“w” 或 “wb”

以只写方式打开文件,若文件存在则文件长度清为0。若文件不存在则创建。

“w+” 或 “w+b”

以读写方式打开文件,其他同”w”。

“a” 或 “ab”

以只写方式打开文件,若文件不存在则创建;向文件写入的数

据被追加到文件末尾。

“a+” 或 “a+b”

以读写方式打开文件。其他同”a”

文件的关闭

int fclose(FILE *stream)
  1. fclose()调用成功返回0,失败返回EOF(-1),并设置errno
  2. 流关闭时自动刷新缓冲中的数据并释放缓冲区,比如:常规文件把缓冲区内容写入磁盘
  3. 当一个程序正常终止时,所有打开的流都会被关闭
  4. fclose()函数的入参stream必须保证为非空,否则出现断错误。

标准I/O – 处理错误信息

errno 存放错误号,由系统生成 perror先输出字符串s,再输出错误号对应的错误信息

strerror根据错误号返回对应的错误信息

extern int  errno;
void perror(const char *s);
char *strerror(int errno);
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc,char *argv[]){
    FILE *fp;
    int fpret;
    fp = fopen("1.txt","r");
    if(fp==NULL){
        //printf("Open file Failed\n");
        perror("fopen");
        printf("fopen:%s\n",strerror(errno));
                

    }else{
	printf("Open file success\n");
       // perror("open");
       fpret = fclose(fp);
       if(fpret==0){
            printf("file close sucess\n");
       }else{
            perror("fclose");
       }
    }
    

}

3.标准I/O – 读写流

字符的输入(读单个字符)

int  fgetc(FILE *stream);
int  getc(FILE *stream);   //宏
int  getchar(void);

成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1),

getchar()等同于fgetc(stdin)

getc和fgetc区别是一个是宏一个是函数

注意事项:

1函数返回值是int类型不是char类型,主要是为了扩展返回值的范围。

2 stdin 也是FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)

3 打开文件后读取,是从文件开头开始读。读完一个后读写指针会后移。读写注意文件位置!

4 调用getchar会阻塞,等待你的键盘输入

字符的输出(写单个字符)

int  fputc(int c, FILE *stream);
int  putc(int c, FILE *stream);
int  putchar(int c);

成功时返回写入的字符;出错时返回EOF

putchar(c)等同于fputc(c, stdout)

注意事项:

1返回和输入参数都是int类型

2遇到这种错误:Bad file descriptor,  很可能是文件打开的模式错误(只读模式去写,只写模式去读)

行输入(读取整个行)

char  *gets(char *s);  读取标准输入到缓冲区s
char *fgets(char *s, int size, FILE *stream);

成功时返回s,到文件末尾或出错时返回NULL

遇到’\n’或已输入size-1个字符时返回,总是包含’\0’

注意事项:

1 gets函数已经被淘汰,因为会导致缓冲区溢出

2 fgets 函数第二个参数,输入的数据超出size,size-1个字符会保存到缓冲区,最后添加’\0’,如果输入数据少于size-1 后面会添加换行符。

行输出(写整行)

int  puts(const char *s);
int fputs(const char *s,  FILE *stream);

成功时返回非负整数;出错时返回EOF

puts将缓冲区s中的字符串输出到stdout,并追加’\n’

fputs将缓冲区s中的字符串输出到stream,不追加  ‘\n’

二进制读写

文本文件和二进制的区别:

存储的格式不同:文本文件只能存储文本。

计算机内码概念:文本符号在计算机内部的编码(计算机内部只能存储数字0101001....,所以所有符号都要编码)

二进制读写函数格式

size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
void *ptr  读取内容放的位置指针
  size_t size 读取的块大小
size_t n 读取的个数
  FILE *fp  读取的文件指针

 

size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
void *ptr  写文件的内容的位置指针
size_t size 写的块大小
size_t n 写的个数
  FILE *fp  要写的文件指针

 

注意事项:

文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。

解决办法:移动指针(后面讲解)到文件头;关闭文件,重新打开

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct student{
   char name[16];
   int age;
   char sex[8];
};

int main(int argc,char *argv[]){
   FILE *fp;
   size_t ret;
   
   struct student stu;
   struct student stu2;

   fp=fopen("1.bin","w");
   if(fp==NULL){
      perror("fopen");
      return 0;
   }


   strcpy(stu.name,"zhangsan");
   stu.age = 49;
   strcpy(stu.sex,"male");
   
   ret = fwrite(&stu,sizeof(stu),1,fp);
   if(ret ==-1){
      perror("fwrite");
      goto end;

   }else{
     printf("write struct student success!\n");
   }
   fclose(fp);

   fp=fopen("1.bin","r");
   if(fp==NULL){
      perror("fopen");
      return 0;
   }
  

 
   ret = fread(&stu2,sizeof(stu),1,fp);
   if(ret ==-1){
      perror("fread");
      goto end;
   }
   
   printf("name=%s,age=%d,sex=%s\n",stu2.name,stu2.age,stu2.sex);


  

end:
   fclose(fp);


}

4.流刷新定位、格式化输入输出

“刷新流”的作用就是确保到目前为止的所有输出操作都已经被完成,数据已经被送到它应该去的地方。

流的刷新

 int fflush(FILE *fp);

成功时返回0;出错时返回EOF

将流缓冲区中的数据写入实际的文件

Linux下只能刷新输出缓冲区,输入缓冲区丢弃

如果输出到屏幕使用fflush(stdout)

流的定位

#include  <stdio.h>
long ftell(FILE *stream);返回当前文件位置指针相对于文件开头的偏移量
long fseek(FILE *stream, long offset,  int whence);用于设置文件位置
void rewind(FILE *stream);将文件位置指针重新设置到文件的开头

fseek 参数whence参数:SEEK_SET/SEEK_CUR/SEEK_END

SEEK_SET 从距文件开头 offset 位移量为新的读写位置

SEEK_CUR:以目前的读写位置往后增加 offset 个位移量

SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量

offset参数:偏移量,可正可负

ftell() 成功时返回流的当前读写位置,出错时返回EOF fseek()定位一个流,成功时返回0,出错时返回EOF

注意事项:

1.文件的打开使用a模式 fseek无效

2.rewind(fp) 相当于 fseek(fp,0,SEEK_SET);

3.这三个函数只适用2G以下的文件

示例一(在文件末尾追加字符’t’)
  FILE *fp = fopen(“test.txt”, “r+”);
  fseek(fp, 0, SEEK_END);
  fputc(‘t’, fp);
示例二(获取文件长度)
  FILE  *fp;
  if ((fp = fopen(“test.txt”, “r+”)) == NULL) {
     perror(“fopen”);
     return  -1;
  }
  fseek(fp, 0, SEEK_END);
  printf(“length is %d\n”, ftell(fp));

格式化输出

int fprintf(FILE *stream, const char *fmt, …);
int sprintf(char *s, const char *fmt, …);

成功时返回输出的字符个数;出错时返回EOF

以指定格式 “年-月-日” 分别写入文件和缓冲区
int  year, month, date;
FILE *fp;
char buf[64];
year = 2014; month = 10; date = 26;
fp = fopen(“test.txt”, “a+”);
fprintf(fp, “%d-%d-%d\n”, year, month, date);
sprintf(buf, “%d-%d-%d\n”, year, month, date);
#include "stdio.h"

int main(int argc,char *argv[]){

    char buf[100]={0};
    int year=2021;
    int month= 10;
    int day=1;

    int syear;
    int smonth;
    int sday;

    sprintf(buf,"%d-%d-%d",year,month,day);
   
    printf("%s\n",buf);

    sscanf(buf,"%d-%d-%d",&syear,&smonth,&sday);
    printf("%d,%d,%d\n",syear,smonth,sday);

}

格式化输入

int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

重点掌握sprintf 和sscanf

#include "stdio.h"

int main(int argc,char *argv[]){
    FILE *fp;
    int year;
    int month;
    int day;

    fp=fopen("ftest.txt","r");
    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);

}

练习

每隔1秒向文件test.txt中写入当前系统时间,格式如下:

1,  2014-10-15 15:16:42

2,  2014-10-15 15:16:43

该程序无限循环,直到按Ctrl-C中断程序 每次执行程序时,系统时间追加到文件末尾,序号递增

1,  2014-10-15 15:16:42

2,  2014-10-15 15:16:43

3,  2014-10-16 11:35:07

4,  2014-10-16 11:35:08

time()用来获取系统时间(秒数)

time_t time(time_t *seconds) 1970.1.1 0:0:0

localtime()将系统时间转换成本地时间 struct tm *localtime(const time_t *timer)

#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>


int main(int argc,char *argv[]){
     FILE *fp;
     time_t ctime;
     struct tm *ctimestr;
     int linecount = 0; 
     char buf[32];
     fp=fopen("test.txt","a+");
     if(fp==NULL){
         perror("fopen");
         return 0;

     }
     //calculate test.txt  line    

     while(fgets(buf,32,fp)!=NULL){
         
          if(buf[strlen(buf)-1] =='\n'){
               linecount++;
          }


    } 


 
     while(1){
         ctime = time(NULL);
         //printf("ctime=%d\n",(int)ctime);
         ctimestr = localtime(&ctime);
         printf("%04d-%02d-%02d %02d:%02d:%02d\n",ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
                                     ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
         fprintf(fp,"%d, %04d-%02d-%02d %02d:%02d:%02d\n",linecount,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
                                     ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
         fflush(fp);
         linecount++;
         sleep(1);
     }

     fclose(fp);

}


写完文件记得fflush ,写到磁盘里面去。

标准IO磁盘文件的缓冲区一般为4096

注意和标准输出的全缓冲区别,标准输出是1024

5.文件IO

文件I/O – 介绍

什么是文件I/O?

posix(可移植操作系统接口)定义的一组函数 不提供缓冲机制,每次读写操作都引起系统调用

核心概念是文件描述符 访问各种类型文件 Linux下, 标准IO基于文件IO实现

文件I/O – 文件描述符

每个打开的文件都对应一个文件描述符。

文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符。

文件描述符从0开始分配,依次递增。 文件IO操作通过文件描述符来完成。

文件描述符概念:

英文:缩写fd(file descriptor)

是0-1023的数字,表示文件。

0, 1, 2 的含义 标准输入,标准输出,错误

文件IO 打开

int open(const char *pathname, int flags);   不创建文件
int open(const char *pathname, int flags, mode_t mode);  创建文件,不能创建设备文件
成功时返回文件描述符;出错时返回EOF
int  fd;

if ((fd  = open(“1.txt”, O_RDWR|O_CREAT|O_EXCL, 0666)) < 0) {
    if (errno == EEXIST) { 
       perror(“exist error”);
    } else {
       perror(“other error”);
    }
}

成功时返回文件描述符;出错时返回EOF

打开文件时使用两个参数

创建文件时第三个参数指定新文件的权限,(只有在建立新文件时有效)此外真正建文件时的权限会受到umask 值影响,实际权限是mode-umaks

可以打开设备文件,但是不能创建设备文件

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

umask概念:

umask 用来设定文件或目录的初始权限

文件和目录的真正初始权限

文件或目录的初始权限 = 文件或目录的最大默认权限 - umask权限

文件的关闭

close函数用来关闭一个打开的文件

#include  <unistd.h>
 int  close(int fd);

成功时返回0;出错时返回EOF

程序结束时自动关闭所有打开的文件

文件关闭后,文件描述符不再代表文件

容易出错点:

求字符串长度使用sizeof,对二进制数据使用strlen

printf 的字符最后没有’\0’

文件I/O – 读

 read函数用来从文件中读取数据

#include  <unistd.h>
 ssize_t  read(int fd, void *buf, size_t count);

成功时返回实际读取的字节数;

出错时返回EOF    读到文件末尾时返回0

buf是接收数据的缓冲区 count不应超过buf大小

例:从指定的文件(文本文件)中读取内容并统计大小

int  main(int argc, char *argv[]) {
{
    int  fd, n, total = 0;
    char  buf[64];
    if (argc < 2) {
       printf(“Usage : %s <file>\n”, argv[0]);  return -1;
    }
    if ((fd  = open(argv[1], O_RDONLY)) < 0) {
           perror(“open”);  return -1;
    }
    while ((n = read(fd, buf, 64)) > 0) {
        total += n;
    }

文件I/O – write

write函数用来向文件写入数据

#include  <unistd.h>  
ssize_t  write(int fd, void *buf, size_t count);

成功时返回实际写入的字节数;

出错时返回EOF

buf是发送数据的缓冲区

count不应超过buf大小

例:将键盘输入的内容写入文件,直到输入quit

int  fd;
    char  buf[20];

    if ((fd  = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
           perror(“open”);  return -1;
    }
    while (fgets(buf, 20, stdin) != NULL) {
        if (strcmp(buf, “quit\n”) == 0) break;
        write(fd, buf, strlen(buf));
    }

文件IO – 定位

lseek函数用来定位文件

#include  <unistd.h>
 off_t  lseek(int fd, off_t offset, intt whence);

成功时返回当前的文件读写位置;出错时返回EOF 参数offset和参数whence同fseek完全一样

6.目录操作和库的使用

文件属性、目录操作

访问目录 – opendir

opendir函数用来打开一个目录文件

#include  <dirent.h>
DIR  *opendir(const char *name);
DIR *fdopendir(int fd); 使用文件描述符,要配合open函数使用

DIR是用来描述一个打开的目录文件的结构体类型

成功时返回目录流指针;出错时返回NULL

访问目录 – readdir

readdir函数用来读取目录流中的内容

#include  <dirent.h>
 struct  dirent *readdir(DIR *dirp);

struct dirent是用来描述目录流中一个目录项的结构体类型

包含成员char  d_name[256]   参考帮助文档

成功时返回目录流dirp中下一个目录项;

出错或到末尾时时返回NULL

访问目录 – closedir

closedir函数用来关闭一个目录文件

#include  <dirent.h>
int closedir(DIR *dirp);

成功时返回0;出错时返回EOF

int  main(int argc, char *argv[]) {
{
    DIR *dirp;
    struct dirent *dp;
    if (argc < 2) {
       printf(“Usage : %s <directory>\n”, argv[0]);  return -1;
    }
    if ((dirp  = opendir(argv[1])) == NULL) {
           perror(“opendir”);  return -1;
    }
    while ((dp = readdir(dirp)) != NULL) {
        printf(“%s\n”, dp->d_name);
    }
    closedir(dirp);

修改文件访问权限 – chmod/fchmod

chmod/fchmod函数用来修改文件的访问权限

#include  <sys/stat.h>
int  chmod(const char *path, mode_t mode);
int  fchmod(int fd, mode_t mode);

成功时返回0;出错时返回EOF

root和文件所有者能修改文件的访问权限

chmod(“test.txt”, 0666);

获取文件属性 – stat/lstat/fstat

stat/lstat/fstat函数用来获取文件属性

#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);

成功时返回0;出错时返回EOF

如果path是符号链接

stat获取的是目标文件的属性;而lstat获取的是链接文件的属性

例:

假设我们有一个目录结构如下
/example_dir/  
    real_file.txt     # 这是一个普通的文本文件  
    symlink_to_file   # 这是一个指向real_file.txt的符号链接
在这个例子中,real_file.txt是一个实际存在的文件,而symlink_to_file是一个符号链接,它指向real_file.txt。

现在,我们使用stat()和lstat()函数来获取这两个文件的属性:
#include <stdio.h>  
#include <sys/stat.h>  
#include <unistd.h>  
  
int main() {  
    struct stat stat_buf, lstat_buf;  
  
    // 使用stat()函数获取符号链接指向的目标文件的属性  
    if (stat("/example_dir/symlink_to_file", &stat_buf) == 0) {  
        printf("stat() on symlink: Size of the target file: %ld bytes\n", stat_buf.st_size);  
    } else {  
        perror("stat");  
    }  
  
    // 使用lstat()函数获取符号链接文件本身的属性  
    if (lstat("/example_dir/symlink_to_file", &lstat_buf) == 0) {  
        printf("lstat() on symlink: Size of the symlink itself: %ld bytes\n", lstat_buf.st_size);  
    } else {  
        perror("lstat");  
    }  
  
    return 0;  
}

在这个程序中,我们首先调用stat()函数来获取符号链接symlink_to_file指向的目标文件(即real_file.txt)的属性,并打印出目标文件的大小。然后,我们调用lstat()函数来获取符号链接文件本身(即symlink_to_file)的属性,并打印出链接文件本身的大小。

stat() on symlink: Size of the target file: 1234 bytes   # 假设real_file.txt的大小是1234字节  
lstat() on symlink: Size of the symlink itself: 23 bytes  # 符号链接本身的大小,取决于路径字符串的长度

文件属性 – struct  stat

struct stat是存放文件属性的结构体类型:
   
mode_t  st_mode;类型和访问权限
uid_t  st_uid;所有者id
uid_t  st_gid;用户组id
off_t  st_size;文件大小
time_t  st_mtime;最后修改时间

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;     //最后一次改变时间(指属性)
};

程序库

库的概念

库是一个二进制文件,包含的代码可被程序调用

标准C库、数学库、线程库……

库有源码,可下载后编译;也可以直接安装二进制包

/lib   /usr/lib

库是事先编译好的,可以复用的代码。

在OS上运行的程序基本上都要使用库。使用库可以提高开发效率。

Windows和Linux下库文件的格式不兼容 Linux下包含静态库和共享库

静态库

编译(链接)时把静态库中相关代码复制到可执行文件中

程序中已包含代码,运行时不再需要静态库 程序运行时无需加载库,运行速度更快

占用更多磁盘和内存空间 静态库升级后,程序需要重新编译链接

编写库文件代码,编译为.o 目标文件。

ar 命令 创建  libxxxx.a 文件

ar -rsv  libxxxx.a  xxxx.o

 注意:

1 静态库名字要以lib开头,后缀名为.a

2 没有main函数的.c 文件不能生成可执行文件。

静态库创建(1)

确定库中函数的功能、接口 

编写库源码hello.c
     #include  <stdio.h>
     void  hello(void) {
        printf(“hello  world\n”);
        return;
     }

编译生成目标文件
     $ gcc  -c  hello.c  -Wall

静态库创建(2)
创建静态库 hello

     $ ar  -rsv  libhello.a  hello.o
静态库创建(3)
ar 参数:
c  禁止在创建库时产生的正常消息
r  如果指定的文件已经存在于库中,则替换它
s  无论 ar 命令是否修改了库内容都强制重新生成库符号表
v 将建立新库的详细的逐个文件的描述写至标准输出
q     将指定的文件添加到库的末尾

t  将库的目录写至标准输出 

静态库创建(4)
查看库中符号信息
     $nm  libhello.a

      hello.o:
      0000000 T  hello
                     U  puts
 nm:显示指定文件中的符号信息
   -a 显示所有符号
   

链接静态库
编写应用程序test.c
    #include  <stdio.h>
     void  hello(void);    
     int  main() {
        hello();
        return  0;
     }

编译test.c 并链接静态库libhello.a
     $  gcc  -o  test  test.c  -L.  -lhello
     $ ./test
      hello  world

动态库(共享库)

共享库特点

编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码

程序不包含库中代码,尺寸小

多个程序可共享同一个库

程序运行时需要加载库

库升级方便,无需重新编译程序 使用更加广泛

1.生成位置无关代码的目标文件
 gcc  -c  -fPIC  xxx.c xxxx.c ....
2.生成动态库
   gcc  -shared -o libxxxx.so  xxx.o  xxx.o ....

3.编译可执行文件
gcc -o 目标文件 源码.c  -L路径  -lxxxx
共享库创建(1)

确定库中函数的功能、接口

编写库源码hello.c  bye.c
     #include  <stdio.h>
     void  hello(void) {
        printf(“hello  world\n”);
        return;
     }

编译生成目标文件
     $ gcc  -c  -fPIC  hello.c  bye.c  -Wall
共享库创建(2)
创建共享库 common
     $ gcc  -shared  -o  libcommon.so.1  hello.o  bye.o

为共享库文件创建链接文件
     $ ln  -s  libcommon.so.1  libcommon.so
      
  符号链接文件命名规则
      lib<库名>.so


链接共享库 
编写应用程序test.c
    #include  <stdio.h>
    #include “common.h”  
     int  main() {
        hello();
        bye();
        return  0;
     }
编译test.c 并链接共享库libcommon.so
     $  gcc  -o  test  test.c  -L.  -lcommon
     
加载共享库
执行程序
   $ ./test
   ./test: error while loading shared libraries: libcommon.so
   cannot open shared object file : No such file or directory

添加共享库的加载路径
   $  export  LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
   $ ./test
      hello  world
      bye!
如何找到共享库

      为了让系统能找到要加载的共享库,有三种方法 :

把库拷贝到/usr/lib和/lib目录下

在LD_LIBRARY_PATH环境变量中添加库所在路径  

添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新

查看可执行文件使用的动态库
ldd 命令 :   ldd 你的可执行文件
root@haas-virtual-machine:/mnt/hgfs/share/newIOP# ldd test
	linux-vdso.so.1 =>  (0x00007fff6548d000)
	libmyheby.so => /mnt/hgfs/share/newIOP/day5/libmyheby.so (0x00007f5c89521000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c89144000)
	/lib64/ld-linux-x86-64.so.2 (0x000055fe52211000)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ARM小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值