1.应用编程概念
应用编程:把前人写好的模块化的程序或函数直接拿来用,叫应用编程。
应用编程时,拿到一个写好的接口函数,首先要想的是怎么用好它,不是非要研究怎么创造它。
统一标准、统一接口
分工的任务合在一起形成一个产品,需要标准的接口才能实现成功合并。
默认有一套标准的接口去守它的一个守则。可能是公司写好的,可能是行业写好的。
应用开发时,看到别人写好的(接口、函数、工具)时,看到它的(返回值、参数)要能知道它干什么用的,大致结构要能猜到。
什么是接口?
接口是规范、是协议,不包含具体怎么实现,不包含具体的代码。
接口函数:在接口的规范之下,实现对应功能的具体代码。
什么是例程?
什么是api?
api只是一个规则,只是一个声明,它不占用内存,也不运行任何代码。
api的核心三要素:1可以为你实现的功能有哪些、2规定输入内容的格式、3规定输出内容的格式。
api本身没有具体实现功能的函数。流程是如下:
1.api提供一个菜单,上面有可选的功能或需求。
2.用户提出需求给api,api将需求转交给对应的函数
3.函数的返回值直接交给用户,回来的时候就不经过api了。
系统调用是api,库函数是api。api是一组预定义的规则和接口,让程序之间可以互相调用功能。
系统调用:是内核对外的api声明,里面有内核的功能菜单,然后具体干活的是内核。
库函数:库函数的声明是api,具体干活的是:库自己的代码(在用户态执行)。它可能再调用系统调用(让内核干活),也可能完全自己干完。
如果一个人提供api给你:需要给你三样东西:头文件、实现代码(或者源码)、接口文档。
接口文档(每个函数是干什么的、参数分别代表什么、返回值如何判断成功/失败、使用注意事项(比如是否需要加锁、是否可重入等))
用好man手册:
遇到没用过的接口函数,要先去查man手册,翻译、总结,了解用法再去使用,不要着急写代码,用错了事倍功半。
2.文件与IO
一切皆文件
linux下一切皆文件,鼠标键盘插上去,系统里多出一个文件。
系统调用(api)
系统调用介于内核层和用户层之间,是一个交流窗口。
shell命令、用户程序他们两个平级,要想访问内核,都会先通过系统调用。(系统调用是众多api的一种)。系统调用会先把用户层发来的服务请求进行分类,再交付给内核。
shell和用户程序虽然是平级,但是,shell一直存在,shell负责把用户程序领到系统调用那里。
系统调用里的IO都是文件IO,linux下一切皆文件。
什么是IO:
IO设备:输入输出数据的硬件设备。
IO接口函数:将IO设备提供或需求的数据,通过函数来实现对应功能。不同类型的功能,有对应的专用协议。
对某一类的功能或应用(比如嵌入式IO的功能或应用),在写对应的IO接口函数时,有专门或常规的一套接口形式的范本或协议。这样就具备通用性,可以不同场景广泛应用。
IO接口:是IO设备设备 和 系统(通过IO接口函数)的桥梁。
C库函数(api)的作用:
库函数是提前写好的,封装好的函数,很标准,bug很少。
不同内核,使用不同版本的系统调用(api),这时,在调用时可能有不方便。于是,将一部分系统调用封装、优化成更便利的C库函数(设计另一个更便利、安全的api)。
库函数有的会调用系统调用,有的不会。
C库函数里有很多接口函数,不只IO接口函数(文件IO、标准IO),还有其他接口。
C库函数里90%是接口函数,供用户调用。
系统调用,让内核来干活:通常是针对硬件或底层操作。库函数针对应用层来操作。
一些IO接口的实现,直接与硬件设备、系统内核密切相关,例如系统调用函数;
一些IO接口的实现侧重屏蔽底层差异,提供兼容性更广的应用编程的数据交换方法(接口),例如C库函数。
总结:
系统调用是API,库函数也是API。
库函数封装了系统调用的一部分接口函数的声明:如:库函数里的标准IO就是封装了系统调用里的文件IO(额外加上缓存)。所以,标准IO使用时,库函数会内部调用系统调用里的文件IO,来访问内核里的文件IO函数。
系统调用是内核提供的 API,库函数是用户态提供的 API。部分库函数(如标准 I/O)封装了系统调用,以增加缓存等高级功能。标准 I/O 函数在需要实际读写数据时,会调用相应的系统调用进入内核。
3.文件IO基础
文件IO 系统调用内核里的struct file ,然后返回int类型的fd值。在应用层对fd进行操作,fd是文件描述符。
文件描述符
英文缩写fd(file descriptor),范围0-1023。文件IO操作通过fd来完成。
0为标准输入,1为标准输出,2为标准出错。所以,文件IO操作fd的范围是3~1023
man手册
终端输入:
man man 查看man手册
man 1 查看可执行文件和shell
man 2 查看系统调用 如: man 2 open
man 3 查看库函数(库调用)
4.文件的打开与关闭
open接口函数:
终端:man 2 open
头文件、函数原型、描述
int open(const char *pathname, int flags);
int open(const char *pathname, int flags,mode_t mode);
close接口函数:
man 2 close
#include <unistd.h>
int close(int fd); 关闭成功返回值0,关闭失败返回值1
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { int fd = -1; #if 1 if((fd = open("helloworld.txt",O_RDONLY)) < 0) { printf("open error!\n"); return -1; } printf("open success! fd :%d\n",fd); #else if((fd = open("4.txt",(O_RDONLY|O_CREAT|O_EXCL),0664)) < 0) { printf("open error!\n"); return -1; } printf("open success! fd :%d\n",fd); #endif if(!close(fd)) printf("close success!\n"); return 0; }umask设定权限
ulimit获取文件打开范围
5.文件的读写
read接口函数
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #define N 20 int main(int argc, const char *argv[]) { int fd = -1,n = 0,total = 0; char buf[N] = {0}; if(argc < 2){return -1; } //if((fd = open("helloworld.txt",O_RDONLY)) == EOF ) if((fd = open(argv[1],O_RDONLY)) == EOF ) { printf("open error!\n"); return -1; } printf("open success! fd :%d\n",fd); while((n = read(fd,buf,N)) > 0){ 读一次,返回正数证明后面还有内容,光标此时在最后 printf("%s",buf); total += n; } printf("read characters: %d\n",total); printf("finished reading \n"); if(!close(fd)) printf("close success!\n"); return 0; }write接口函数
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <string.h> #define N 20 int main(int argc, const char *argv[]) { int fd = -1,n = 0; char buf1[N] = {0}; char buf2[N] = "hello wolrd!!!"; if(argc < 2){return -1; } #if 0 if((fd = open(argv[1],O_RDWR)) == EOF ) { printf("open error!\n"); return -1; } printf("open success! fd :%d\n",fd); #elif 0 if((fd = open(argv[1],O_RDONLY)) == EOF ) { printf("open error!\n"); return -1; } printf("open success! fd :%d\n",fd); #else if((fd = open(argv[1],O_WRONLY)) == EOF ) { printf("open error!\n"); return -1; } printf("open success! fd :%d\n",fd); #endif //读操作 if(( n = read(fd,buf1,N)) < 0){ printf("read error"); return -1; } printf("read success\n"); printf("read characters: %s\n",buf1); //写操作 do{ n = write(fd,buf2,strlen(buf2)); }while(n < 0); printf("write characters :%d\n",n); printf("write finished\n"); if(!close(fd)) printf("close success!\n"); return 0; }6.文件IO:定位文件与访问权限修改
lseek接口函数
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #define N 20 #include <string.h> int main(int argc, const char *argv[]) { int fd = -1; int ret = 0; char buf[N] = {0}; if((fd = open("test.txt",O_RDWR)) < 0) { printf("open error!\n"); return -1; } printf("open success! fd :%d\n",fd); ret = lseek(fd,0,SEEK_CUR); printf("%u\n",ret); read(fd,buf,10); printf("%s\n",buf); ret = lseek(fd,0,SEEK_CUR); printf("%u\n",ret); lseek(fd,0,SEEK_END); write(fd,"aaa", strlen("aaa")); ret = lseek(fd,0,SEEK_CUR); printf("%u\n",ret); ret = lseek(fd,0,SEEK_SET); printf("%u\n",ret); bzero(buf,sizeof(buf)); while(read(fd,buf,sizeof(buf)) > 0){ printf("%s",buf); } putchar('\n'); printf("read end\n"); if(!close(fd)) printf("close success!\n"); return 0; }bzero函数,属于库函数里 man 3,作用:清除buf里的内容
chmod/fchmod接口函数
被改成只写,所以后面打开文件以读写权限时会报错
open实在文件原本权限基础上打开,不能超越文件原本权限。
7.标准IO基础
标准IO是在封装系统调用文件IO的基础上,加了缓存,减少了系统调用的次数,降低了与硬件内核之间的硬耦合。
文件IO用文件描述符fd来存文件信息。
标准IO用结构体来存文件信息,这个结构体叫流(FILE),同时加一个缓存区。
无缓存:
行缓存:
读:标准IO读缓存区放满了,才会刷新,去调用系统调用文件IO的read接口函数
写:标准IO写缓存区放满了,才会刷新,去调用系统调用文件IO的write接口函数
两个标准IO函数:
fopen:打开一个文件,并生成FILE结构
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { FILE *fp = fopen("a.txt","a+"); //fprintf(fp,"a"); printf("%lu\n",fp->_IO_buf_end - fp->_IO_buf_base); int i = 0; do{ fprintf(fp,"a"); i++; }while(i<4098); printf("%lu",fp->_IO_write_ptr - fp->_IO_write_base); sleep(5); return 0; }setbuf修改缓存类型函数
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { printf("a"); sleep(5); setbuf(stdout,NULL); printf("b"); sleep(5); return 0; }8.标准IO:文件打开、关闭、错误处理
fopen接口函数
#include <stdio.h> int main(int argc, const char *argv[]) { FILE * fp = NULL; if((fp = fopen("test.txt","a+")) == NULL){ printf("fopen error\n"); return -1; } printf("fopen success\n"); fclose(fp); return 0; }fclose接口函数
errno、perror、strerror错误信息处理函数
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> int main(int argc, const char *argv[]) { FILE * fp = NULL; #if 0 if( (fp = fopen("test.txt","r")) == NULL ){ printf("errno:%d\n",errno); perror("fopen:"); return -1; } #else if( (fp = fopen("test.txt","r")) == NULL ){ printf("errno:%d\n",errno); printf("fopen:%s\n",strerror(errno)); return -1; } #endif printf("errno:%d\n",errno); perror("perror:"); sleep(3); fclose(fp); return 0; }9.标准IO操作:字符行、读写
fgetc、getc、getchar字符输入接口函数
#include <stdio.h> int main(int argc, const char *argv[]) { int c; FILE *fp = NULL; if((fp = fopen("test.txt","r+")) == NULL ){ perror("fopen"); return -1; } printf("fopen success\n"); while(1){ #if 0 if( (c = fgetc(fp)) == EOF ){ perror("fgetc"); break; } #else if( (c = getc(fp)) == EOF ){ perror("getc"); break; } #endif printf("%c",(char)c); } fclose(fp); return 0; }fputc、putc、putchar字符输出接口函数
#include <stdio.h> int main(int argc, const char *argv[]) { int c; FILE *fp = NULL; if((fp = fopen("test.txt","a+")) == NULL ){ perror("fopen"); return -1; } printf("fopen success\n"); #if 1 if( (c = fputc('a',fp)) == EOF ){ perror("fputc"); return -1; } #elif 0 if( (c = putc('a',fp)) == EOF ){ perror("putc"); return -1; } #else putchar('b'); #endif printf("c = %c\n",c); fclose(fp); return 0; }实践:字符读写复制文件
#include <stdio.h> int main(int argc, const char *argv[]) { FILE * fp_a = NULL,* fp_b = NULL; int c = 0; if(argc < 3 ){ printf("Usage: %s <be copied file> <copied file>\n",argv[0]); return -1; } if((fp_a = fopen(argv[1],"r+")) == NULL){ perror("be copied file fopen"); return -1; } if((fp_b = fopen(argv[2],"w+")) == NULL){ perror("copied file fopen"); fclose(fp_a); return -1; } printf("fopen files ok\n"); do{ if((c = fgetc(fp_a)) == EOF){ perror("fgetc"); break; } if((fputc(c,fp_b)) == EOF){ perror("fputc"); break; } }while(1); fclose(fp_a); fclose(fp_b); return 0; }fgets行输入接口函数:
#include <stdio.h> #define N 32 #define M 6 int main(int argc, const char *argv[]) { FILE * fp = NULL; char buf[M] = {0}; if((fp = fopen("test.txt","r+")) == NULL){ perror("fopen"); return -1; } while(1){ if(fgets(buf,M,fp) == NULL){ perror("fgets"); break; } printf("test look:%s",buf); } printf("fgets from file ok\n"); fclose(fp); return 0; }fputs/puts行输出接口函数
#include <stdio.h> #define N 32 int main(int argc, const char *argv[]) { FILE * fp = NULL; char buf[N] = {0}; if((fp = fopen("test.txt","a+")) == NULL ){ perror("fopen"); return -1; } if(fgets(buf,N,stdin) == NULL){ perror("fgets"); fclose(fp); return -1; } if(fputs(buf, fp) == EOF){ perror("fputs"); fclose(fp); return -1; } fclose(fp); return 0; }实践:统计文本行
#include <stdio.h> #include <string.h> #define N 32 int main(int argc, const char *argv[]) { FILE * fp = NULL; int cout = 0; char buf[N] = {0}; if((fp = fopen("test.txt","a+")) == NULL){ perror("fopen"); return -1; } while(1){ if(fgets(buf,N,fp) == NULL){ perror("fgets"); break; } if(buf[strlen(buf)-1] == '\n'){ cout++; } } printf("cout = %d\n",cout); fclose(fp); return 0; }10.标准IO:文件的二进制读写与对象读写
fread、fwrite二进制读写接口函数
fread接口函数:
fwrite接口函数:
fflush函数:刷新,立即将缓冲区内容交给内核
#include <stdio.h> #include <unistd.h> typedef struct note { int id; char name[8]; float score; }STU; int main(int argc, const char *argv[]) { FILE * fp = NULL; if((fp = fopen("test.txt","a+")) < 0){ perror("perror"); return -1; } STU s[2] = {{1,"wang",98},{2,"li", 95}}; int len = sizeof(STU); if(fwrite(&s[0],len,1,fp) < 0){ perror("fwrite"); return -1; } fflush(fp); sleep(5); if(fwrite(&s[1],len,1,fp) < 0){ perror("fwrite"); return -1; } fclose(fp); if((fp = fopen("test.txt","r+")) < 0){ perror("perror"); return -1; } STU ret; fread(&ret, len,1,fp); printf("%d,%s,%f\n",ret.id,ret.name,ret.score); fclose(fp); return 0; }bzero清空函数
bzero(buf,sizeof(buf));将数组buf的sizeof(buf)个成员变成'\0'
feof判断结尾函数
feof(流) 返回值为非零时,文件到结尾了。返回值为0时,文件没到结尾。
实践:二进制读写复制文件
#include <stdio.h> #include <string.h> #define N 512 int main(int argc, const char *argv[]) { FILE *fp_r = NULL,*fp_w = NULL; if(argc < 3){ printf("Usage :%s <source file><destination file>\n",argv[0]); return -1; } if((fp_r = fopen(argv[1],"r")) == NULL){ printf("fopen source file failed\n"); return -1; } if((fp_w = fopen(argv[2],"a+")) == NULL){ printf("fopen destination file failed\n"); return -1; } char buf[N]; size_t bytes_read,bytes_write; while(1){ if(feof(fp_r)){ printf("read file in last line\n"); break; } if((bytes_read = fread(buf,1,sizeof(buf), fp_r)) < 0){ printf("fread failed\n"); goto END; } if((bytes_write = fwrite(buf,1,bytes_read, fp_w)) < 0){ printf("fread failed\n"); goto END; } if(bytes_read != bytes_write){ printf("failed to write to destination file\n"); goto END; } bzero(buf,sizeof(buf)); } printf("file copied successfully from '%s' to '%s'\n",argv[1],argv[2]); END: fclose(fp_r); fclose(fp_w); return 0; }fscanf、sscanf、fprintf、sprintf 格式化输入、输出函数
11.标准IO操作、流的刷新与定位
fflush流的刷新
ftell、rewind、fseek流的定位函数
实践:获取文本大小
12.目录操作
opendir、fdopendir打开目录函数
closedir关闭目录函数
dirent访问目录函数
实践:获取文件夹中的内容
stat 、lstat、fstat 获取文件属性函数
属于 man 2 stat
实践:获取文件信息
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <time.h> int main(int argc, const char *argv[]) { if(argc < 2){ printf("Usage :%s <filename>\n",argv[1]); return -1; } struct stat st; #if 1 if(stat(argv[1],&st) == -1){ perror("stat"); return -1; } #elif 0 if(lstat(argv[1],&st) == -1){ perror("lstat"); return -1; } printf("file size:%ld\n",st.st_size); #else int fd = open(argv[1],O_RDONLY); if(fd < 0){ perror("open"); return -1; } fstat(fd,&st); printf("ffile size:%ld\n",st.st_size); #endif switch(S_IFMT&st.st_mode){ case S_IFREG: putchar('_'); break; case S_IFDIR: putchar('d'); break; case S_IFSOCK: putchar('s'); break; case S_IFLNK: putchar('l'); break; case S_IFBLK: putchar('b'); break; case S_IFCHR: putchar('C'); break; case S_IFIFO: putchar('f'); break; default: printf("have afailed\n"); return -1; } int n = 8; do{ if(st.st_mode & (0x1<<n)){ switch(n%3){ case 0: putchar('x'); break; case 1: putchar('w'); break; case 2: putchar('r'); break; } }else{ putchar('-'); } }while(n--); printf(" file size:%ld\t",st.st_size); printf("\t%s",ctime(&st.st_ctime)); return 0; }