精华内容
下载资源
问答
  • 缓冲

    千次阅读 2019-10-21 20:53:17
    计算机中的缓冲区: 缓冲器为暂时置放输出或输入资料的内存。 缓冲器内资料自储存设备(如硬盘)来,放置在缓冲器中,须待机送至CPU或其他运算设备。 缓冲区(buffer)这个中文译意源自当计算机的高速部件与低速...

    计算机中的缓冲区:

    1. 缓冲器为暂时置放输出或输入资料的内存。
    2. 缓冲器内资料自储存设备(如硬盘)来,放置在缓冲器中,须待机送至CPU或其他运算设备。
    3. 缓冲区(buffer)这个中文译意源自当计算机的高速部件与低速部件通讯时,必须将高速部件的输出暂存到某处,以保证高速部件与低速部件相吻合. 后来这个意思被扩展了,成为"临时存贮区"的意思。

    当然,上面是计算机上的缓冲区,总的来说计算机中的缓冲区就是为了CPU在硬盘上的存取时速度的适配,存放常使用的输入输出数据,在下次存取时缩短时间,不至于浪费CPU。(硬盘速度慢,CPU快,比如内存和高速缓冲器)。

    从这个当中我们可以得到一些启示:在某些我们需要多次访问某些数据的时候可以建造一个缓冲区来存放。

     

    在MVC模式中:


    1. dao层主要做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此。
    2. service层主要负责业务模块的应用逻辑应用设计。
    3. controller层负责具体的业务模块流程的控制,在此层要调用service层的接口来控制业务流程。针对具体的业务流程,会有不同的控制器。

    他们之间的关系是:Service层是建立在DAO层之上的,建立了DAO层后才可以建立Service层,而Service层又是在Controller层之下的,因而Service层应该既调用DAO层的接口,又要提供接口给Controller层的类来进行调用,它刚好处于一个中间层的位置。每个模型都有一个Service接口,每个接口分别封装各自的业务处理方法。


    比如给出下面这样一个例子:

    1. 给一个数据库放置一个表,放置学生的信息。
    2. 建立Dao层,Dao层负责建立与数据库存取学生信息关系。
    3. 建立Service层,service层负责处理取得到的学生信息或者处理存放的学生信息。

    此时若一个页面想要登录取得自己所有信息,则会通过controller层去调用service层的方法去验证密码的正确性,以及密码对或错的处理。此时的数据库里面学生的密码应通过dao层取得(访问数据的时间过久)。但是若是这个用户取得学生信息的次数很多次呢,我们可以利用缓冲区的想法,在Service层建立一个Pool = HashMap<>();我们可以定义一个Count,访问一次Count加一,当超过一定的次数的时候,用Pool来存放这个用户的ID和Password,以后每次来查询时先去pool里面查询,查不到再去数据库访问取得。到此时又出现了两个问题:

    1. 若此时这位用户不停地点击查询,会给服务器增加负担。
    2. 若这位用户更改了数据(密码),如何去更新缓存中的数据。

    对于第一个问题:我们可以给一个lastTime来判断上一次的时间,若检查出时间差距过小便不予理会或者通知客户。

    对于第二个问题:我们可以在用户更改数据的时候通知Service层去在更改后重新读取更改后的数据来更改Pool。

    而第二个问题又衍生出了一个问题,但是如果服务器端采用的是分布式服务器呢,可能在这个服务器上登录一次,存储数据在这个服务器上,在另一个服务器上登录一次,存储数据在这个服务器上,但是当更改数据后只更改了一个服务器上的,另一个服务器上数据不能同步。此时原谅小编还未到火候PO上链接

    分布式系统数据一致性的6种方案

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • C++对缓冲区的理解

    千次阅读 2017-08-20 14:01:51
    什么是缓冲缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。 缓冲区根据其对应的是输入设备还是...

    什么是缓冲区
    缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。
    缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

    为什么要引入缓冲区
    我们为什么要引入缓冲区呢?
    比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。
    又比如,我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的CPU可以处理别的事情。
    现在您基本明白了吧,缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。

    缓冲区的类型
    缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。
    1、全缓冲
    在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
    2、行缓冲
    在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
    3、不带缓冲
    也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
    缓冲区的刷新
    下列情况会引发缓冲区的刷新:
    1、缓冲区满时;
    2、执行flush语句;
    3、执行endl语句;
    4、关闭文件。
    可见,缓冲区满或关闭文件时都会刷新缓冲区,进行真正的I/O操作。另外,在C++中,我们可以使用flush函数来刷新缓冲区(执行I/O操作并清空缓冲区),如:
    cout<<flush; //将显存的内容立即输出到显示器上进行显示

    endl控制符的作用是将光标移动到输出设备中下一行开头处,并且清空缓冲区。
    cout<<endl;
    相当于
    cout<<”/n” <<flush;

    通过实例演示说明

    1、文件操作演示全缓冲
    创建一个控制台工程,输入如下代码:

    #include <fstream>
    using namespace std;
    
    int main()
    {
        //创建文件test.txt并打开
    	ofstream outfile("test.txt");
    
        //向test.txt文件中写入4096个字符’a’
    	for(int n=0;n<4096;n++)
    	{
    		outfile<<'a';
    	}
        //暂停,按任意键继续
    	system("PAUSE");
        
        //继续向test.txt文件中写入字符’b’,也就是说,第4097个字符是’b’
    	outfile<<'b';
    
        //暂停,按任意键继续
    	system("PAUSE");
    
    	return 0;
    }

    上面这段代码很容易理解,已经在代码内部作了注释。
    编写这段小代码的目的是验证WindowsXP下全缓冲的大小是4096个字节,并验证缓冲区满后会刷新缓冲区,执行真正的I/O操作。

    编译并执行,运行结果如下:

    此时打开工程所在文件夹下的test.txt文件,您会发现该文件是空的,这说明4096个字符“a”还在缓冲区,并没有真正执行I/O操作。敲一下回车键,窗口变为如下:

    此时再打开test.txt文件,您就会发下该文件中已经有了4096个字符“a”。这说明全缓冲区的大小是4K(4096),缓冲区满后执行了I/O操作,而字符“b”还在缓冲区。
    再次敲一下回车键,窗口变为如下:

    此时再打开test.txt文件,您就会发现字符“b”也在其中了。这一步验证了文件关闭时刷新了缓冲区。

    2、键盘操作演示行缓冲
    先介绍getchar()函数。
    函数原型:int getchar(void);
    说明:当程序调用getchar()函数时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar()函数才开始从键盘缓冲区中每次读入一个字符。也就是说,后续的getchar()函数调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才重新等待用户按键。
    不知道您明白了没有,再通俗一点讲,当程序调用getchar()函数时,程序就等着用户按键,并等用户按下回车键返回。期间按下的字符存放在缓冲区,第一个字符作为函数返回值。继续调用getchar()函数,将不再等用户按键,而是返回您刚才输入的第2个字符;继续调用,返回第3个字符,直到缓冲区中的字符读完后,才等待用户按键。
    如果您还没有明白,只能怨我表达能力有限,您可以结合以下实例体会。

    创建一个控制台工程,输入如下代码:

    #include <iostream>
    using namespace std;
    
    int main()
    {
    
    	char c;
    
    //第一次调用getchar()函数
    //程序执行时,您可以输入一串字符并按下回车键,按下回车键后该函数才返回
    	c=getchar();
    
        //显示getchar()函数的返回值
    	cout<<c<<endl;
    
        //暂停
    	system("PAUSE");
     
    //循环多次调用getchar()函数
    //将每次调用getchar()函数的返回值显示出来
    //直到遇到回车符才结束
    	while((c=getchar())!='/n')
    	{
    		printf("%c",c);
    	}
    
        //暂停
    	system("PAUSE");
    
    	return 0;
    }

    这段小代码也很简单,同样在代码内部都有注释。
    getchar()函数的执行就是采用了行缓冲。第一次调用getchar()函数,会让程序使用者(用户)输入一行字符并直至按下回车键 函数才返回。此时用户输入的字符和回车符都存放在行缓冲区。
    再次调用getchar()函数,会逐步输出行缓冲区的内容。
    好了,本人表达能力有限,还是编译运行程序,通过运行结果自己领会吧。

    编译运行程序,会提示您输入字符,您可以交替按下一些字符,如下:
     
    您一直按下去,您就会发现当您按到第4094个字符时,不允许您继续输入字符。这说明行缓冲区的大小也是4K。
    此时您按下回车键,返回第一个字符’a’,如下图:

    继续敲一下回车键,将缓冲区的其它的字符全部输出,如下图:

    3、标准错误输出不带缓冲
    如错误输出时使用:

    cerr<<”错误,请检查输入的参数!”;

    这条语句等效于:
    fprintf(stderr, ”错误,请检查输入的参数!”);

    好了,就说到这吧,祝您好运,希望能对您有所帮助。

    展开全文
  • 想实现一个播放器,网络不好的情况下播放~ 会显示正在缓冲字样~
  • C++编程对缓冲区的理解

    千次阅读 2013-11-02 17:50:15
    什么是缓冲区  缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。   缓冲区根据其对应的是...
    什么是缓冲区
    

        缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。  

          缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

     

    为什么要引入缓冲区

      比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。

      又比如,我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的CPU可以处理别的事情。

      现在您基本明白了吧,缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。

     

    缓冲区的类型

      缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。

      1、全缓冲

           在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。

      2、行缓冲

           在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。

      3、不带缓冲

           也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。

     

    缓冲区的刷新

      下列情况会引发缓冲区的刷新:

      1、缓冲区满时;

      2、执行flush语句;

      3、执行endl语句;

      4、关闭文件。

     

      可见,缓冲区满或关闭文件时都会刷新缓冲区,进行真正的I/O操作。另外,在C++中,我们可以使用flush函数来刷新缓冲区(执行I/O操作并清空缓冲区),如:

      cout<<flush ; // 将显存的内容立即输出到显示器上进行显示

      

      cout<<endl ; // endl控制符的作用是将光标移动到输出设备中下一行开头处,并且清空缓冲区

      相当于

      cout<<”\n” <<flush ;

     

      通过实例演示说明

      1、文件操作演示全缓冲

      创建一个控制台工程,输入如下代码:

    #include <fstream> 
    using namespace std; 
    
    int main() 
    { 
        //创建文件test.txt并打开 
       ofstream outfile("test.txt"); 
    
        //向test.txt文件中写入4096个字符’a’ 
       for(int n=0; n < 4096; n++) 
        { 
            outfile << 'a'; 
        }
    
        //暂停,按任意键继续 
       system("PAUSE"); 
    
        //继续向test.txt文件中写入字符’b’,也就是说,第4097个字符是’b’ 
       outfile << 'b'; 
    
        //暂停,按任意键继续 
       system("PAUSE"); 
    
      return 0; 
    }


    上面这段代码很容易理解,已经在代码内部作了注释。

      编写这段小代码的目的是验证WindowsXP下全缓冲的大小是4096个字节,并验证缓冲区满后会刷新缓冲区,执行真正的I/O操作。

     

         编译并执行,运行结果如下:

         C++编程对缓冲区的理解


    此时打开工程所在文件夹下的test.txt文件,您会发现该文件是空的,这说明4096个字符“a”还在缓冲区,并没有真正执行I/O操作。敲一下回车键,窗口变为如下:

         C++编程对缓冲区的理解

      此时再打开test.txt文件,您就会发下该文件中已经有了4096个字符“a”。这说明全缓冲区的大小是4K(4096),缓冲区满后执行了I/O操作,而字符“b”还在缓冲区。

      再次敲一下回车键,窗口变为如下:

         C++编程对缓冲区的理解

      此时再打开test.txt文件,您就会发现字符“b”也在其中了。这一步验证了文件关闭时刷新了缓冲区。

      2、键盘操作演示行缓冲

      先介绍getchar()函数。

      函数原型:int getchar(void) ;

      说明:当程序调用getchar()函数时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar()函数才开始从键盘缓冲区中每次读入一个字符。也就是说,后续的getchar()函数调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才重新等待用户按键。

     

      不知道您明白了没有,再通俗一点讲,当程序调用getchar()函数时,程序就等着用户按键,并等用户按下回车键返回。期间按下的字符存放在缓冲区,第一个字符作为函数返回值。继续调用getchar()函数,将不再等用户按键,而是返回您刚才输入的第2个字符;继续调用,返回第3个字符,直到缓冲区中的字符读完后,才等待用户按键。

      如果您还没有明白,只能怨我表达能力有限,您可以结合以下实例体会。

      创建一个控制台工程,输入如下代码:

    #include<iostream> 
    using namespace std; 
    
    
    
    int main() 
    {  
    
      char c;  
        
    
        //第一次调用getchar()函数,程序执行时,您可以输入一串字符并按下回车键,按下回车键后该函数返回。返回值是用户输入的第一个字符 (假设用户输入了 abcdef,函数返回a)
       c = getchar(); 
    
        //显示getchar()函数的返回值
       cout<< c << endl; // 输出 a  
    
    
        // 循环多次调用getchar()函数,将每次调用getchar()函数的返回值显示出来,直到遇到回车符才结束。 这时函数执行不会让用户输入而是顺序读取缓冲区字符内容。第一个字符用户输入结束后已经读取,所以会从第二个字符开始读
       while((c = getchar())!='\n') 
       { 
           cout<< "," << c <<endl 
       } 
    
       return 0;
    }

     这段小代码也很简单,同样在代码内部都有注释。最后输出结果是

          a

          ,b

          ,c

          ,d

          ,e

          ,f 

      

      getchar()函数的执行就是采用了行缓冲。第一次调用getchar()函数,会让程序使用者(用户)输入一行字符并直至按下回车键 函数才返回。此时用户输入的字符和回车符都存放在行缓冲区。

      再次调用getchar()函数,会逐步输出行缓冲区的内容。

     

      3、标准错误输出不带缓冲

      如错误输出时使用:

      cerr<<”错误,请检查输入的参数!” ;

      这条语句等效于:

      fprintf(stderr, ”错误,请检查输入的参数!”) ;

     

         看完了缓冲区内容我们来学习C++标准IO库。

     

    8.1 面向对象的标注库

        先来看IO操作类图:

        

          

         IO库大致可操作三类数据: 控制台流(stream) , 文件(file) , 字符串 (string)。

         操作类型又可分三类:输入(in), 输出(out) ,输入与输出(in/out)。

     

         ostream 是所有类型输出操作的基类

             它扩展出两个子类: ofstream针对文件的输出操作类;ostringstream针对string的输出操作类

     

         istream 是所有类型输入操作的基类

             它扩展出两个子类: ifstream针对文件的输入操作类;istringstream针对string的输入操作类

     

         ostream 和 istream共同扩展出类 iostream 它负责处理控制台stream的输入和输出

             iostream 扩展出两个子类:stringstream专门处理string的输入输出; fstream专门处理文件的输入输出

     

         所有类定义在不同头文件,正确使用类必须引入对应的头文件

             iosteam头文件中定义了
                  istream(从流中读取);
                  ostream(写到流中);
                  iostream(对流进行读写,从istream和osteam派生而来)


             fsteam头文件中定义了
                  ifstream(从文件中读取,由istream派生而来);
                  ofstream(写到文件中去,由ostream派生而来);
                  fstream(对文件进行读写,由iostream派生而来);


             ssteam头文件中定义了: 
                  istringstream(从string对象中读取,由istream派生而来);
                  ostringstream(写到string对象中去,由ostream派生而来);
                  stringstream(对string对象进行读写,由iostream派生而来);

        

         要想正确使用IO类一定要理解他们的对应关系和每个类的职责。 

     

        c++的标准输入输出库iostream 是一个类库,以类的形式组织,使用该库中的类要先引用命名空间:using std;
        最常使用的是cin和cout,这两个都是对象,cin是istream类的对象,cout是ostream类的对象,而输入的 cin>> 与输出时的 cout<< 中的左移 << 与右移 >> 分别是 istream 类与 ostream 类的操作符重载。

        iostream库里面创建了3个标准流对象:
          cin 表示标准输入的istream对象,cin可以使我们从设备读取数据。
          cout 表示标准输出的ostream对象,cout可以使我们向设备写入数据。
          cerr 表示标准错误的ostream对象,cerr是导出程序错误消息的地方,只能向屏幕设备写数据。

        标准的流对象都有默认的设备:
        cout << data ;  cout默认的设备是显示器缓冲区。
        cin >> data ;  cin默认的设备是键盘缓冲区。

         不管是输出到屏幕还是写入string或者保存到txt文本,字符都是不可获取的。通常我们不仅仅使用英文标准字符,我们还可能输入输出中文字符或其他非英文字符。这时候需要国际字符支持。

         例如我们要保存一个字符 'a' 可以定义 char, 但是我们要保存字符 '家' 就无法按使用char 而要使用 wchar_t 了。IO类也有这样的区分,例如我们要在控制台输出中文字符就只能用 wcout << "你好" << endl想要将中文保存到文本就要用wofstream 或 wfstream 。要正确读取包含中文字符文件要使用 wifstream 类。

         

         IO对象无法复制或者赋值,所以io对象作为函数形参或返回值时只能使用指针或引用。

     

         iostream &Getio(iostream &io, fstream *fs){ ...} // 正确,参数和返回以引用或指针形式传递

         iostream Getio(iostream io){ ...}  // 错误,参数和返回以拷贝方式传递会发生复制和赋值操作

     

    8.2 条件状态

        IO对象在任意时候都对应一种状态:比如有效状态(还未处理或者正确处理完毕时的状态),比如失败状态(处理失败时),比如数据流被破坏(文件错误)等等。看下面的表

     

        strm::iostate     // 机器相关的整型名,由各个iostream类定义,用于定义条件状态
        strm::badbit      // strm::iostate类型的值,用于指出被破坏的流
        strm::failbit       // strm::iostate类型的值,用于指出失败的IO操作
        strm::eofbit       // strm::iostate类型的值,用于指出流已经到达文件结束符
        s.eof()               // 如果设置了流s的eofbit值,则该函数返回true
        s.fail()               // 如果设置了流s的failbit值,则该函数返回true
        s.bad()              // 如果设置了流s的badbit值,则该函数返回true
        s.good()            // 如果流s处于有效状态,则该函数返回true
        s.clear()            // 将流s中的所有状态值都重设为有效状态
        s.clear(flag)      // 将流s中的某个指定条件状态设置为有效。flag的类型是strm::iostate
        s.setstate(flag) // 给流s添加指定条件。flag的类型是strm::iostate
        s.rdstate()        // 返回流s的当前条件,返回值类型为strm::iostate

     

        这么多操作我们该如何理解呢。

        上面说过,任何一个IO对象在任意时刻都有一种状态。iostate 就是代表状态的枚举。badbit,failbit,eofbit,goodbit是iostate的一个具体值,看下面代码

    int main()
    {
        cout << std::iostream::good << std::iostream::badbit << std::iostream::eofbit << std::iostream::failbit << endl;  // 输出 0 1 2 4
        std::iostream::iostate coutstate = cin.rdstate(); // 得到cin对象的原始状态值
        cout << coutstate << endl;  // 输出 0 cin的状态值是 std::iostream::good
         
        int i;
    
        cin >> i; // 输入"123"
        cout << cin.rdstate() << endl;  // 输出 0 因为“123”可以被正确转成int并被存入i 所以cin的状态置为 std::iostream::good 
        cout << cin.good() << cin.eof() << cin.fail() << cin.bad() << end; // 输出 1 0 0 0
    
        cin >> i; // 输入"abd"
        cout << cin.rdstate() << endl;  // 输出 4  因为“abc”无法转成int存入i 所以cin的状态置为 std::iostream::failbit 
        cout << cin.good() << cin.eof() << cin.fail() << cin.bad() << end; // 输出 0 0 1 0
    
        cin.clear(); // 重置cin状态为std::iostream::good 否则下面的cin << i 不会执行, 或者这样设置cin.clear(std::iostream::failbit)
    
        cin >> i; // 输入"568"
        cout << cin.rdstate() << endl;  // 输出 0 因为“568”可以被正确转成int并被存入i 所以cin的状态置为 std::iostream::good
        cout << cin.good() << cin.eof() << cin.fail() << cin.bad() << end; // 输出 1 0 0 0
    }

    8.3 输出缓冲区的管理

        关于缓冲区内容本章开头有介绍。

        这里介绍tie()函数用法,函数可将输出流与输入流关联起来,在这种情况下在读取输入流时将刷新关联的输出流,下面代码解释了含义

    #include <fstream> 
    using namespace std; 
    
    int main() 
    {    
        //创建文件test.txt并打开 
      ofstream outfile("test.txt"); 
     
    
        //将输出流对象 outfile 和输入cin关联起来
    
        cin.tie(&outfile);
    
    
        //向test.txt文件中写入字符串
       outfile << “abcdef”; 
    
        int in;
        // 执行输入语句时会立刻刷新关联的输出流,字符串被写到文件中 
       cin >> in; 
    
        return 0; 
    }

    如果不用tie()函数做关联,main 方法执行完毕字符串"abcedf"才会被刷新到test.txt文本文件(执行完毕后系统会自动关闭文件从而刷新缓冲区内容到文件)。

        tie()关联之后第一次执行到 cin 语句系统会立刻刷新关联的输出流,所以内容会立刻写入文件不必等到main执行完毕。

     

    8.4 文件流对象的使用   

        C++ 通过以下几个类支持文件的输入输出:

        ofstream: 写操作(输出)的文件类 (由ostream引申而来)

        ifstream: 读操作(输入)的文件类(由istream引申而来)

        fstream: 可同时读写操作的文件类 (由iostream引申而来)

     

        打开文件(Open a file)

              对这些类的一个对象所做的第一个操作通常就是将它和一个真正的文件联系起来,也就是说打开一个文件。被打开的文件在程序中由一个流对象(stream object)来表示 (这些类的一个实例) ,而对这个流对象所做的任何输入输出操作实际就是对该文件所做的操作。

     

              要通过一个流对象打开一个文件,我们使用它的成员函数open() :

           void open (const char *filename, openmode mode) ;

              这里filename 是一个字符串,代表要打开的文件名,mode 是以下标志符的一个组合:

    ios::in 为输入(读)而打开文件
    ios::out 为输出(写)而打开文件
    ios::ate 初始位置:文件尾
    ios::app 所有输出附加在文件末尾
    ios::trunc 如果文件已存在则先删除该文件
    ios::binary 二进制方式

              这些标识符可以被组合使用,中间以”或”操作符(|)间隔。例如,如果我们想要以二进制方式打开文件"example.bin" 来写入一些数据,我们可以通过以下方式调用成员函数open() 来实现:

                ofstream file ;
                file.open ("example.bin", ios::out | ios::app | ios::binary) ;

     

              ofstream, ifstream 和 fstream所有这些类的成员函数open 都包含了一个默认打开文件的方式xxstream.open("filepath") ,这三个类的默认方式各不相同:

    参数的默认方式
    ofstream ios::out | ios::trunc
    ifstream ios::in
    fstream ios::in | ios::out

              只有当函数被调用时没有声明方式参数的情况下,默认值才会被采用。如果函数被调用时声明了任何参数,默认值将被完全改写,而不会与调用参数组合。

     

              由于对类ofstream, ifstream 和 fstream 的对象所进行的第一个操作通常都是打开文件,这些类都有一个构造函数可以直接调用open 函数,并拥有同样的参数。这样,我们就可以通过以下方式进行与上面同样的定义对象和打开文件的操作:

           ofstream file("example.bin", ios::out | ios::app | ios::binary); 

               // 等价于

               ofstream file ;

               file.open("example.bin",ios::out | ios::app | ios::binary) ;

     

              两种打开文件的方式都是正确的。你可以通过调用成员函数is_open()来检查一个文件是否已经被顺利的打开了:

           bool is_open() ;

              它返回一个布尔(bool)值,为真(true)代表文件已经被顺利打开,假( false )则相反。

     

        关闭文件(Closing a file)

              当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件。它的格式很简单:void close();

              这个函数一旦被调用,原先的流对象(stream object)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。

              为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close。

     

        文本文件(Text mode files)

              类ofstream, ifstream 和fstream 是分别从ostream, istream 和iostream 中引申而来的。这就是为什么 fstream 的对象可以使用其父类的成员来访问数据。

              一般来说,我们将使用这些类与同控制台(console)交互同样的成员函数(cin 和 cout)来进行输入输出。如下面的例题所示,我们使用重载的插入操作符<<

    #include <fiostream.h>
                
    int main () 
    {
        ofstream examplefile ("example.txt");
        if (examplefile.is_open()) 
        {
            // 输出到(写入)文件
            examplefile << "This is a line.\n"; 
            examplefile << "This is another line.\n";
            examplefile.close();
        }
        return 0;
    }

     文件内容是:   

              This is a line.
              This is another line.  

     

              从文件中读入数据也可以用与 cin的使用同样的方法:

    #include <iostream.h>
    #include <fstream.h>
    #include <stdlib.h>
                
    int main () 
    {
        char buffer[256];
        ifstream examplefile ("example.txt");
        if (! examplefile.is_open())
        { 
            cout << "Error opening file"; exit (1); 
        }
    
        while (! examplefile.eof() ) 
        {
            // 从文件输输入(读出)一行内容到 buffer        
            examplefile.getline (buffer,100);
            cout << buffer << endl;
        }
    
        return 0;
    }

    exit()退出程序,把控制权交给OS 
    return结束当前函数,返回函数值,把控制权交给调用函数
    在main函数中return 与exit用法差不多

      上面的例子读入一个文本文件的内容,然后将它打印到屏幕上。注意我们使用了一个新的成员函数叫做eof ,它是ifstream 从类 ios 中继承过来的,当到达文件末尾时返回true 。

     

        状态标志符的验证(Verification of state flags)

              除了eof()以外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):

    • bad()

      如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。

    • fail()

      除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。

    • eof()

      如果读文件到达文件末尾,返回true。

    • good()

      这是最通用的:如果调用以上任何一个函数返回true 的话,此函数返回 false 。

            要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear(),没有参数。

     

     

        获得和设置流指针(get and put stream pointers)

              所有输入/输出流对象(i/o streams objects)都有至少一个流指针:

    • ifstream, 类似istream, 有一个被称为get pointer的指针,指向下一个将被读取的元素。
    • ofstream, 类似 ostream, 有一个指针 put pointer ,指向写入下一个元素的位置。
    • fstream, 类似 iostream, 同时继承了get 和 put

             我们可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针:

    • tellg() 和 tellp()

      这两个成员函数不用传入参数,返回pos_type 类型的值(根据ANSI-C++ 标准) ,就是一个整数,代表当前get 流指针的位置 (用tellg) 或 put 流指针的位置(用tellp).

    • seekg() 和seekp()

      这对函数分别用来改变流指针get 和put的位置。两个函数都被重载为两种不同的原型:

      seekg ( pos_type position ) ;
      seekp ( pos_type position ) ;

      使用这个原型,流指针被改变为指向从文件开始计算的一个绝对位置。要求传入的参数类型与函数 tellg 和tellp 的返回值类型相同。

      seekg ( off_type offset, seekdir direction ) ;
      seekp ( off_type offset, seekdir direction ) ; 
      使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。它可以是:

      ios::beg 从流开始位置计算的位移
      ios::cur 从流指针当前位置开始计算的位移
      ios::end 从流末尾处开始计算的位移

       

             流指针 get 和 put 的值对文本文件(text file)和二进制文件(binary  file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekg 和 seekp的第一种原型,而且不要对tellg 或 tellp 的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。

     

             以下例子使用这些函数来获得一个二进制文件的大小:

    #include <iostream.h>
    #include <fstream.h>
                
    const char * filename = "example.txt";
                
    int main () 
    {
        long l,m;
        ifstream file(filename, ios::in|ios::binary);    
        l = file.tellg();
    
        file.seekg(0, ios::end);
        m = file.tellg();
        file.close();
        cout << "size of " << filename << " is " << (m-l) << " bytes.\n"; // 输出 size of example.txt is 40 bytes. 
        return 0;
    }

    二进制文件(Binary files)

              在二进制文件中,使用<< 和 >>,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。

              文件流包括两个为顺序读写数据特殊设计的成员函数:write 和 read。第一个函数 (write) 是ostream 的一个成员函数,都是被ofstream所继承。而read 是istream 的一个成员函数,被ifstream 所继承。类 fstream 的对象同时拥有这两个函数。它们的原型是:

              write ( char * buffer, streamsize size ) ;
              read ( char * buffer, streamsize size ) ;
     

              这里 buffer 是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数

    #include <iostream>
    #include <fstream.h>
                
    const char * filename = "example.txt";
                
    int main () 
    {
        char * buffer;
        long size;
        ifstream file (filename, ios::in|ios::binary|ios::ate); // ios::ate 表示指向文件末尾
        size = file.tellg(); // 取得文件大小
        file.seekg (0, ios::beg);
        buffer = new char[size];
        file.read(buffer, size);
        file.close();
                
        cout << "the complete file is in a buffer";
                
        delete[] buffer;
        return 0;
    }

    缓存和同步(Buffers and Synchronization)

              当我们对文件流进行操作的时候,它们与一个streambuf 类型的缓存(buffer)联系在一起。这个缓存(buffer)实际是一块内存空间,作为流(stream)和物理文件的媒介。例如,对于一个输出流, 每次成员函数put  (写一个单个字符)被调用,这个字符不是直接被写入该输出流所对应的物理文件中的,而是首先被插入到该流的缓存(buffer)中。

              当缓存被排放出来(flush)时,它里面的所有数据或者被写入物理媒质中(如果是一个输出流的话),或者简单的被抹掉(如果是一个输入流的话)。这个过程称为同步(synchronization),它会在以下任一情况下发生:

    • 当文件被关闭时: 在文件被关闭之前,所有还没有被完全写出或读取的缓存都将被同步。
    • 当缓存buffer 满时:缓存Buffers 有一定的空间限制。当缓存满时,它会被自动同步。
    • 控制符明确指明:当遇到流中某些特定的控制符时,同步会发生。这些控制符包括:flush 和endl。
    • 明确调用函数sync(): 调用成员函数sync() (无参数)可以引发立即同步。这个函数返回一个int 值,等于-1 表示流没有联系的缓存或操作失败。

         在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:
         1、插入器 (<<) 
             向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<'n';就表示把字符串"Write Stdout"和换行字符('n')输出到标准输出流。


         2、析取器 (>>) 
             从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
              在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。
              一、打开文件 
                    在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:
                    void open(const char* filename, int mode, int access) ;
                    参数:

                    filename: 要打开的文件名 
                    mode: 要打开文件的方式 
                    access: 打开文件的属性 
           

                    打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:

                    ios::app:          以追加的方式打开文件 
                    ios::ate:           文件打开后定位到文件尾,ios:app就包含有此属性 
                    ios::binary:      以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文 
                    ios::in:             文件以输入方式打开 
                    ios::out:           文件以输出方式打开 
                    ios::nocreate:  不建立文件,所以文件不存在时打开失败 
                    ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败 
                    ios::trunc:        如果文件存在,把文件长度设为0 
                    可以用“或”把以上属性连接起来,如 ios::out| ios::binary

     

                   打开文件的属性取值是:

                   0:普通文件,打开访问 
                   1:只读文件 
                   2:隐含文件 
                   4:系统文件 
                   可以用“或”或者“+”把以上属性连接起来 ,如3或 1|2 就是以只读和隐含属性打开文件。

                   例如:以二进制输入方式打开文件c:config.sys
                   fstream file1 ; 
                   file1.open("config.sys", ios::binary | ios::in,0) ;


                   如果open函数只有文件名一个参数,则是以读/写普通文件打开,即:

                   file1.open("config.sys") ; 等价于 file1.open("config.sys",ios::in|ios::out,0) ; 
                   另外,fstream还有和open()一样的构造函数,对于上例,在定义的时侯就可以打开文件了:
                   fstream file1("config.sys") ;
                   特别提出的是,fstream有两个子类:ifstream(input file stream)和ofstream(outpu file stream),ifstream默认以输入方式打开文件,而ofstream默认以输出方式打开文件。
                   ifstream file2("dos.def") ; //以输入方式打开文件 
                  ofstream file3("x.123") ;   //以输出方式打开文件
                   所以,在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开,就用ifstream来定义;如果想以输出方式打开,就用ofstream来定义;如果想以输入/输出方式来打开,就用fstream来定义。

              二、关闭文件 
                   打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,如:file1.close();就把file1相连的文件关闭。

     

              三、读写文件 
                    读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式

     

                    1、文本文件的读写 
                         文本文件的读写很简单:用插入器(<<)向文件输出;用析取器(>>)从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:
                         file2<<"I Love You" ;  // 向文件写入字符串"I Love You" 
                         int i ; 
                         file1 >> i ;  // 从文件输入一个整数值。

                         这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些
                         操纵符 功能 输入/输出 
                        dec    格式化为十进制数值数据 输入和输出 
                        endl   输出一个换行符并刷新此流 输出 
                        ends  输出一个空字符 输出 
                        hex   格式化为十六进制数值数据 输入和输出 
                        oct    格式化为八进制数值数据 输入和输出 
                        setpxecision(int p) 设置浮点数的精度位数 输出
                         比如要把123当作十六进制输出:file1<<hex<<123;要把3.1415926以5位精度输出:file1<<setpxecision(5)<<3.1415926。
                    2、二进制文件的读写 
                        ①put() 
                           put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put('c');就是向流写一个字符'c'。
                        ②get() 
                           get()函数比较灵活,有3种常用的重载形式:
                           一种就是和put()对应的形式:ifstream &get(char &ch) ;功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。
                           另一种重载形式的原型是: int get() ;这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。
                           还有一种形式的原型是:ifstream &get(char *buf,int num,char  delim='n') ;这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符'n'。例如:
                           file2.get(str1,127,'A') ;  // 从文件中读取字符到字符串str1,当遇到字符'A'或读取了127个字符时终止。
                        ③读写数据块 
                           要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:
                           read(unsigned char *buf,int num) ; 
                           write(const unsigned char *buf,int num) ;
                           read() 从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。
                           例:
                           unsigned char str1[]="I Love You"; 
                           int n[5]; 
                           ifstream in("xxx.xxx"); 
                           ofstream out("yyy.yyy"); 
                           out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中 
                           in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换 
                           in.close();  out.close();

     

              四、检测EOF 
                   成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();
                   例: if(in.eof()) ShowMessage("已经到达文件尾!") ;

     

              五、文件定位 
                    和C的文件操作方式不同的是,C++  I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时相应的指针自动变化。

                    所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:
                    istream &seekg(streamoff offset,seek_dir origin); 
                    ostream &seekp(streamoff offset,seek_dir origin);
                    streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
                    ios::beg: 文件开头 
                    ios::cur: 文件当前位置 
                    ios::end: 文件结尾 
                    这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。
                    例:

                    file1.seekg(1234,ios::cur) ;  //把文件的读指针从当前位置向后移1234个字节 
                    file2.seekp(1234,ios::beg) ; //把文件的写指针从文件开头向后移1234个字节





    展开全文
  • 由于这需要等待,那么这中间很可能已经切换到另一个进程了,这个时候在另一个运行的进程也需要请求磁盘的资源,而恰好第一个进程请求的资源已经准备好了,那么这两个进程是不是都会认为现在磁盘缓冲区里面的数据是...
  • 环形缓冲

    千次阅读 2017-03-18 16:37:29
    在嵌入式软件开发中,经常会遇到这样的场景,创建一个临时缓冲区用于存放待读取的数据。这时候,环形缓冲区是一个不错的选择。所谓环形缓冲区就是一段有限的内存空间,并且有两个指针分别代表读、写指向这一块内存,...

    在嵌入式软件开发中,经常会遇到这样的场景,创建一个临时缓冲区用于存放待读取的数据。这时候,环形缓冲区是一个不错的选择。

    所谓环形缓冲区就是一段有限的内存空间,并且有两个指针分别代表读、写指向这一块内存,读指针表示读的位置,写指针则表示写的位置。当这块内存被写满的时候,写指针会回到内存的起始位置写,读操作也是如此,读到最后的位置会回到内存起始位置读取,要实现这样的指针移动功能,移动指针操作就并非单纯的++操作,而是

    writePoint = (writePoint + 1) % BUFFSIZE;  //读指针
    readPoint = (readPoint + 1) % BUFFSIZE;    //写指针

    环形缓冲区代码的实现,关键在于判断缓冲区是空状态还是满状态。空状态表示没有数据可读取了,即环形缓冲区上的数据都是被读取过了,这时候读指针和写指针相同:

    writePoint == readPoint;    //相等表示缓冲区的数据都被读取过了

    满状态则表示整个环形缓冲区的数据都没被读取过,这时候读指针位于内存的首地址,而写指针则位于缓冲区的最后一个元素的首地址

    readPoint == (writePoint + 1) % BUFFSIZE;

    下面的代码是基于面向对象思想来写的环形缓冲区:

    #include <stdio.h>
    #include <stdlib.h>
    
    //容错判断宏
    #define ERRP(con, ret, ...) do  \
        if (con){                   \
        printf(__VA_ARGS__);        \
        ret;                        \
    }while(0)
    
    //前向typedef
    typedef struct _circleBuffer CIRCLEBUFFER;
    
    //函数指针,作为结构体的"成员函数"
    typedef int (*_isFull)(CIRCLEBUFFER* buff);     //判断缓冲器是否满(所有数据都尚未被读取)
    typedef int (*_isEmpty)(CIRCLEBUFFER* buff);    //判断缓冲区是否为空(所有数据都被读取完毕)
    typedef int (*_getData)(CIRCLEBUFFER* buff, void* OutputDat);  //获取缓冲区上的数据
    typedef int (*_putData)(CIRCLEBUFFER* buff, void* inputDat);   //将数据写入缓冲区
    
    //缓冲区的描述结构体
    struct _circleBuffer{
        void* buffer;       //指向动态开辟饿缓冲区的首地址
        int num;            //指定开辟多少个数据元素的缓冲区
        int size;           //指定每个数据元素的大小
        int readPos;        //读指针
        int writePos;       //写指针
        _isFull bufIsFull;  
        _isEmpty bufIsEmpty;
        _getData getBufData;
        _putData putBufData;
    };
    
    //销毁动态分配的环形缓冲区
    void destroyCircleBuffer(CIRCLEBUFFER** buff)
    {
        CIRCLEBUFFER* my_buf = *buff;
        free((*buff)->buffer);
        free(my_buf);
        *buff = NULL;
    }
    
    int isFull(CIRCLEBUFFER* buff)
    {
        //这个判断条件会使得缓冲区的最后一个数据位得不到填充,关系不大
        return ((buff->writePos + 1) % buff->num == buff->readPos);
    }
    
    int isEmpty(CIRCLEBUFFER* buff)
    {
        return buff->writePos == buff->readPos;
    }
    
    int getData(CIRCLEBUFFER* buff, void* OutputDat)
    {
        if (buff->bufIsEmpty(buff))  //这里对函数指针的调用,记得要把对应的参数传入
            return -1;
        *((int* )OutputDat) = *((int* )(buff->buffer + buff->readPos * buff->size));
        buff->readPos = (buff->readPos + 1) % buff->num;
    
        return 0;
    }
    
    int putData(CIRCLEBUFFER* buff, void *inputDat)
    {
        //这个判断条件会使得缓冲区的最后一个数据位得不到填充,不过关系不大
        if (buff->bufIsFull(buff))  //这里对函数指针的调用,记得要把对应的参数传入
            return -1;
    
        *((int *)(buff->buffer + buff->writePos * buff->size)) = *((int *)inputDat);
        buff->writePos = (buff->writePos + 1) % buff->num;
    
        return 0;
    }
    
    CIRCLEBUFFER* createCircleBuffer(int num, int size, _isFull isFull, _isEmpty isEmpty, _getData getData, _putData putData)
    {
        CIRCLEBUFFER* circleBuffer = NULL;
    
        circleBuffer = (CIRCLEBUFFER* )malloc(sizeof(CIRCLEBUFFER));
        ERRP(circleBuffer == NULL, return NULL, "malloc circleBuffer failed!!");
    
        circleBuffer->num = num;
        circleBuffer->size = size;
        circleBuffer->bufIsFull = isFull;
        circleBuffer->bufIsEmpty = isEmpty;
        circleBuffer->readPos = 0;
        circleBuffer->writePos = 0;
        circleBuffer->getBufData = getData;
        circleBuffer->putBufData = putData;
    
        circleBuffer->buffer = malloc(circleBuffer->size * circleBuffer->num);
        ERRP(circleBuffer == NULL, return NULL, "malloc buffer failed!!");
    
        return circleBuffer;
    
    ERR2:
        free(circleBuffer);
    ERR1:
        return NULL;
    }
    
    int main(void)
    {
        int i = 0, ret = 0;
        int OutBuf[200] = {};
    
        CIRCLEBUFFER* circleBuffer = NULL;
    
        //创建环形缓冲区,10个空间大小为4的数据成员空间
        circleBuffer = createCircleBuffer(10, 4, isFull, isEmpty, getData, putData);
        ERRP(circleBuffer == NULL, return -1, "circleBuffer is NULL!!\n");
    
        //添加20个数据,实际只能添加9个,因为第10位被判断满的条件忽略了 
        for (i = 0; i < 20; i++)
        {
            ret = circleBuffer->putBufData(circleBuffer, (void* )&i);
            ERRP(ret < 0, goto READ, "环形缓冲区已满,已经装了%d个数据\n", i);
        }
    
    READ:
        for (i = 0; i < 2; i++) //读取2个
        {
            ret = circleBuffer->getBufData(circleBuffer, &OutBuf[i]);
            ERRP(ret < 0, goto WRITE, "环形缓冲区已经没有数据,已经读取了%d个数据\n", i);
        }
        printf("读取了%d个数据\n", i);
    
    WRITE:
        for (i = 0; i < 15; i++) //写2个
        {
            ret = circleBuffer->putBufData(circleBuffer, (void* )&i);
            ERRP(ret < 0, goto FREE, "环形缓冲区已满,已经装了%d个数据\n", i);
        }   
    
    FREE:
        destroyCircleBuffer(&circleBuffer); //释放堆空间
    
        return 0;
    }

    运行结果:
    环形缓冲区运行结果

    展开全文
  • 缓冲

    热门讨论 2017-11-05 21:36:06
    缓冲是第一个缓冲区读入数据完成时第二个缓冲区开始工作,读入用户区结束后判断第一个缓冲区是否停止工作,如果停止工作那继续向第一个缓冲读入数据。 【2011年计算机统考真题】某文件占用10个磁盘块
  • 环形缓冲

    千次阅读 2019-06-02 21:02:38
    环形缓冲器(ringr buffer),也称作圆形队列(circular queue),循环缓冲区(cyclic buffer),圆形缓冲区(circula buffer),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。...
  • <br />说明:我只网络资源进行了整合,方便学习~。~ 基于流的操作最终会调用read或者write函数进行I/O操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O库函数的次数。...
  • C语言输入输出流和缓冲区的深入理解

    万次阅读 多人点赞 2015-10-06 10:08:17
    导读:C语言输入输出流和缓冲区的深入理解,C语言缓冲区(缓存)详解,缓冲区又称为缓存,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,缓冲区根据其对应的是输入设备还是输出设备,分为...
  • 【摘要】当发现自己数据库SQL执行慢的时候,可以排查自己的MYSQL数据库缓冲池是否已经不够。点击自己数据库的监控按钮,来到监控页面,可以看到命中率指标: 可以看到两个指标,缓冲池利用率和缓冲池命中率如下: ...
  • UDP socket缓冲区的理解

    万次阅读 2014-09-05 14:03:17
    UDP socket缓冲区的理解  UDP套接字的收发报文要用sendto 和 recvfrom,可以类比TCP套接字的connect和accept,参数里面会标识要发往的端,或者要接收的端的IP地址和端口;UDP套接字connect的行为也只是...
  • 缓冲与双缓冲的区别

    千次阅读 2016-03-30 17:22:05
    OpenGL单缓冲与双缓冲的区别 单缓冲,实际上就是将所有的绘图指令在窗口上执行,就是直接在窗口上绘图,这样的绘图效率是比较慢的,如果使用单缓冲,而电脑比较慢,你回到屏幕的闪烁。 双缓冲,实际上的绘图指令是...
  • Technote (troubleshooting) 本文档仅适用于以下语言版本: ...本文将简单介绍判断一个查询的性能变化是否是由缓冲池命中率导致的方法。 症状 相同的查询,在 DB/DBM 配置和应用都没有变化的情况下,
  • Linux 之缓冲

    2018-08-18 00:07:07
    Linux缓冲区介绍 标准I/O为我们提供了3种类型的缓冲区:全缓冲区、行缓冲区、无缓冲区。 (1)全缓冲区: 这种缓冲区默认大小为BUFSIZ,具体大小与系统定义有关。在缓冲区慢或主动调用缓冲区刷新函数fflush()...
  • gui 中的显示功能 gui 中的显示最终通过调用...与 gui 而言,所有的在调用 lcd 驱动刷新 framebuffer 到屏幕上显示之前都是通过 framebuffer 的操作完成。这里提及的操作主要使用 memcpy、memset 来完成,这也...
  • 此工具让缓冲操作变得更加方便,除了缓冲区他还有两个channel——ByteInputChannel和ByteOutputChannel,这两个通道一个用于输入读取数据,一个用于输出数据,并且会自动判断缓冲区是否超出规定的缓冲大小,一旦超
  • 点击打开链接我们可以用printk打印kernel的日志信息(即log信息),根据时间戳可以判断内核新打印的log会覆盖掉以前打印的log。原因是内核用环形缓冲区存放打印的log信息。那么如何增大缓冲区的大小呢?我们看kernel...
  • 那么就可以将非阻塞I/O方式下的数据传输比做数据传输的集装箱方式(在字节和低层数据传输之间,多了一层缓冲区,因此,可以将缓冲区看做是装载字节的集装箱)。  如果将同步I/O方式下的数据传输比做数据...
  • 队列1-环形缓冲

    千次阅读 2019-01-27 16:58:34
    本篇为队列的第一篇文章,介绍基于数组结构的一个环形缓冲区队列。我觉得没有必要再从数组来写起,毕竟对于数组本身来说,我觉得是没有太多可说的,但是基于数组的数据结构就有的说了。 什么是环形缓冲区 环形缓冲...
  • 为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O库函数的次数。 基于流的I/O提供以下3种缓冲: 全 缓冲:直到缓冲区被填满,才调用系统I/O函数。对于读操作来说,直到读入的内容的字节...
  • //点是否在多边形内判断 Coordinate point = new Coordinate(116.663609,40.387187); PointLocator a=new PointLocator(); boolean p1=a.intersects(point, bg); if(p1) System.out.println("point1:"+"该点在...
  • SurfaceFlinger服务在启动的过程中,会系统的硬件帧缓冲区进行初始化。由于系统的硬件帧缓冲区一般只有一个,并且不是谁都可以随便访问的,因此,它就需要由一个服务来统一管理。在Android系统中,这个服务便是...
  • NIO之缓冲区【基础内容】

    千次阅读 2019-04-10 12:30:58
    缓冲区Buffer 1.缓冲区介绍   一个Buffer对象是固定数量的数据的容器。其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索。缓冲区可以写满和释放。对于每个非布尔原始数据类型都有一个缓冲...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 286,889
精华内容 114,755
关键字:

如何判断缓冲对