初学C语言的问题思考

本文探讨了C语言编程中如何处理用户输入,特别是如何通过改进scanf和使用字符串处理来避免缓冲区溢出。作者分享了从错误处理到使用gets_s和atoi转换输入的经验,以及如何确保程序健壮性,涉及字符输入、整数转换和输入验证技巧。
摘要由CSDN通过智能技术生成

        在老师布置作业的要求写一些小程序时,肯定是要与用户进行交互,比如输入1继续,输入0退出。

        而做一个交互界面肯定是需要一个while(1)循环,让用户在不慎输入错误的情况下能重新输入。第一次在写的时候通常这样干:

 int Menu(void)
{
    printf("是否继续(输入1继续,输入0退出)");
    int choice;
    scanf("%d",&choice);
    if(choice==1)
        {
            /*......*/
            return 1;
        }
    else
        return -1;
}

这样写似乎是没有问题,至少输入2,或者3下面的程序是没有问题的

#include<stdio.h>
int Menu(void);
int main(void)
{
    /*......*/
    while(1)
    {
        int Check=Menu();
        if(Check==1)
            break;
        else
            printf("输入有误,请重新输入!\n");
    }
    
    /*......*/
}
 int Menu(void)
{
    printf("是否继续(输入1继续,输入0退出)");
    int choice;
    scanf("%d",&choice);
    if(choice==1)
        {
            /*......*/
            return 1;
        }
    else
        return -1;
}

但是如果输入的是a3你会发现程序就崩溃了,不停地输出,循环无法结束。

因为scanf并未能成功读取a3,从a开始(包括a)的数据都被留在了缓冲区


提到缓冲区这里就简单说下scanf:

它本身是一个函数。

函数原型为int scanf(const char *format, ...);

调用格式scanf("<格式化字符串>", <参量表>);

scanf("%s")是不会读取空格

scanf从缓冲区读取数据,我们从键盘输入的数据按enter后就会进入缓冲区。

返回值就是成功读取的个数,而老师上课时候根本没有强调这一点,只是告诉我们要按照格式输入,从来不详细展开如果输入的数据与格式不匹配会发生什么。

这也是我第一次上C语言课时,vs有如下警告我不是很明白:

 若我们对代码进行改进,把scanf放在一个if判断句里面就可以消除这个警告。

#include<stdio.h>
int Menu(void);
int main(void)
{
    /*......*/
    while(1)
    {
        int Check=Menu();
        if(Check==1)
            break;
        else
            printf("输入有误,请重新输入!\n");
    }
    
    /*......*/
}
 int Menu(void)
{
    printf("是否继续(输入1继续,输入0退出)");
    int choice;
    if(scanf("%d",&choice)&&choice==1)
        {
            /*......*/
            return 1;
        }
    else
        return -1;
}

回到上面的问题:

输入的是a3。那么scanf开始依次读取,遇到a,与%d不匹配,结束录入,返回1

为什么要强调依次?因为你输入1a3的时候虽然也是非法输入,但scanf是从1开始读取,遇到1,格式匹配成功读取,寻找下个,发现是a,不匹配,结束录入。所以choice就被赋值为1,而当choice为1的时候,Menu返回1,循环可以退出,就这段而言是能正常进行(也就是有选项)。但如果输入的是2a3可么仍然会崩溃,因为choice被赋值为2,但我们的判断是if(&&choice==1)才返回1,因此Menu返回-1,主函数Check不为1,循环仍继续进行,这里缓冲区是有a3残留,因此scanf不给我们机会重新输入,从缓冲区读取,然后失败返回-1,循环继续......

那么到这里也就能明白为什么程序输入非数字会有问题。

那么怎么解决呢?

我们可以把所有的输入一律视为字符串的输入就可以了

下面是改进的程序:

#include<stdio.h>
#include<string.h>
int Menu(void);
int main(void)
{
    /*......*/
    while (1)
    {
        int Check = Menu();
        if (Check == 1)
            break;
        else
            printf("输入有误,请重新输入!\n");
    }

    /*......*/
}
int Menu(void)
{
    printf("是否继续(输入1继续,输入0退出)");
    char choice[200];
    gets_s(choice, 200);
    if (strcmp(choice,"1")==0)
    {
        /*......*/
        return 1;
    }
    else
        return -1;
}

数组的大小可以自己定,这里我写的200。

如果你觉得char choice[200]这种还是有一定缺陷,建议去了解下C++的string类。

gets因为可以无限读取,易发生溢出,因此C11标准中废除了这个函数。

这里用的是get_s代替,如你所见,加上了最大读取数量200,以保证数组不会越界。


到这里我们的问题也就解决的差不多了,但是还没完。

我是以字符形式读取,虽然说是一定程度上保证输入的健壮性,但如果函数里面需要我们输入的数字呢?比如说我输入个50,函数里面需要运用,但我是以字符串形式读取的那该怎么办呢?

最直接的想法那肯定是转化成整形

刚好C语言也实现了这个函数:atoi

在头文件stdlib.h里面

int atoi(const char* str)

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int Menu(void);
int main(void)
{
    /*......*/
    while (1)
    {
        int Check = Menu();
        if (Check == 1)
            break;
        else
            printf("输入有误,请重新输入!\n");
    }

    /*......*/
}
int Menu(void)
{
    printf("是否继续(输入1继续,输入0退出)");
    char choice[200];
    gets_s(choice, 200);
    if (strcmp(choice,"1")==0)
    {
        int t=atoi(choice);
        printf("%d",t);
        /*......*/
        return 1;
    }
    else
        return -1;
}

这里直接运行下程序你可以看到t确实是被赋值为了1。


以上便是我个人在C语言学习的过程中遇到的一些问题给出自己的想法,若有不对请指出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值