您的位置:首页 > 产品设计 > UI/UE

[APUE]详解5.14节内存流实例5-15

2020-02-01 17:57 1061 查看

学习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字节,还需要同时满足流缓冲区数据量增加的这个条件,二者缺一不可。

  • 点赞
  • 收藏
  • 分享
  • 文章举报
reflectionEt1rnal 发布了3 篇原创文章 · 获赞 0 · 访问量 83 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: