【C语言陷阱】00_scanf函数输入含空格时的陷阱

前言

本文同时作为【C语言编码练习】的第00节,主要记录遇到的关于scanf函数输入含空格时的陷阱问题。

问题场景:

  • 将一句话的单词进行倒置,标点不倒置。比如 “I like beijing.”,经过处理后变为:“beijing. like I”。
    字符串长度不超过100。
  • 输入描述:
    输入一个仅包含小写字母、空格、‘.’ 的字符串,长度不超过100。
    ‘.’ 只出现在最后一个单词的末尾。
  • 输出描述:
    依次输出倒置之后的字符串,以空格分割。

问题描述

首先,写了问题简化的倒置如下:

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

int main()
{
    char a[100];
    char *p=a;
    int count=0;
    printf("Please enter a string(<100):\n");
    gets(a);
    scanf("%s",&a);
    for(;*p!=0;p++)
    count++;
    printf("The string in reverse order is:\n");
    for(int i=0;i<count;i++){
        p--;
        printf("%c",*p);   
    }
    printf("\n");
    system("pause");
}

然后按题目要求写时发现这样问题,简化代码如下:

int main()
{
    char a[100];
    char *p=a;
    int count=0;
    printf("Please enter a string(<100):\n");
    scanf("%s",&a);
    for(;*p!='\0';p++)
    count++;
    printf("%d\n",a[3]); 

  • 以上代码输入’abc aabc’,a[3]及以后元素打印均为0.

原因分析:

  • scanf()函数原型:
int scanf(const char * restrict format,...);
  • scanf()遇见空格则会停止扫描。如下(5)说明。

(1)在高版本的 Visual Studio 编译器中,scanf 被认为是不安全的,被弃用,应当使用scanf_s代替 scanf。
(2) 对于字符串数组或字符串指针变量,由于数组名可以转换为数组和指针变量名本身就是地址,因此使用scanf()函数时,不需要在它们前面加上"&“操作符。
(3) scanf函数中没有类似printf的精度控制。
如: scanf(”%5.2f",&a); 是非法的。不能企图用此语句输入小数为2位的实数。
(4) scanf中要求给出变量地址,如给出变量名则会出错
如 scanf(“%d”,a);是非法的,应改为scanf(“%d”,&a);才是合法的。
(5) 在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔,则可用空格,制表符或回车作间隔。
C编译在碰到空格,制表符,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束。
(6) 在输入字符数据(%c)时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。


解决方案:

如果使用gets这个函数获得标准输入流(键盘)上的字符串的话就不会出现这种问题。

  • 作者使用gets重写如下:
    (本方法后半部分当时出于节省内存的目的,采用了直接输出打印的方式,导致程序可读性变差,若同前半部分一样先倒置在数组中,然后再打印,可提高可读性。在此不在赘述。)
#include<stdio.h>
#include<stdlib.h>

int main()
{
    char a[100],b[100];
    char *p=a;
    int count=0,dig=0,xx;
    
    printf("Please enter a string(<100):\n");
    gets(a);
    for(;*p!='\0';p++)
    count++;//长度
    printf("The string in reverse order is:\n");
    for(int i=0;i<count;i++)
    {
        p--; 
        b[i]=*p;   
    }
    
    while(dig<count)
    {
        for (; dig < count; dig++) 
        {
            int tmp = dig;
            while (dig < count && b[dig] != ' ')
                ++dig;
            xx=dig;
            for(int x=xx-1;(x+1)!=tmp;--x)
            {
                printf("%c",b[x]);
            volatile  int y=y;  
            }
            printf("%c",b[dig]);
            tmp=dig+1;
        }
    }
    printf("\n");
    system("pause");
}

网上答案(使用了库函数):

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
	string str;
	getline(cin, str);
	reverse(str.begin(), str.end()); // 先完全倒置一遍
	for (size_t i = 0; i < str.size(); ++i) {
		int tmp = i;
		while (i < str.size() && str[i] != ' ') // 注意别越界 查找单词分界
			++i;
		reverse(str.begin() + tmp, str.begin() + i); // 倒置每一个单词
	}
	cout << str << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不僈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值