C语言GETS函数和Scanf函数在终止输入时的区别

首先我们来看一个例子,代码如下:

int main()
{
    char str[50],ch1[50];

    scanf("%s",ch1);
    gets(str);
    printf("ch1的值为:");
    puts(ch1);
    printf("str的值为:");
    puts(str);
    printf("输出完毕\n");

    return 0;
}

代码很简单就是分别输入两个字符串然后分别输出。但如果我们只输入hello,然后就回车,就会发现程序结束,输出结果如下:
在这里插入图片描述
从输出结果我们可以看出,scanf函数接收的输入值为hello,gets函数接收的输入值为空(注意gets函数的输入值不是回车换行,因为puts函数会自带一个回车换行),这是为什么呢?我的本意是换行后接着输入第二个字符串,怎么就结束了。

下面我们梳理一下这个输入的流程就应该明白了为什么会导致这个结果。首先我们在终端输入字符时,这些字符是保存到缓冲区的,然后在我们按下回车时字符才进入标准输入流中,等待scanfgets这些输入函数读取,并且回车换行符也进入了标准输入流中。首先就是scanf函数读取字符,scanf函数遇见空格、制表符以及换行符这些空白字符时就会结束读取,所以scanf函数在读到换行符时就认为输入结束,换行符继续留在标准输入流中,故它的输入值为hello。然后就是gets函数开始执行,gets函数发现标准输入流中还有未读取的字符,故继续在标准输入流中读取字符,但它读到的第一个字符就是回车换行符,而gets函数输入结束的标志就是回车换行符,所以它在读第一个字符时就停止读取,函数结束返回,故它的输入值为空。并且gets函数会将标准输入流清空。再次调用输入函数,需要在终端重新输入。

总结一下,造成这样的原因就在于scanf函数的输入结束符是空白字符,也就是空格、制表符、和换行符这些,而gets函数的输入结束符是回车换行符。因为gets函数所读取的第一个字符就是回车换行符,所以gets函数的输入内容为空。

另外值得注意的就是用scanf函数输入字符串时,它是从第一个非空白字符开始读取,而gets函数无这个特性。

<think>我们正在讨论C语言getsscanf函数输入字符串区别。 首先,让我们回顾一下这两个函数: 1. gets函数: - 原型:char *gets(char *str); - 功能:从标准输入(stdin)读取一行,并将其存储到str所指向的字符串中。它会一直读取,直到遇到换行符(由Enter键产生)或文件结束符(EOF)为止。 - 换行符会被丢弃,不会存储在字符串中,然后在字符串末尾自动添加空字符('\0')。 2. scanf函数: - 当使用%s格式scanf用于读取字符串。 - 原型:int scanf(const char *format, ...); - 使用示例:scanf("%s", str); - 功能:它会跳过前导空白字符(如空格、制表符、换行符),然后读取非空白字符,直到遇到空白字符或文件结束符为止。然后,它会自动在末尾添加空字符。 主要区别: a) 空格处理: - gets可以读取包含空格的整行(直到换行符),因此它读取的字符串可以包含空格。 - scanf使用%s,遇到空格就会停止读取,因此它不能读取包含空格的字符串(只能读取一个单词)。 b) 缓冲区溢出风险: - gets函数不检查目标数组的容量,如果输入的行过长,会导致缓冲区溢出,这是一个严重的安全问题。因此,在C99标准中已被弃用,在C11标准中已被移除。 - scanf虽然也有风险,但可以通过指定字段宽度来避免(尽管很多程序员没有这样做)。例如,使用`scanf("%10s", str);`可以确保最多读取10个字符(因为还要一个位置放'\0',所以实际最多存储9个字符)。如果不指定宽度,同样有溢出风险。 c) 返回值: - gets在成功返回传入的指针,失败或遇到文件结束符返回NULL。 - scanf返回成功匹配赋值的输入项数,如果失败或遇到文件结束符,则返回EOF。 d) 换行符处理: - gets会读取并丢弃换行符(换行符不会放入字符串,而是替换为'\0')。 - scanf使用%s,不会读取换行符,换行符会留在输入缓冲区中。这可能会影响后续的输入读取。 由于gets函数的不安全性,推荐使用fgets函数来代替。fgets可以指定最大读取字符数,因此更安全。 下面给出两个例子: 示例1:使用gets(注意:不推荐使用) ```c #include <stdio.h> int main() { char str[100]; printf("Enter a string: "); gets(str); // 输入整行,包括空格,但存在溢出风险 printf("You entered: %s\n", str); return 0; } ``` 示例2:使用scanf(只能读取一个单词) ```c #include <stdio.h> int main() { char str[100]; printf("Enter a string: "); scanf("%s", str); // 遇到空格就停止 printf("You entered: %s\n", str); return 0; } ``` 示例3:安全地使用fgets替代gets ```c #include <stdio.h> int main() { char str[100]; printf("Enter a string: "); fgets(str, sizeof(str), stdin); // 安全,指定了缓冲区大小 // 注意:fgets会保留换行符(如果有的话),我们可以手动去掉 int len = strlen(str); if (len > 0 && str[len-1] == '\n') { str[len-1] = '\0'; // 去掉换行符 } printf("You entered: %s\n", str); return 0; } ``` 总结: - 避免使用gets,因为它不安全。 - 如果只需要读取一个单词(无空格),可以使用scanf,但要注意限制宽度以避免溢出。 - 如果要读取整行(包含空格),推荐使用fgets并处理掉末尾的换行符(如果有)。 §§相关问题§§: 1. C语言中为什么gets函数被弃用? 2. fgetsgets有什么区别? 3. 如何安全地使用scanf读取字符串而不导致缓冲区溢出? 4. 在C语言中,如何处理输入缓冲区中残留的换行符? </think>### C语言中`gets``scanf`输入字符串区别 #### 核心区别: 1. **空格处理能力** - `gets()` 读取整行(包括空格),直到遇到换行符 - `scanf("%s", ...)` 遇到空格/换行会终止输入 2. **安全性** - `gets()` 不检查缓冲区边界,存在溢出风险 - `scanf()` 可通过字段宽度限制预防溢出(如`%10s`) 3. **行尾处理** - `gets()` 丢弃换行符 - `scanf()` 保留空白字符在输入缓冲区中 --- ### 代码示例与说明 #### 1. `gets()` 示例(危险!) ```c #include <stdio.h> int main() { char buffer[5]; // 仅容纳4字符+结尾符 printf("Enter text: "); gets(buffer); // 输入超过4字符将溢出! printf("You entered: '%s'\n", buffer); return 0; } ``` **风险**:输入 `"12345"` 会覆盖临近内存,可能导致程序崩溃。 #### 2. 安全的 `scanf()` 用法 ```c #include <stdio.h> int main() { char buffer[5]; printf("Enter text: "); scanf("%4s", buffer); // 限制读取4字符 printf("Safe input: '%s'\n", buffer); // 清空输入缓冲区 int c; while ((c = getchar()) != '\n' && c != EOF); return 0; } ``` **关键点**: - `%4s` 确保最多读取4字符 - `while` 循环清除缓冲区残余字符 --- ### 推荐替代方案:使用 `fgets()` ```c #include <stdio.h> #include <string.h> int main() { char buffer[5]; printf("Enter text: "); fgets(buffer, sizeof(buffer), stdin); // 安全限定长度 // 移除可能的换行符 if (strlen(buffer) > 0 && buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = '\0'; printf("Safe with fgets: '%s'\n", buffer); return 0; } ``` **优势**: - 明确指定缓冲区大小 - 自动处理换行符(需手动移除) - 无溢出风险
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值