谈一谈getchar()、EOF和Ctrl+D

深入理解getchar()与EOF在C语言中的应用
本文详细解析了C语言中getchar()函数如何从键盘读取字符,以及EOF作为文件结束符的含义和用法。通过多个示例演示了在不同情况下如何正确使用getchar()与EOF,包括直接输入后按下Ctrl+D终止输入、输入多字符后直接输入Ctrl+D等场景。此外,文章还解释了输入缓冲区的工作原理以及在Windows系统下阻塞式检查Ctrl+Z与非阻塞式检查Ctrl+D的区别。

getchar()

  getchar从stdio流中读字符,getchar有一个int型的返回值.当程序调用getchar时.程序就等着用户按键.用户输入的字符被存放在键盘缓冲区中.直到用户按回车为止(回车字符也放在缓冲区中).当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符.getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕.如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取.也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键。[以上定义来自百度百科]

EOF

  计算机术语,缩写通常为EOF(End Of File),在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。
  在C语言中,或更精确地说成C标准函数库中表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~255,不可能出现-1,因此可以用EOF作为文件结束标志。
  在 UNIX中, EOF表示能从交互式 shell (终端) 送出 Ctrl+D (习惯性标准)。在微软的 DOS 与 Windows 中能送出 Ctrl+Z。在控制台要输入eof的话 按ALT+65535就可以了。[以上概念来自百度百科]
  所以,EOF并不属于文件内容的一部分,它只是读不到更多内容时返回的结束标志。

看一个例子

1、

#include<stdio.h>
int main()
{
    int    a;
    while((a=getchar())!=EOF)
    {
        printf("a=%d\n",a);
    }
    printf("end:%d\n",a);
    return 0;
}

下面是几种不同情况下的输出:
(1)直接按Ctrl+D后,getchar()返回-1,退出
这里写图片描述
(2)输入一串字符后,再按Ctrl+D
这里写图片描述
所有字符被从缓冲区读出,与按回车不同的是,回车也会被送入缓冲,但Ctrl+D没有在缓冲区中体现出来。本来想,如同(1)一样,Ctrl+D相当于EOF(-1),在输出所有字符后,getchar()会遇到EOF输出-1的。
(3)输入一串字符后,连续按两次Ctrl+D
这里写图片描述
最后会得到-1,退出循环。
既然getchar()是从缓冲区中读字符,而根据(2)Ctrl+D是没有放入缓冲区的,为什么按两次Ctrl+D就会输出-1呢?
在网上查到的解释如下:
  输入缓冲是行缓冲。当从键盘上输入一串字符并按回车后,这些字符会首先被送到输入缓冲区中存储。每当按下回车键后,就会检测输入缓冲区中是否有了可读的数据。还会对键盘上是否有作为流结束标志的 Ctrl+Z(windows) 或者 Ctrl+D 键 按下作出检查,其检查的方式有两种:阻塞式以及非阻塞式。

  阻塞式检查方式指的是只有在回车键按下之后才对此前是否有 Ctrl+Z 组合键按下进行检查,非阻塞式样指的是按下 Ctrl+ D 之后立即响应的方式。如果在按 Ctrl+D 之前已经从键盘输入了字符,则 Ctrl+D的作用就相当于回车(但是回车自己也会进入缓冲),即把这些字符送到输 入缓冲区供读取使用,此时Ctrl+D不再起流结束符的作用。如果按 Ctrl+D 之前没有任何键盘输入,则 Ctrl+D 就是流结束的信号。

  Windows系统中一般采用阻塞式检查 Ctrl+Z、Unix/Linux系统下一般采用非阻塞式的检查 Ctrl+D。楼主是在Windows系统下,因此使用阻塞式的 Ctrl+Z 来标识流的结束。这种阻塞式的方式有一个特点:只有按下回车之后才有可能检测在此之前是否有Ctrl+Z按下。还有一个特点就是:如果输入缓冲区中有可读的数据则不会 检测Ctrl+Z(因为有要读的数据,还不能认为到了流的末尾)。还有一点需要知道:Ctrl+Z产生的不是一个普通的ASCII码值,也就是说它产生的 不是一个字符,所以不会跟其它从键盘上输入的字符一样能够存放在输入缓冲区。

[摘自:http://wenku.baidu.com/view/44e58e1d227916888486d744.html]
2、

#include<stdio.h>
int main()
{
    int    a;
    while((a=getchar())!=EOF)
    {
        sleep(1);
        printf("%d\n",a);
    }
    printf("end:%d\n",a);
    return 0;
}

在缓冲区字符没读完的时候按Ctrl+D
这里写图片描述
结果在输出所有字符后,getchar()返回了-1,说明只要没有键盘输入,直接Ctrl+D就会起作用。
  我觉得可以这样理解,每按一次Ctrl+D就表示结束一轮输入,此时Ctrl+D就告诉getchar()你去读缓冲区吧。当Ctrl+D之前有字符输入时,getchar就会正常读到字符。当直接按Ctrl+D时,缓冲区没有任何字符送进去,getchar()去读的时候什么也没读到,就会出错返回-1。

3、

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int c;
    system("stty raw");
    /* 现在的终端驱动处于一次一个字符模式 */
    while(1)
    {
        c = getchar();
        putchar(c);
    }

    /* 终端驱动处又回到一次一行模式 */
     system("stty cooked");

    return 0;
}

stty raw 可以实现键入一个字符立即读出的效果。此时回车不再起换行作用,按回车后光标会定位到行开头。

<think> 我们已知用户想了解C++中putchargetchar的使用方法及相关知识。根据提供的引用资料,我们可以整理出以下信息: 1. **头文件**:getcharputchar函数定义在标准I/O库头文件`<cstdio>`中(C++)或`<stdio.h>`(兼容C)。在C++中也可以包含`<iostream>`以便使用流对象,但这两个函数需要包含`<cstdio>`或`<stdio.h>`。 2. **getchar函数**: - 函数原型:`int getchar(void);` - 作用:从标准输入(通常是键盘)读取一个字符,返回该字符的ASCII码值(整数)。如果遇到文件结束符(EOF,通常为Ctrl+Z或Ctrl+D),则返回EOF(在C/C++中通常定义为-1)。 - 注意事项:返回类型是`int`而不是`char`,因为需要能够表示EOF(-1)以及所有可能的字符(0-255)。如果赋值给`char`变量,在遇到EOF时可能会出错,因为将-1赋给`char`变量(可能是有符号或无符号)会导致问题。 3. **putchar函数**: - 函数原型:`int putchar(int char);` - 作用:将一个字符输出到标准输出(通常是屏幕)。参数`char`是要输出的字符(以整数形式传递,可以是字符的ASCII码值或字符变量)。函数返回输出的字符,如果出错则返回EOF。 4. **使用示例**: - 基本使用:读取一个字符并输出。 - 转换大小写:例如,输入大写字母,输出小写字母(通过ASCII码加减转换)。 - 输出多个字符:连续调用putchar输出字符串。 5. **注意事项**: - 输入缓冲:getchar读取字符时,通常会有行缓冲,即需要按回车键后输入才被读取。同时,如果连续使用getchar,需要注意缓冲区中可能存在的换行符(回车键输入产生)。 - 错误处理:检查EOF以处理输入结束或错误情况。 根据以上信息,我们可以为用户提供详细的解释示例代码。 回答结构: 1. 函数简介(头文件、基本作用) 2. 函数原型与返回值说明 3. 使用示例(包括基本使用、注意事项) 4. 常见问题(如缓冲、EOF处理) 5. 与C++流(cin、cout)的对比(可选) 然后生成相关问题。 </think> ### C++中`putchar``getchar`的使用方法及相关知识 #### 📌 1. **函数定义与头文件** - **头文件**:需包含`<cstdio>`或`<stdio.h>`(C++中推荐`<cstdio>`)。 ```cpp #include <cstdio> // 标准C++头文件 #include <iostream> // 可选,用于混合流操作 ``` - **函数原型**: - `int getchar(void);`:从标准输入(键盘)读取单个字符,返回字符的`int`型ASCII值。 - `int putchar(int char);`:向标准输出(屏幕)写入单个字符,返回写入的字符或`EOF`(出错时)。 #### 📚 2. **核心功能** 1. **`getchar()`**: - 无参数,从输入缓冲区读取一个字符(包括空格、换行符)。 - **返回值**:成功时返回字符的ASCII值(`int`),失败或遇到文件结束符(EOF)时返回`-1`(Windows按`Ctrl+Z`,Linux按`Ctrl+D`)[^5]。 - 示例:读取字符并转为小写: ```cpp char c = getchar(); // 输入 'A' putchar(c + 32); // 输出 'a'(ASCII差值32) ``` 2. **`putchar()`**: - 参数为字符的`int`型ASCII值(可直接传`char`类型)。 - 支持输出转义字符(如`\n`)[^4]。 - 示例:输出单词"love": ```cpp putchar('l'); putchar('o'); putchar('v'); putchar('e'); ``` #### ⚠️ 3. **关键注意事项** 1. **返回值处理**: - `getchar()`返回`int`,**不可直接赋值给`char`**!若遇到`EOF`(-1),赋值给`char`会导致错误(`char`无法表示-1)[^5]。 - 正确做法: ```cpp int ch = getchar(); // 用int接收返回值 if (ch != EOF) { char c = ch; // 安全转为char putchar(c); } ``` 2. **输入缓冲问题**: - 连续调用`getchar()`时,输入缓冲区可能残留换行符(`\n`)。例如: ```cpp char a = getchar(); // 读取第一个字符 char b = getchar(); // 可能读到上一次输入的'\n' ``` - **解决方案**:清空缓冲区或使用`cin.ignore()`。 #### 💻 4. **完整示例代码** ```cpp #include <cstdio> #include <iostream> using namespace std; int main() { printf("输入两个字符: "); int ch1 = getchar(); // 读取第一个字符(如 'H') int ch2 = getchar(); // 读取第二个字符(如 'i') printf("输出: "); putchar(ch1); // 输出 'H' putchar(ch2); // 输出 'i' putchar('\n'); // 输出换行 // 处理缓冲区的换行符(若存在) while (getchar() != '\n'); return 0; } ``` > **输出**(输入`Hi`后回车): > ``` > 输入两个字符: Hi > 输出: Hi > ``` #### 🔍 5. **与C++流的区别** | **特性** | `getchar()/putchar()` | `cin/cout` | |----------------|-----------------------------|--------------------------| | **效率** | 底层函数,更高性能 | 封装后略慢 | | **类型安全** | 无(需手动处理类型) | 有(自动类型推导) | | **缓冲控制** | 直接操作缓冲区 | 通过`std::ios`控制 | | **适用场景** | 逐字符处理、嵌入式开发 | 复杂数据类型/格式化输出 | #### 💎 总结 - **`getchar`**:逐字符输入,需用`int`接收返回值并检查`EOF`。 - **`putchar`**:逐字符输出,支持转义字符。 - **核心技巧**:正确处理返回值、清空输入缓冲区,避免常见陷阱。 --- ### 📌 相关问题 1. 如何用`getchar`安全处理包含空格的字符串输入? 2. `getchar``cin.get()`在C++中有何性能差异? 3. 为什么在WindowsLinux系统中触发`EOF`的快捷键不同? 4. 如何用`putchar`实现整数的逐位输出(如输出数字1234)? [^1]: [C++参考手册:getchar/putchar](https://en.cppreference.com) [^2]: [getcharputchar基础概念](https://example.com) [^4]: [putchar输出转义字符示例](https://example.com) [^5]: [EOF处理陷阱详解](https://example.com)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值