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


    前言

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

    问题场景:

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

    问题描述

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

    #include
    #include
    
    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");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    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]); 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 以上代码输入’abc aabc’,a[3]及以后元素打印均为0.

    原因分析:

    • scanf()函数原型:
    int scanf(const char * restrict format,...);
    
    • 1
    • 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
    #include
    
    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");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

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

    #include 
    #include 
    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    【计算机扫盲】计算机的基础操作你知多少?
    Apipost:API研发团队的协同利器
    爬虫脚本代理池调度
    stm32之28.ADC
    JSP足球场地预约平台系统myeclipse定制开发mysql数据库网页模式java编程jdbc
    入门人工智能 —— 使用 Python 进行文件读写,并完成日志记录功能(4)
    Linux:基础开发工具之Makefile和缓冲区的基本概念
    UPC2022暑期个人训练赛第23场(Credit Card Payment)
    加速迈入云原生时代,国产数据库行业要变天
    SpringBoot篇之集成Mybatis-plus
  • 原文地址:https://blog.csdn.net/weixin_43490708/article/details/126774060