Linux C 字符函数 getchar()、putchar() 与 EOF 详解
首先给出《The_C_Programming_Language》这本书中的例子:
#include <stdio.h> int main()
{
int c;
c = getchar();
while (c != EOF)
{
putchar();
c = getchar();
} return 0;
}
这里主要解释下为什么要用int型来接受getchar函数。
很多时候,我们会写这样的两行代码:
char c;
c = getchar();
这样就很有可能出现问题。因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux下)即文件结束符EOF时,getchar ()的返回EOF,这个EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不正确的。
下面用一个题目来看其实如何获取字符和输出字符的。
#include "stdio.h" main()
{
char c, d, e, f;
printf("please input two characters:\n"); c = getchar();
putchar(c);
putchar('\n'); d = getchar();
putchar(d);
putchar('\n'); e = getchar();
putchar(e);
putchar('\n'); f = getchar();
putchar(f);
putchar('\n'); printf("c=%c\n", c);
printf("d=%c\n", d);
printf("e=%c\n", e);
printf("f=%c\n", f);
}
运行后先输入“12”,回车,再输入“34”,回车。
运行环境是 CentOS gcc
运行结果:
please input two characters:
12
1
234
3
c=1
d=2
e=f=3
下面具体解释一下:
getchar函数每次从缓冲区中得到一个字符,putchar函数每次输出一个字符。
首先输入了两个字符12,然后回车,注意这时写入缓存中的有3个字符 1,2,回车。
程序中有四个getchar(),于是c='1',d='2',e='\n'。
这时运行到 f=getchar(); 输入缓存中的三个字符均被前三个getchar获取,这时需要用户输入,
这里输入了34
于是f=3,4和后面的回车没有被利用。
这便是整个流程。
基于C语言 EOF 与 getchar() 的使用详解
大师级经典的著作,要字斟句酌的去读,去理解。以前在看K&R的The C Programming Language(SecondEdition)
第1.5节的字符输入/输出,被getchar()和EOF所迷惑了。可能主要还是由于没有搞清楚getchar()的工作原理和EOF的用法。因此,感觉很有必要总结一下,不然,很多琐碎的知识点长时间过后就会淡忘的,只有写下来才是最好的方法。
其实,getchar()最典型的程序也就几行代码而已。本人所用的环境是DebianGNU/Linux,在其他系统下也一样。
一、getchar的两点总结:
1、getchar是以行为单位进行存取的。
当用getchar进行输入时,如果输入的第一个字符为有效字符(即输入的不是文件结束符EOF,Windows下为组合键Ctrl+Z, Unix/Linux下为组合键Ctrl+D),那么只有当最后一个输入字符为换行符'\n'(也可以是文件结束符EOF,EOF将在后面讨论)时, getchar才会停止执行,整个程序将会往下执行。譬如下面程序段:
int c;
while ((c = getchar()) != EOF)
{
putchar(c);
}
执行程序,输入:abc,然后回车。则程序就会去执行puchar(c),然后输出abc,这个地方不要忘了,系统输出的还有一个回车。然后可以继续输入,再次遇到换行符的时候,程序又会把那一行的输入的字符输出在终端上。
对于getchar,肯定很多初学的朋友会问,getchar不是以字符为单位读取的吗?那么,既然我输入了第一个字符a,肯定满足while循环(c = getchar()) != EOF的条件啊,那么应该执行putchar(c)在终端输出一个字符a。不错,我在用getchar的时候也是一直这么想的,但是程序就偏偏不照这样执行,而是必需读到一个换行符或者文件结束符EOF才进行一次输出。
对这个问题的一个解释是,在大师编写C的时候,当时并没有所谓终端输入的概念,所有的输入实际上都是按照文件进行读取的,文件中一般都是以行为单位的。因 此,只有遇到换行符,那么程序会认为输入结束,然后采取执行程序的其他部分。同时,输入是按照文件的方式存取的,那么要结束一个文件的输入就需用到EOF (Enf Of File), 这也就是为什么getchar结束输入退出时要用EOF的原因。
2、getchar()的返回值一般情况下是字符,但也可能是负值,即返回EOF。
这里要强调的一点就是,getchar函数通常返回终端所输入的字符,这些字符系统中对应的ASCII值都是非负的。因此,很多时候,我们会写这样的两行代码:
char c;
c = getchar();
这样就很有可能出现问题。因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux下)即文件结束符EOF时,getchar()的返回EOF,这个EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不 正确的。为了能够让所定义的变量能够包含getchar函数返回的所有可能的值,正确的定义方法如下(K&R C中特别提到了这个问题):
int c;
c = getchar();
二、EOF的两点总结(主要指普通终端中的EOF)
1、EOF作为文件结束符时的情况:
EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符。
(1)、遇到getcahr函数执行时,要输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
(2)、在前面输入的字符为换行符时,接着输入Ctrl+D;
(3)、在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,至于第一次的Ctrl+D的作用将在下面介绍。
其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符。
2、EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入。
这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D并不是文件 结束符,而只是相当于换行符的功能,即结束当前的输入。以上面的代码段为例,如果执行时输入abc,然后Ctrl+D,程序输出结果为:
abcabc
注意:
第一组abc为从终端输入的,然后输入Ctrl+D,就输出第二组abc,同时光标停在第二组字符的c后面,然后可以进行新一次的输入。这时如果再次输入Ctrl+D,则起到了文件结束符的作用,结束getchar()。
如果输入abc之后,然后回车,输入换行符的话,则终端显示为:
abc // 第一行,带回车
abc // 第二行
// 第三行
其中第一行为终端输入,第二行为终端输出,光标停在了第三行处,等待新一次的终端输入。
从这里也可以看出Ctrl+D和换行符分别作为行结束符时,输出的不同结果。
EOF的作用也可以总结为:当终端有字符输入时,Ctrl+D产生的EOF相当于结束本行的输入,将引起getchar()新一轮的输入;当终端没有字符 输入或者可以说当getchar()读取新的一次输入时,输入Ctrl+D,此时产生的EOF相当于文件结束符,程序将结束getchar()的执行。
【补充】
本文第二部分中关于EOF的总结部分,适用于终端驱动处于一次一行的模式下。也就是虽然getchar()和putchar()确实是按照每次一个字符 进行的。但是终端驱动处于一次一行的模式,它的输入只有到“\n”或者EOF时才结束,因此,终端上得到的输出也都是按行的。
如果要实现终端在读一个字符就结束输入的话,下面的程序是一种实现的方法(参考《C专家编程》,略有改动)
#include <stdio.h>
#include <stdlib.h> int main(void)
{
int c; // 终端驱动处于普通的一次一行模式
system("stty raw"); // 现在的终端驱动处于一次一个字符模式
c = getchar();
putchar(c); // 终端驱动处又回到一次一行模式
system("stty cooked"); return 0;
}
参考:
http://www.cnblogs.com/hdchild/archive/2009/11/19/1606457.html
《C陷阱与缺陷》 chap5.1
http://www.cnblogs.com/younes/archive/2010/05/31/1748002.html
Linux C 字符函数 getchar()、putchar() 与 EOF 详解的更多相关文章
- Linux下的I/O复用与epoll详解(转载)
Linux下的I/O复用与epoll详解 转载自:https://www.cnblogs.com/lojunren/p/3856290.html 前言 I/O多路复用有很多种实现.在linux上,2 ...
- linux管道命令grep命令参数及用法详解---附使用案例|grep
功能说明:查找文件里符合条件的字符串. 语 法:grep [-abcEFGhHilLnqrsvVwxy][-A<显示列数>][-B<显示列数>][-C<显示列数>] ...
- linux基础之LSB定义的常用目录详解
Linux基础之LSB定义的基本目录详解 1.LSB中FHS(Filesystem Hierarchy Standard)定义的一些文件 /boot:主要是存放引导文件的目录,比如内核文件(vmlin ...
- Linux下nginx编译安装教程和编译参数详解
这篇文章主要介绍了Linux下nginx编译安装教程和编译参数详解,需要的朋友可以参考下 一.必要软件准备1.安装pcre 为了支持rewrite功能,我们需要安装pcre 复制代码代码如下: # y ...
- linux ssh使用深度解析(key登录详解)
linux ssh使用深度解析(key登录详解) SSH全称Secure SHell,顾名思义就是非常安全的shell的意思,SSH协议是IETF(Internet Engineering Task ...
- Linux文本处理三剑客之grep及正则表达式详解
Linux文本处理三剑客之grep及正则表达式详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Linux文本处理三剑客概述 grep: 全称:"Global se ...
- Linux shell利用sed如何批量更改文件名详解[转载]
需求背景: 目录A用来存放自动化的包生成的apk文件,现在开发修改了包名的 命名规则:环境名称(pro|uat)-release-日期-v版本号.apk 原来的是思路是通过正则表达式匹配新的包名,但 ...
- 【转帖】linux sort,uniq,cut,wc,tr,xargs命令详解
linux sort,uniq,cut,wc,tr,xargs命令详解 http://embeddedlinux.org.cn/emb-linux/entry-level/201607/21-5550 ...
- Linux/centos/redhat下各种压缩解压缩方式详解
1.zip命令 zip -r myfile.zip ./* 将当前目录下的所有文件和文件夹全部压缩成myfile.zip文件,-r表示递归压缩子目录下所有文件. 2.unzip unzip -o -d ...
随机推荐
- JS入门学习,写一个简单的选项卡
/* 经过昨天一整天的纠结和摸索.总结下学习初期我最致命的几个问题…… 1.var oDiv = document.getElementById(''); 一定要多输,熟悉后o u什么的字母别搞 ...
- 三维网格补洞算法(Radial Basis Function)
在逆向工程中,由于设备或模型的原因,我们获取得到的三维模型数据往往并不完整,从而使得生成的网格模型存在孔洞,这对后续的模型分析会造成影响.下面介绍一种基于径向基函数(RBF:Radial Basis ...
- 小讲堂:在线编辑在Mobox文档管理软件中的意义
今天我们来讨论一下,mobox文档管理软件中的在线编辑的这个功能,相信这个功能是用户在日常的文档维护中非常需要的. 文档管理软件的诸多功能中,在线编辑是一块很重要的功能点,因为在线编辑可以说是提高工作 ...
- httpd安装.md
httpd 简介 httpd是由apache软件基金会开发的一款著名的web服务器软件.由于其开放源代码,并且拥有跨平台.功能强大.安全稳定等特性,而被广泛使用.早期httpd是在修修补补的基础上成长 ...
- c# 传递Null的string值导致的调用C++的dll报错 Attempted to read or write protected memory.
c# 调用C++的dll报错 Attempted to read or write protected memory: 原因是:c# 传递Null的string值导致的,将Null改为string ...
- ajax实现下拉菜单无刷新加载更多
$(function() { var page = 1; var discount = $('#discount'); var innerHeight = window.innerHeight; va ...
- Qt——动态库的创建和使用
一.动态库是什么 很多人写程序的人都见过.lib和.dll文件,对动态库也略有耳闻. 生成动态库后可以得到两个文件,后缀名分别是.lib以及.dll. 简而言之,.lib称为导入库,相当于头文件:.d ...
- mybatis的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- scala速成记录1
选择 Learning Scala这本书,两百多页,足够薄. 安装 http://www.scala-lang.org/ 下载Binary的版本.bin里边有所有操作系统下运行的可以运行的交互式s ...
- FineUI小技巧(6)自定义页面回发
前言 FineUI中的绝大部分回发事件都是由控件触发了,比如按钮的点击事件,下拉列表的改变事件,表格的排序分页事件.但有时我们可能会要自己触发页面回发,这时就要知道怎么使用 JavaScript 来做 ...