[APUE]详解5.14节内存流实例5-15
学习5.14内存流这节时,没有看懂实例5-15的运行结果,主要的问题有:一、不清楚第二组实验输出为什么前面会有12个字符b;二、不清楚为什么最后一组没有追加null字节。
反复实验加上看到另一位大佬写的解读后,终于明白了( 大佬的解读:点这里)。第一个问题的关键在于对当前文件位置的维护;第二个问题的关键在于第二组实验和第三组实验之间没有造成流缓冲区中数据量的增加,所以不满足追加写null字节的两个条件之一。在此记录下我对整个代码的解读,阅读此文时,注意当前文件位置的变化。
首先看代码,这个实例演示的是内存流的写入如何在我们自己指定的缓冲区上进行操作。
#include "apue.h" #define BSZ 48 int main() { FILE *fp; char buf[BSZ]; //第一组实验 memset(buf, 'a', BSZ-2); buf[BSZ-2] = '\0'; buf[BSZ-1] = 'X'; if ((fp = fmemopen(buf, BSZ, "w+")) == NULL) err_sys("fmemopen failed"); printf("initial buffer contents: %s\n", buf);fprintf(fp, "hello, world"); printf("before flush: %s\n", buf); fflush(fp); printf("after fflush: %s\n", buf); printf("len of string in buf = %ld\n", (long)strlen(buf)); //第二组实验 memset(buf, 'b', BSZ-2); buf[BSZ-2] = '\0'; buf[BSZ-1] = 'X'; fprintf(fp, "hello, world"); fseek(fp, 0, SEEK_SET); printf("after fseek: %s\n", buf); printf("len of string in buf = %ld\n", (long)strlen(buf)); //第三组实验 memset(buf, 'c', BSZ-2); buf[BSZ-2] = '\0'; buf[BSZ-1] = 'X'; fprintf(fp, "hello, world"); fclose(fp); printf("after fclose: %s\n", buf); printf("len of string in buf = %ld\n", (long)strlen(buf)); return(0); }
输出:
$./memstr
initial buffer contents:
before flush:
after fflush: hello, world
len of string in buf = 12
after fseek: bbbbbbbbbbbbhello, world
len of string in buf = 24
after fclose: hello, worldcccccccccccccccccccccccccccccccccc
len of string in buf = 46
以下是对代码的解读:
1.通过memset函数将buf字符串前46(BSZ-2)项赋值为字符a,接着将倒数第二项赋值为NULL(缓冲区内容中用0表示),末项赋值为X。注意此时还未涉及到当前文件位置。
memset(buf, 'a', BSZ-2); buf[BSZ-2] = '\0'; buf[BSZ-1] = 'X';
此时缓冲区的内容为:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0X
2.通过fmemopen创建一个与指定缓冲区buf绑定的内存流,此时当前文件位置为0;
if ((fp = fmemopen(buf, BSZ, "w+")) == NULL) err_sys("fmemopen failed"); printf("initial buffer contents: %s\n", buf);
此时缓冲区的内容为:
0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0X
格式化输出函数printf读取到第一个字符为NULL,判断此字符串已到结尾,故输出为空。
对应第1行输出:
initial buffer contents:
3.fprintf函数将字符串"hello,world"写入指针fp指向的流中,流缓冲区中数据量增加。此时当前文件位置仍为0。
fprintf(fp, "hello, world"); printf("before flush: %s\n", buf);
此时还未将写入到内存流中的数据冲洗到缓冲区,缓冲区的内容仍为:
0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0X
对应第2行输出:
before flush:
4.将流中的数据冲洗到缓冲区并清除 流 缓冲区的内容。因为当前文件位置为0,所以从缓冲区的首项开始写。
fflush(fp); printf("after fflush: %s\n", buf); printf("len of string in buf = %ld\n", (long)strlen(buf));
“hello,world”字符串长度为12,写入后当前文件位置值应为12,由于调用了fflush(),所以在当前位置(第12项)写入一个NULL字节,此时缓冲区内容为:
hello,world0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0X
第一个NULL出现在第12项,则调用strlen()返回值为12,对应第3、4行输出:
after fflush: hello, world
len of string in buf = 12
5.与1类似,注意此时当前文件位置为12。
memset(buf, 'b', BSZ-2); buf[BSZ-2] = '\0'; buf[BSZ-1] = 'X';
此时缓冲区内容:
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0X
6.将字符串“hello,world”写入到指针fp指向的流中。由于之前调用了fflush(),清除了流缓冲区的数据内容,所以此写入流操作带来的效果之一就是流缓冲区数据量增加。然后调用fseek(),引起缓冲区冲洗(当前文件位置为12)然后将当前文件位置设为0。
fprintf(fp, "hello, world"); fseek(fp, 0, SEEK_SET); printf("after fseek: %s\n", buf); printf("len of string in buf = %ld\n", (long)strlen(buf));
此时重新从缓冲区第十二项开始写入数据,且由于调用fseek()和流缓冲区数据量增加,在写入数据后的当前位置(24)写入一个NULL,此时缓冲区内容:
bbbbbbbbbbbbhello,world0bbbbbbbBbbbbbbbbbbbbbb0X
对应5、6行输出:
after fseek: bbbbbbbbbbbbhello, world
len of string in buf = 24
7.用字符c填充缓冲区,此时当前文件位置为0。
memset(buf, 'c', BSZ-2); buf[BSZ-2] = '\0'; buf[BSZ-1] = 'X';
缓冲区内容为:
cccccccccccccccccccccccccccccccccccccccccccccc0X
8.当前文件位置为0,将字符串“hello,world”写入到指针fp指向的流中。注意到此时的向流中写入相同字符串的操作并不会造成流缓冲区数据量增加。调用fclose(),引起缓冲区冲洗。
fprintf(fp, "hello, world"); fclose(fp); printf("after fclose: %s\n", buf); printf("len of string in buf = %ld\n", (long)strlen(buf));
此时从缓冲区的首项开始写入数据(当前文件位置为0),写入数据后,当前文件位置为12。但由于流缓冲区的数据量没有增加,所以此时不会在当前文件位置写入一个NULL。此时缓冲区内容为:
hello,worldccccccccccccccccccccccccccccccccccc0X
对应7,8行输出:
after fclose: hello, worldcccccccccccccccccccccccccccccccccc
len of string in buf = 46
总结:
关于null字节追加策略的问题,关键点就在书P138所写的:
第三,任何时候需要增加流缓冲区中数据量以及调用fclose、fflush、fseek、fseeko以及fsetpos时都会在当前位置写入一个null字节。
注意到这段描述表明,只调用所提及的这些函数并不能造成追加写null字节,还需要同时满足流缓冲区数据量增加的这个条件,二者缺一不可。
- 点赞
- 收藏
- 分享
- 文章举报
- C语言实现txt数据读入内存/CPU缓存实例详解
- C++中的内存对齐实例详解
- C语言内存对齐实例详解
- APUE 5-15 观察内存流的写入操作
- Linux内存描述符mm_struct实例详解
- Java学习1:图解Java内存分析详解(实例)
- linux free命令详解和使用实例(查看内存使用率)
- Spark算子[15]:sample、takeSample 源码实例详解
- 详解 swift3.0 可选绑定共用同一块内存空间的实例
- static修饰的静态变量与实例变量的区别,及其在初始化和内存中的运行机制详解
- linux free命令详解和使用实例(查看内存使用率)
- Android仿微信清理内存图表动画(解决surfaceView屏幕闪烁问题)demo实例详解
- Objective-C中关于实例所占内存的大小详解
- Tomcat 检测内存泄漏实例详解
- C++ 类的实例中 内存分配详解
- 实例详解new和delete 内存管理
- Spark算子[15]:sample、takeSample 源码实例详解
- IOS 调整内存中的图片大小实例详解
- C语言中的内存分配实例详解
- UNIX编程之冲洗内存流与null追加策略(APUE F5-15)