文章目录
- 一、Linux内核中错误码
- 1.1 错误码的概念
- 1.2 在应用程序中获取错误码
- 1.3 错误转化错误信息(strerror函数)
- 1.4 直接打印错误信息(perror函数)
- 1.5 将错误打印封装宏放到.h文件中
- 二、缓冲区
- 2.1 为什么使用缓冲区机制
- 2.2 缓冲区的类型及其大小
- 2.3 缓冲区的刷新时机
- 2.3.1 行缓存的刷新时机
- 2.3.2 全缓存的刷新时机
- 三、fflush函数
一、Linux内核中错误码
1.1 错误码的概念
在Linux内核中有4K(4096)个错误码
当出现错误时返回给上层的是负值,定义的正数,返回时是负值。
也就是相当于return -ENOENT;
上层进行拿错误码的时候拿的是正值,会将负号强制去掉。
那上层是怎么拿到这个值的呢?
#include <errno.h>
extern int errno
直接从errno
中拿到错误码的值
1.2 在应用程序中获取错误码
#include <stdio.h>
#include <errno.h>
int main(int argc,const char * argv[])
{
FILE *fp;
fp=fopen("./hello.txt","r");
if(fp==NULL)
{
printf("errno=%d\n",errno);
return -1;
}
return 0;
}
结果展示:
应用层的工程师拿到2这个错误码之后其实并不知道是因为什么原因错误了,所以需要调用函数将这个错误码转化为错误信息,才能够看的懂。
1.3 错误转化错误信息(strerror函数)
strerror
就是将错误码转换为错误信息字符串
#include <string.h>
char *strerror(int errnum);
功能:将错误码转化为错误信息
参数:
@errnum:错误码
返回值:错误信息的字符串
代码示例:
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc,const char * argv[])
{
FILE *fp;
fp=fopen("./hello.txt","r");
if(fp==NULL)
{
printf("errno=%d\n",errno);
printf("%s\n",strerror(errno));
return -1;
}
return 0;
}
结果展示:
1.4 直接打印错误信息(perror函数)
#include <stdio.h>
void perror(const char *s);
功能:直接输出错误信息
参数:
@s:你给出错时候附加的信息
返回值:无
代码示例:
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc,const char * argv[])
{
FILE *fp;
/*fp=fopen("./hello.txt","r");
if(fp==NULL)
{
perror("打开文件失败");
return -1;
} 优化代码*/
if((fp=fopen("./hello.txt","r"))==NULL)
{
perror("打开文件失败了");
}
return 0;
}
继续优化
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char *argv[])
{
FILE *fp;
if ((fp = fopen("./hello.txt", "r")) == NULL)
PRINT_ERR("打开文件失败");
return 0;
}
结果展示:
1.5 将错误打印封装宏放到.h文件中
head.h
#ifndef __HEAD_H__
#define __HEAD_H__
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define PRINT_ERR(msg) \
do \
{ \
printf("%s %s %d\n",__FILE__,__func__,__LINE__); \
perror(msg); \
return -1; \
} while (0)
#endif
打印文件名,函数,行数printf("%s %s %d\n",__FILE__,__func__,__LINE__);
我们将head.h文件直接放到系统头文件目录下
执行命令将head.h放到/usr/include/目录下
cd /usr/include/
sudo mv head.h的路径
结果展示:
二、缓冲区
标准IO中包含缓冲区,文件IO是不包含缓冲区的。
2.1 为什么使用缓冲区机制
减少CPU对磁盘的读写次数;CPU读取磁盘中的数据并不是直接读取磁盘,而是先将磁盘的内容读入到内存,也就是缓冲区,然后CPU对缓冲区进行读取,进而操作数据;计算机对缓冲区的操作时间远远小于对磁盘的操作时间,大大的加快了运行速度。
2.2 缓冲区的类型及其大小
标准I/O库提供缓冲的目的是尽可能减少使用read和write调用的次数。它也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。遗憾的是,标准I/O库最令人迷惑的也是它的缓冲。
缓冲区有三类
全缓存:和文件相关的操作就是全缓存(fp),大小是4096
行缓存:和终端相关的操作是行缓存(stdin、stdout),大小是1024
无缓存:标准出错stderr,大小是0
代码验证:
#include <head.h>
int main(int argc, const char *argv[])
{
char h;
//行缓存
scanf("%c",&h);
printf("stdin buffer size:%ld\n",stdin->_IO_buf_end-stdin->_IO_buf_base);
printf("stdout buffer size:%ld\n",stdout->_IO_buf_end-stdout->_IO_buf_base);
//无缓存
fputs("666",stderr);
printf("stderr buffer size:%ld\n",stderr->_IO_buf_end-stderr->_IO_buf_base);
//全缓存
FILE *fp;
if ((fp = fopen("./hello.txt", "w")) == NULL)
PRINT_ERR("打开文件失败");
fputc('1',fp);
printf("fp buffer size:%ld\n",fp->_IO_buf_end-fp->_IO_buf_base);
return 0;
}
结果展示:
总结:
- 如果在一个程序中不使用
stdin
,它的缓冲区的大小就是0。
如果使用stdin
,此时验证的大小就是1024(1k)。 - 如果在一个程序中不使用
stdout
,它的缓冲区的大小就是0。
如果使用stdout
,此时验证的大小就是1024(1k)。 -
stderr
打印的1只是标识有没有使用stderr
,大小是0。 - 全缓存的缓冲区的大小是4096(4K)。
2.3 缓冲区的刷新时机
2.3.1 行缓存的刷新时机
#include <stdio.h>
int main(int argc, const char *argv[])
{
//printf("hello world");//程序结束刷新行缓冲区
//printf("hello world\n");//行缓存遇见换行时刷新缓冲区
//while(1);
//printf("hello world");//当使用fflush的时候,会刷新行缓冲区
//fflush(stdout);
//while(1);
//printf("hello world");//当关闭文件指针的时候,会刷新行缓冲区
//fclose(stdout);
//while(1);
//当行缓存满也会刷新行缓冲区
//for(int i=0;i<1025;i++){
// fputc('a',stdout);
//}
//while(1);
//当输入输出转换时刷新行缓冲区
printf("hello world");
char ch;
scanf("%c",&ch);
while(1);
return 0;
}
总结:
- 当程序结束的时候会刷新行缓存
- 当行缓存遇到换行符的时候会刷新缓冲区
- 当使用
fflush
的时候,会刷新行缓冲区 - 当关闭文件指针的时候也会刷新行缓冲区
- 当行缓存满(1024)也会刷新缓冲区
- 当输入和输出发生切换的时候也会刷新行缓冲区
2.3.2 全缓存的刷新时机
#include <head.h>
int main(int argc, const char *argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "w+")) == NULL)
PRINT_ERR("fopen error");
//fputs("hello world\n",fp);//全缓存遇见换行时不能刷新缓冲区
//while(1);
//fputs("hello world\n",fp);//程序结束刷新全缓冲区
//fputs("hello world1\n",fp);//当使用fflush的时候,会刷新全缓冲区
//fflush(fp);
//while(1);
//fputs("hello world2\n",fp);//当关闭文件指针的时候,会刷新全缓冲区
//fclose(fp);
//while(1);
//当全缓存满会刷新全缓冲区
//for(int i=0;i<4097;i++){
// fputc('a',fp);
//}
//while(1);
//当输入输出转换时刷新全缓冲区
char ch;
fputs("hello world3\n",fp);
fgetc(fp);
while(1);
return 0;
}
总结:
- 程序结束会刷新全缓存
- 关闭文件也会刷新全缓存
-
fflush
也会刷新全缓存 - 当缓冲区满(4906)会刷新全缓存
- 输入输出发生切换的时候也会刷新全缓冲区
三、fflush函数
fflush
是一个在C语言标准输入输出库中的函数,功能是冲洗流中的信息,该函数通常用于处理磁盘文件。fflush()
会强迫将缓冲区内的数据写回参数stream 指定的文件中。
函数名: fflush
功 能: 清除读写缓冲区,在需要立即把输出缓冲区的数据进行物理写入时
头文件:stdio.h
原型:int fflush(FILE *stream)
其中stream是要冲洗的文件指针
fflush(stdin);
刷新标准输入缓冲区,把输入缓冲区里的东西丢弃。(非标准)fflush(stdout);
刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上。
注:
C和C++的标准里从来没有定义过 fflush(stdin)。也许有人会说:“可是我用 flush(stdin) 解决了这个问题,你怎么能说是错的呢?”的确,某些编译器(如VC6)支持用 fflush(stdin) 来清空输入缓冲,但是并非所有编译器都要支持这个功能,因为标准中根本没有定义fflush(stdin)。