免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: stdlib.h
打印 上一主题 下一主题

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问! [复制链接]

论坛徽章:
0
21 [报告]
发表于 2005-08-13 18:55 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

再顶一下,为什么没有人解答呢?高手们都去哪儿了,都放假了吗?

论坛徽章:
0
22 [报告]
发表于 2005-08-14 11:10 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

几天没来,发现这么多关于stream和buffer的问题,首先谢谢大家提出这么多好问题。
我在这方面也不是高手,但我会尽我所能争取能和大家一道解决好这个问题。
对于楼主stdlib.h的疑问:

1、
我的理解是:一旦执行到getchar()这个语句,getchar()首先去查看缓冲区看有没有数据,如果没有,就让用户用标准输入键盘输入数据,一旦检测到有敲“回车”这个动作,就认为输入结束,然后把这一串输入(包括一个'a' 97 和一个回车符CR 10)当作字节流送到缓冲区中去,然后程序从缓冲区中读取数据,由于getchar()只读取一个字符,所以CR就被留在了缓冲区中,这样会引发一些问题,所以kenelxu在精华区的那个帖子中详细的讨论了如何解决这个问题,比如在后面加一个getchar()吃掉这个回车符,这也是最不保险的办法。
  但是,我的理解却是错的!  
  通过打印buf的值,发现是:
   97 10 10 0 0 0 0 0 0 0
   over
   按我的理解,应该是:
  10  0  0  0  0  0  0  0  0  0
   over

你的理解是错误的,getchar()不是取走了'a',而只是读取了它。在缓冲区刷新之前,所有输入的字符都存在于缓冲区之中。标准输入缓冲区的刷新大体可分为系统刷新和人为刷新,系统刷新需遵循一定的规则(我正在思索中),而标准C语言对于人为刷新标准输入缓冲区(fflush(stdin))是未定义的。所以精华贴的实例中不可用fflush(stdin)。但是,若我们使用setbuf(stdin, buf)来修改系统提供的缓冲区为我们指定的缓冲区,我猜想这时候可以使用fflush(stdin),我已试验了几例都可以,哪位仁兄可以帮忙在linux下试试,我的系统坏了。
       下次读取缓冲区哪一个单元是由stdin的文件结构体中的位置指针来指示的。所以缓冲区中的数据是依次读取的。
       再者,为什么这里有两个‘10’,一个是敲入的回车转换来的,这个大家都知道。另一个我认为是与系统有关系,是由的系统内部自己加上的,为什么呢?我猜想(正在查找原因)是因为在自定义的缓冲区中(buf[10]),那些未用的单元要么存储着初值要么存储着未初始化造成的垃圾数据,系统加入‘10’就是为了避免这些垃圾数据被“悄悄地读取”。

2、
然后,我把c=getchar()改成了scanf("%d",&i);
   运行后输入一个数,如15,回车!嗯?没反应,光标往下移了一行!我再输入一个数或者字符如'a',再回车,程序才接着往下走,打印出如下结果:
  97 10 10 10 0 0 0 0 0 0
  over
  这下我就更糊涂了,为什么要输入两次才有反应?那个15跑哪去了?缓冲区中为什么会有三个换行符?
我又试着这样运行:输入15 25再回车,马上就有结果了:

我怀疑是你的程序问题:我没有这种问题。你是不是scanf()上面还有个getchar()啊?

3、
2:还有一个问题就是使用setbuf(stdin,NULL);关掉标准输入的行缓冲功能后,到底输入的过程是怎样的,为什么它就能解决c=getchar()后残留的那个回车符问题?

setbuf(stdin,NULL);是设定stdin流缓冲区的大小为0,也就是使其变成无缓冲的。

论坛徽章:
0
23 [报告]
发表于 2005-08-14 11:57 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

对于fsilence的疑问:
的效果是不是应该是一样的?
下面改动后的程序就不能治疗getchar()读到换行符的问题


1、
setbuf()的设置在整个main()函数中都有效。也就是说当程序执行完毕后,操作系统收回对标准输入缓冲区的控制权,恢复系统默认的缓冲区设置。但一定要在流已被打开后调用(这是十分明显的,因为每个函数都要求一个有效的文件指针作为它们的第一个参数),而且也应在对该流执行任何一个其他操作之前调用。
2、
所以,在main()函数中使用一次即可。
3、
为什么

  1. #include <stdio.h>;

  2. int main(void)
  3. {
  4. int i,j;
  5. char c;
  6. printf("\n do you want to cal:y/n \n");
  7. setbuf(stdin,NULL);

  8. while (c=getchar()=='y')
  9. {
  10.      printf("input number:");
  11.      scanf("%d%d",&i,&j);
  12.      printf("i*j=%d",i*j);
  13.      //setbuf(stdin, NULL);
  14.      printf("\n do you want to cal:y/n \n");
  15. }
  16. /*system("pause");*/
  17. return 0;
  18. }
复制代码

这个有问题,是因为这里面可能还涉及到一个问题:
程序设定输入不缓冲并不表示操作系统shell也不缓冲,而且还可能涉及到硬件缓冲的问题。引用一外国朋友Walter Roberson的话:
you generally need to enter a newline after the initial 'y' because although the input might not be "buffered" by the time it gets to your program, it might be buffered at the shell -- setting
stdin to be unbuffered is *not* the same as undertaking system-
specific methods to turn on "raw" mode.

还得仔细研究一下。
哪位高人也指点一下吧。

论坛徽章:
0
24 [报告]
发表于 2005-08-14 13:52 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

首先,赞一下kernelxu的解释,很详细,个人觉得也很有道理,虽然想不通的问题依然存在
若我们使用setbuf(stdin, buf)来修改系统提供的缓冲区为我们指定的缓冲区,我猜想这时候可以使用fflush(stdin),我已试验了几例都可以,哪位仁兄可以帮忙在linux下试试,我的系统坏了。

我在精华贴的源码基础上稍作修改

  1. int main(void)
  2. {
  3.   int i,j;
  4.   char c;
  5.   char buffer[1024];

  6.   printf("\n do you want to cal:y/n \n");
  7.   setbuf(stdin,buffer);

  8. while ((c=getchar())=='y')
  9.   {
  10.       printf("input number:");
  11.       scanf("%d%d",&i,&j);
  12.       printf("i*j=%d",i*j);
  13.       fflush(stdin);
  14.       printf("\n do you want to cal:y/n \n");
  15.       for(i=0;i<10;i++)
  16.         printf("%d ",buffer[i]);
  17.       printf("\n");
  18.   }

  19. }
复制代码

结果是:又是运行一次就退出了;我的i,j输入的是1和2,printf那个buffer的结果是
50 10 0 0 0 0 0 0 0 0
说明fflush(stdin)还是不好用

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
25 [报告]
发表于 2005-08-14 14:05 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

楼上的兄弟 运行程序的平台是什么?

可能是未实现 对stdin 做fflush. 不同的平台实现不同吧.

论坛徽章:
0
26 [报告]
发表于 2005-08-14 14:23 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

原帖由 "fsilence" 发表:
结果是:又是运行一次就退出了;我的i,j输入的是1和2,printf那个buffer的结果是
50 10 0 0 0 0 0 0 0 0
说明fflush(stdin)还是不好用

我在DEV-C++下执行没有问题很好用的。不知你是在什么环境下。
不过,这也是我的一个猜想而已,既然能找到不能实现的例子,那么也就说明我的猜想有问题,至于说是什么原因,大家不妨继续探讨。

论坛徽章:
0
27 [报告]
发表于 2005-08-14 16:05 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

很不好意思的说,我的平台很老,RH7.3,gcc 2.96
stdlib.h 该用户已被删除
28 [报告]
发表于 2005-08-15 09:50 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
29 [报告]
发表于 2005-08-15 10:23 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

kernelxu兄弟是不是comp.lang.c上的kernelxu?呵呵我不小心认出来了

论坛徽章:
0
30 [报告]
发表于 2005-08-15 12:44 |只看该作者

看了精华区kenelxu关于缓冲区的那篇文章,仍有以下疑问!

原帖由 "albcamus"]kernelxu兄弟是不是comp.lang.c上的kernelxu?呵呵我不小心认出来了[/quote 发表:


呵呵,这也被老大人出来了。不知老大在上面是什么马甲,能不能站内短信告知一下,以后说不定就“撞车”了。
自己有些东西也搞不清,就在comp.lang.c上发了一个贴。
大家也可以看一看别人的解答:
http://groups-beta.google.com/gr ... en#6b7dfa8b01784910

我将解答总结在下面,希望对大家理解setbuf()和setvbuf()有所帮助。
1、C标准(ISO/IEC 9899:1999 standard)规定fflush(stdin)操作是未定义的<参看《ISO/IEC 9899:1999 standard》p270>;。也就是说不一定能实现刷新功能,但有的编译器可能不遵循标准,对fflush(stdin)操作不予警告,并且有时可能产生正确的结果,但最好不要这样使用。
[quote]若我们使用setbuf(stdin, buf)来修改系统提供的缓冲区为我们指定的缓冲区,我猜想这时候可以使用fflush(stdin),我已试验了几例都可以,哪位仁兄可以帮忙在linux下试试,我的系统坏了。

所以这种猜测是错误的,不可仿效。至于说MSDN说可以fflush(stdin),估计是
MS的VC的特性吧,但这样编出的程序也就只能属于VC了。
2、为什么C标准规定fflush(stdin)是未定义的呢?其实,fflush()对其他流的刷新也有可能会失败。以下给出几类fflush()可能失败的情况:

The fflush() function shall fail if:
[EAGAIN]
The O_NONBLOCK flag is set for the file descriptor underlying stream and the thread would be delayed in the write operation.  
[EBADF]
The file descriptor underlying stream is not valid.  
[EFBIG]
An attempt was made to write a file that exceeds the maximum file size.  
[EFBIG]
An attempt was made to write a file that exceeds the process' file size limit.  
[EFBIG]
The file is a regular file and an attempt was made to write at or beyond the offset maximum associated with the corresponding stream.  
[EINTR]
The fflush() function was interrupted by a signal.  
[EIO]
The process is a member of a background process group attempting to write to its controlling terminal, TOSTOP is set, the process is neither ignoring nor blocking SIGTTOU, and the process group of the process is orphaned. This error may also be returned under implementation-defined conditions.  
[ENOSPC]
There was no free space remaining on the device containing the file.  
[EPIPE]
An attempt is made to write to a pipe or FIFO that is not open for reading by any process. A SIGPIPE signal shall also be sent to the thread.  
The fflush() function may fail if:
[ENXIO]
A request was made of a nonexistent device, or the request was outside the capabilities of the device.


3、
关于函数setbuf(stream, buf)和setvbuf(stream, buf, mode, BUFSIZ)
1)   setbuf(stream, buf)中,当buf不是空指针时,它应该(shall be)等同于setvbuf(stream, buf, _IOFBF, BUFSIZ);setbuf(stream, buf)中,当buf是空指针时,它应该(shall be)等同于setvbuf(stream, buf, _IONBF, BUFSIZ).
2)   setbuf(stream, buf)中buf的大小至少应为BUFSIZ(DEV-C++为512bytes),所以前面讨论的设置buf[10]对于普通PC机是错误的,自己定义缓冲区大小应比标准规定的大(C89规定为256bytes)。
3)   还有一点需要注意:调用setbuf()分配的BUFSIZ个字节的空间不一定表示这些空间都来用作缓冲区(With setbuf(), allocating a buffer of BUFSIZ bytes does not necessarily imply that all of BUFSIZ bytes are used for the buffer area.《http://www.opengroup.org/onlinep ... nctions/setbuf.html》)


4)   像楼主的程序中在main()函数内部调用setbuf(stream, buf),buf声明为一个局部变量是有问题的,因为是操作系统来完成流操作的大部分后台(关闭、打开等)工作。main()函数完成后,buf被释放,操作系统就找不到了流的缓冲区,这样一来就引发了错误。所以自定义缓冲区一定得是全局变量或静态变量。
引用Eric Sosman的话:
      
You've asked stdin to use a buffer that will cease to exist as soon as main() returns, but when main() returns stdin is still "alive" and hasn't been closed. If any shutting-down operations try to make use of the now- vanished buffer, there's no telling what could happen.  (True, this is usually more troublesome for output streams than for input, but you're begging for trouble.)
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP