scanf函数和回车、空格 及其返回值

本文深入解析了C语言中scanf函数的工作原理,特别是与%c、%s格式符相关的输入处理,包括回车、空格的特殊作用,以及如何解决连续输入时的常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

scanf和回车的关系:

先看一个我们经常使用的代码:
  
  char c;
  scanf("%c", &c);
  scanf 的各种格式中,%d、%c、%s三种最常用,通常都是以回车作为一次输入的结束。由于对字符解析方式及字符特点不同(如数字中一般不会出现空格回车等特殊字符)不同,%d格式一般不会在连续输入时,遇到问题。而%s和%c却会出现各种各样的问题,比如回车问题,空格问题等。

我们已经习惯了上面的代码,再看下面存在连续输入时的情况:
  int i;
  char c;
  scanf("%d", &i);
  scanf("%c", &c);
  如果我们为i赋值为3,并以回车的方式结束对i的赋值。再按常规思路为c赋值时,就会发现并不能为c赋值,并且c自动赋值为十进制的10,也就是回车键。why???

要回答这个问题,先看scanf函数的功能和工作原理:

scanf函数这个函数的作用是从标准输入设备获取输入值,并存储到参数列表中指针所指向的内存单元,如果读入成功,函数会返回读入成功的数据的个数;scanf函数的结束通常有3种,遇到空格、回车或者tab键为常用的结束方式;或者按照格式控制符的指定来控制结束,如%5d类的格式;遇到非法输入也会自动结束。

对常用的三种格式,结束符号分别如下:

%d格式输入,默认分隔符是所有的 white-spaces(空格、回车、制表);

%c格式输入,则按ASCII字符考虑,无分隔符。可能会受到之前输入的影响,必要时用fflush(stdin);清除缓冲区;

%s 是 字符串格式,默认分隔符是所有的 white-spaces,输入后自动加入结束符"\0"。

继续上面的由于连续输入带来的问题,往深了说,就涉及到缓冲区了。scanf函数是以删除的方式从缓冲区读取数据(缓冲区中存储来自标准输入的数据)。如果缓冲区是空的,就阻塞之,等待从键盘输入;并且scanf还能对数字输入忽略先导的空白符,如\n\t和空格等(注意,对字符输入并不忽略先导字符,这个也是很自然的道理,因为\n\t和空格在字符中都是合法的字符)。

scanf的缓冲机制和对字符的处理方式就造成了scanf对字符%c和字符串%s的读取时的各种意外。比如上面的例子,回头分析这段代码:

输入了i的值为3然后按回车,当前缓冲区中数据为”3\n”,由回车的作用scanf开始从缓冲区中读取一个%d控制的数据,也就是3,此时缓冲区中还剩”\n”;对下一个scanf函数的格式控制是%c,这个情况下并不忽略先导的空白字符\n,而是直接赋给字符c了。想要查看缓冲区的内容,stdin[s1] ,如果想要查看当前stdin中的内容,一般方法都比较忤逆,可以尝试使用文件操作freopen将stdin中的数据重定向到另外一个file*中。这个另辟一文。

如何解决这个缓冲区和字符解析的问题呢?既然缓冲区有我们不需要的东西,那就清除缓冲区。微软系统中是fflush(stdin)函数可以清除缓冲区,而有的编译系统并没有定义对stdin的fflush操作,就把stdin中的数据读出来:

  1. 可以使用fgets()函数,这个函数没有编译器的限制;

  2. 或者把缓冲区中多余的东西交给别的函数,如getchar(),具体代码为 while( (c = getchar() ) != ‘/n’ && c != EOF );

  3. 上面的方法基于原理,但有点麻烦,尤其是遇到字符%c和字符串%s对回车的处理时。C还提供了gets()函数解决了这个问题,gets()函数是不论中间有什么字符,一律读进来,直到遇到回车符;

  4. C++中,还可以操作stdin的指针,stdin是一个File*类型的数据结构,使stdin->_IO_read_ptr = stdin->_IO_read_end;。但是C中不可以。

scanf和空格键

前面就有说,scanf函数,根据格式的不同,对空白字符的处理也不同。%d格式下,对空白字符不敏感,通常都是作为结束符的;对%c来说,对回车符比较敏感,空格的做为一个普通字符处理的;对%s来说,回车和空格都是当前函数的结束字符,由于缓冲区stdin机制,这里又要特别注意 空格和回车对%s的影响。

%s默认分隔符是所有的 white-spaces,输入后自动加入结束符"\0",使其成为一个字符串(之所以加上\0,是和字符数组char[]的结束符有关的,C中是没有string这个类型的,是使用char[]结构实现字符串)。值得注意的是,即使输入字符的长度足够,%s是宁愿舍弃输入字符,也要把/0加上去的,作为字符串的结束。并且,scanf会忽略缓冲区开头的空格,知道遇到一个非空格字符,才开始向内存中读取数据。

比如,我们想要输入”The C Programming Language.\n”,中间的空格怎么处理呢?

其中一个解决方法是使用gets函数,这个函数是以回车符作为输入结束的标志的;还有一种解决方式是:scanf("%[^\n]", c):

char c[15];
  scanf("%[^\n]", c[s2] );
  只是scanf("%[^\n]", c);和gets这两种处理方式都是不忽略所有的空格,包括缓冲区开头的空格;这一点和scanf("%s",c);的处理方式不同,%s的方式,忽略缓冲区开头的空格。

另:scanf的返回值

scanf()函数返回的值为:正确按指定格式输入变量的个数;也即能正确接收到值的变量个数。在类型匹配错误的时候,以非正常的方式退出。可以利用scanf函数的返回值判断输入是否正确,并进行流程控制:

 int i = 0;
 char c1[15];
 while((scanf("%c", &c1[i])!=EOF) && i<14)
 {
          i++;
 }

[s1]stdin是标准输入,一般针对键盘,是个FILE* 类型的数据。

[s2]这里注意使用c 还是 &c:

      c是个数组,数组 即代表了数组的首地址,所以可以使用c;

      &c,表示数组c的地址,也就是指首地址,所以也是可以的。但通常c更符合逻辑一点。这在读取%s类型的数据时可以说明这一点:

              scanf(“%s”, c);
<think>嗯,用户想了解scanf函数的用法。我得先回想一下scanf的基础知识。scanf是C语言中用于从标准输入读取数据的函数,属于stdio.h头文件。用户可能刚开始学C,所以需要从基础讲起。 首先,我应该解释scanf的基本语法,比如函数原型:int scanf(const char *format, ...); 但可能用更简单的方式描述,比如参数中的格式字符串变量地址。要强调地址操作符&的重要性,否则会导致错误,比如在变量前加&,但数组或指针的情况例外。 然后,可能需要举例说明不同的格式说明符,比如%d对应整数,%f对应float,%lf对应double,%c对应字符,%s对应字符串。例子要简单明了,比如读取整数浮点数的情况。 还要注意常见问题,比如输入缓冲区的问题。比如,当输入多个值时,用户可能用空格回车分隔,但有时会有残留字符,尤其是读取字符时,之前的换行符可能会被%c捕获,导致意外结果。这时候需要提示如何清空缓冲区,比如用getchar()或者处理格式字符串中的空格。 另外,scanf返回值很重要,它返回成功读取的项目数。用户可以检查这个返回值来判断输入是否有效,比如如果预期读一个整数,但用户输入字母,scanf会返回0,这时需要处理错误。 安全性的问题也不能忽视,比如使用%s时可能会有缓冲区溢出的风险,应该建议指定宽度,比如%10s来限制读取的字符数,或者使用更安全的函数如fgets。 用户可能还关心如何处理多个输入,比如同时读取多个不同类型的变量,这时候格式字符串中的空格分隔符如何处理,比如scanf("%d,%f", &a, &b)需要用户输入逗号分隔。 最后,要总结一下使用scanf的注意事项,比如正确使用格式符,处理返回值,避免缓冲区溢出,以及清空缓冲区的方法。可能还要对比一下其他输入函数,比如getsfgets的区别,但用户的问题主要集中在scanf,所以可能不需要展开。 需要确保语言口语化,避免使用太多术语,让初学者容易理解。同时结构要清晰,分点说明,比如基本用法、格式说明符、注意事项、常见问题示例。这样用户能一步步理解,并应用到实际编程中。</think>### scanf函数用法详解 --- #### **1. 基本功能** `scanf` 是C语言标准库(`<stdio.h>`)中的**格式化输入函数**,用于从标准输入(如键盘)读取数据,并按指定格式将数据存储到变量中。 --- #### **2. 函数原型** ```c int scanf(const char *format, ...); ``` - **参数**: - `format`:格式控制字符串(定义输入数据的类型格式)。 - `...`:可变参数列表,需传入变量的地址(用 `&` 符号取地址,字符串指针变量除外)。 - **返回值**: - 成功读取的变量数量;若输入与格式不匹配,返回 `EOF`(-1)。 --- #### **3. 常见格式说明符** | 说明符 | 用途 | 示例变量类型 | |--------|------------------------|--------------------| | `%d` | 读取整数 | `int` | | `%f` | 读取单精度浮点数 | `float` | | `%lf` | 读取双精度浮点数 | `double` | | `%c` | 读取单个字符 | `char` | | `%s` | 读取字符串(无空格) | `char数组` | | `%u` | 读取无符号整数 | `unsigned int` | | `%x` | 读取十六进制数 | `int` | --- #### **4. 基础用法示例** ```c #include <stdio.h> int main() { int num; float value; printf("请输入一个整数一个浮点数:"); scanf("%d %f", &num, &value); // 输入如:10 3.14 printf("num=%d, value=%.2f", num, value); return 0; } ``` --- #### **5. 注意事项** 1. **变量地址必须传递**: - 错误写法:`scanf("%d", num);` ❌ - 正确写法:`scanf("%d", &num);` ✅ - 例外:字符数组(字符串)不需要 `&`,如 `char str[20]; scanf("%s", str);` ✅ 2. **输入缓冲区问题**: - 若输入的数据与格式不匹配(如输入字母但要求数字),会导致后续输入失效。 - 解决方法:清空缓冲区(如循环 `while(getchar() != '\n');`)。 3. **字符串输入风险**: - `%s` 会读取到空格或换行符前停止,且可能溢出数组。 - 改进:指定宽度,如 `%19s` 限制最多读取19字符(保留1位给终止符 `\0`)。 4. **格式字符串中的空格**: - `scanf("%d,%d", &a, &b)` 要求输入如 `10,20`(逗号分隔)。 - `scanf("%d %d", &a, &b)` 允许用空格、Tab或换行分隔。 --- #### **6. 处理返回值** 通过返回值判断输入是否合法: ```c int a, b; if (scanf("%d %d", &a, &b) == 2) { printf("成功读取两个整数"); } else { printf("输入错误!"); } ``` --- #### **7. 常见错误案例** - **问题**:输入字符时残留换行符 ```c char ch; scanf("%c", &ch); // 若前面有其他输入,可能读到换行符 ``` - **解决**:在格式字符串前加空格 ```c scanf(" %c", &ch); // 空格会跳过空白字符 ``` --- #### **8. 对比其他输入函数** - `gets()`:读取整行(已弃用,不安全)→ 改用 `fgets()`。 - `getchar()`:读取单个字符。 - `fscanf()`:从文件读取(用法类似 `scanf`)。 --- #### **总结** `scanf` 是C语言中灵活但易出错的输入工具,需注意格式匹配、地址传递缓冲区管理。初学时建议结合调试工具逐步验证输入逻辑!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值