鱼C论坛

 找回密码
 立即注册
查看: 1350|回复: 4

[已解决]关于s1e13 动动手2进阶疑问

[复制链接]
发表于 2022-6-6 22:37:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
题目是模拟atoi函数,进行提取字符串里第一段数字。在进阶题目里,小甲鱼要求对于溢出int的数值报错。网上很多帖子给出两种判断方式:
(1)你的如何短时间计算出2^ 31?(2)因为x是int型,即使x溢出,由于存储的限制,那么x的值也必然在这个区间范围内(这里不考虑类型升级)。

针对(1)如何快速计算2^ 31次方:简单粗暴的用16进制表示出来(),或者利用位运算。将最大值最小值记作int_max 和int_min。

针对(2)我们可以先比较x/10与int_max/10的大小, 再比较最后一位,即可避免上面的尴尬之处。

两种方式在说明什么,我没有看懂。答案里,小甲鱼利用了第一种方式,也就是牺牲时间来计算int的最大值和最小值,然后计算。说真的,我也没看懂。
现在说一下我的思路:
  出现溢出,必然是在计算过程中。当一个数值(我们这个题目里只考虑正数,没有把符号计入要提取的范围)接近溢出的时候,已经很大了。溢出刚刚发生的时候,得到的数字会很小。那么我利用临时变量,对提取前后的数值进行了比较,如果数值突然变小,就认为是溢出了。
  我感觉这个方法虽然只针对了本题,但是会比小甲鱼的思路要简洁一点。
  [b][b]请各位大佬指正我这思路的不妥之处(除了只能针对本题外)[/b][/b]。
小甲鱼的程序:
#include <stdio.h>
#include <math.h>

int main()
{
        int ch;
        long long num = 0;
        long long temp; // 临时变量,用于测试是否超出范围
        int is_overflow = 0;

        const int max_int = pow(2, sizeof(int) * 8) / 2 - 1;
        const int min_int = pow(2, sizeof(int) * 8) / 2 * (-1);

        printf("请输入待转换的字符串:");

        do
        {
                ch = getchar();

                if (ch >= '0' && ch <= '9')
                {
                        temp = 10 * num + (ch - '0');
                        if (temp > max_int || temp < min_int)
                        {
                                is_overflow = 1;
                                break;
                        }
                        else
                        {
                                num = temp;
                        }
                }
                else
                {
                        if (num)
                        {
                                break; // 如果已有数字,则退出循环
                        }
                }

        }
        while (ch != '\n');

        if (is_overflow)
        {
                printf("数值超出范围,结果未定义!\n");
        }
        else
        {
                if (!num)
                {
                        printf("并未找到任何数值!\n");
                }
                else
                {
                        printf("结果是:%d\n", num);
                }
        }

        return 0;
}


我写的程序:
#include<stdio.h>

int main()
{
   int ch,num=0,num0;//ch为输入的字符,num存储结果用的整形数值
   printf("请输入待转换的字符串:");
do
  {
    ch=getchar();
   

        if(ch>='0'&&ch<='9')
        {
        num0=num;//临时存储,用于比较是否溢出
        num=10*num+(ch-'0');//按照10进制记录已读入的值
                if(num<num0)break;  //溢出了,跳出循环,不再读取字符。
        }
        else if(num)   break;  //不是数字,则跳出循环。
          
   }

   
   while ((ch!='\n'));
   if(num<num0)         //溢出时打印结果
       
        printf("数值超出范围,结果未定义!\n");
       
   else  if(num!=0)     //正常时打印结果
        printf("结果是:%d\n",num);
   else
        printf("并未找到任何数值!\n");     //没有数字时打印结果



return 0;
}
最佳答案
2022-6-7 23:51:06
确实奇怪,为什么会没有人帮你呢?
我帮你看了看,你的思路是对的
但是这么写不是很好,我的调试器检查出了这个溢出
还是强制转换一下unsigned类型吧
$ cat main.c
#include <stdio.h>

int main() {
    int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num0 = num; //临时存储,用于比较是否溢出
#if 1
            num = 10 * num + (ch - '0'); //按照10进制记录已读入的值
#else
            num = (unsigned)10 * num + (ch - '0'); //按照10进制记录已读入的值
#endif
            if(num < num0)
                break; //溢出了,跳出循环,不再读取字符。
        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:2147483647
结果是:2147483647
$ ./main
请输入待转换的字符串:2147483648
main.c:11:17: runtime error: signed integer overflow: 2147483640 + 8 cannot be represented in type 'int'
数值超出范围,结果未定义!
$
$
$
$ vim main.c
$ cat main.c
#include <stdio.h>

int main() {
    int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num0 = num; //临时存储,用于比较是否溢出
#if 0
            num = 10 * num + (ch - '0'); //按照10进制记录已读入的值
#else
            num = (unsigned)10 * num + (ch - '0'); //按照10进制记录已读入的值
#endif
            if(num < num0)
                break; //溢出了,跳出循环,不再读取字符。
        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:2147483648
数值超出范围,结果未定义!
$


如果不考虑负数的话,可以直接作为无符数来计算
超过了0x80000000就是溢出(包括这个数)
$ cat main.c
#include <stdio.h>

int main() {
    //int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    int ch;
    unsigned int num = 0;
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num = num * 10 + (ch - '0');

            if(num >= 0x80000000) break; //溢出了,跳出循环,不再读取字符。

        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num >= 0x80000000) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:2147483648
数值超出范围,结果未定义!
$ ./main
请输入待转换的字符串:2147483647
结果是:2147483647
$


你的这个程序还有一个bug
0是不是数字,0是数字
但是
$ cat main.c
#include <stdio.h>

int main() {
    int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num0 = num; //临时存储,用于比较是否溢出
#if 1
            num = 10 * num + (ch - '0'); //按照10进制记录已读入的值
#else
            num = (unsigned)10 * num + (ch - '0'); //按照10进制记录已读入的值
#endif
            if(num < num0)
                break; //溢出了,跳出循环,不再读取字符。
        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:0
并未找到任何数值!
$ ./main
请输入待转换的字符串:abcd1234
结果是:1234
$ ./main
请输入待转换的字符串:abcd0
并未找到任何数值!
$ ./main
请输入待转换的字符串:abcd0000
并未找到任何数值!
$

这个bug自己改吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-6-7 21:18:20 | 显示全部楼层
没有人来看看么?好奇怪,我感觉应该不是标题的问题,应该是网页排序造成的,否则怎么会浏览量一直为0呢。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-6-7 23:51:06 | 显示全部楼层    本楼为最佳答案   
确实奇怪,为什么会没有人帮你呢?
我帮你看了看,你的思路是对的
但是这么写不是很好,我的调试器检查出了这个溢出
还是强制转换一下unsigned类型吧
$ cat main.c
#include <stdio.h>

int main() {
    int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num0 = num; //临时存储,用于比较是否溢出
#if 1
            num = 10 * num + (ch - '0'); //按照10进制记录已读入的值
#else
            num = (unsigned)10 * num + (ch - '0'); //按照10进制记录已读入的值
#endif
            if(num < num0)
                break; //溢出了,跳出循环,不再读取字符。
        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:2147483647
结果是:2147483647
$ ./main
请输入待转换的字符串:2147483648
main.c:11:17: runtime error: signed integer overflow: 2147483640 + 8 cannot be represented in type 'int'
数值超出范围,结果未定义!
$
$
$
$ vim main.c
$ cat main.c
#include <stdio.h>

int main() {
    int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num0 = num; //临时存储,用于比较是否溢出
#if 0
            num = 10 * num + (ch - '0'); //按照10进制记录已读入的值
#else
            num = (unsigned)10 * num + (ch - '0'); //按照10进制记录已读入的值
#endif
            if(num < num0)
                break; //溢出了,跳出循环,不再读取字符。
        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:2147483648
数值超出范围,结果未定义!
$


如果不考虑负数的话,可以直接作为无符数来计算
超过了0x80000000就是溢出(包括这个数)
$ cat main.c
#include <stdio.h>

int main() {
    //int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    int ch;
    unsigned int num = 0;
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num = num * 10 + (ch - '0');

            if(num >= 0x80000000) break; //溢出了,跳出循环,不再读取字符。

        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num >= 0x80000000) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:2147483648
数值超出范围,结果未定义!
$ ./main
请输入待转换的字符串:2147483647
结果是:2147483647
$


你的这个程序还有一个bug
0是不是数字,0是数字
但是
$ cat main.c
#include <stdio.h>

int main() {
    int ch, num = 0, num0; // ch为输入的字符,num存储结果用的整形数值
    printf("请输入待转换的字符串:");
    do {
        ch = getchar();
        if(ch >= '0' && ch <= '9') {
            num0 = num; //临时存储,用于比较是否溢出
#if 1
            num = 10 * num + (ch - '0'); //按照10进制记录已读入的值
#else
            num = (unsigned)10 * num + (ch - '0'); //按照10进制记录已读入的值
#endif
            if(num < num0)
                break; //溢出了,跳出循环,不再读取字符。
        } else if(num) break; //不是数字,则跳出循环。
    } while((ch != '\n'));

    if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");
    else if(num != 0) //正常时打印结果
        printf("结果是:%d\n", num);
    else
        printf("并未找到任何数值!\n"); //没有数字时打印结果

    return 0;
}
$ gcc-debug -o main main.c
$ ./main
请输入待转换的字符串:0
并未找到任何数值!
$ ./main
请输入待转换的字符串:abcd1234
结果是:1234
$ ./main
请输入待转换的字符串:abcd0
并未找到任何数值!
$ ./main
请输入待转换的字符串:abcd0000
并未找到任何数值!
$

这个bug自己改吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-6-7 23:58:54 | 显示全部楼层
    if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");

这个报错说的很好了,数值超出范围,结果未定义!
既然结果未定义,那你怎么能依赖这个未定义的结果来写这个程序呢?

我的调试器捕捉到了这个问题,报了下面这个错误
强制转换一下unsigned类型就可以了
main.c:11:17: runtime error: signed integer overflow: 2147483640 + 8 cannot be represented in type 'int'
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-6-8 08:20:16 | 显示全部楼层
人造人 发表于 2022-6-7 23:58
if(num < num0) //溢出时打印结果
        printf("数值超出范围,结果未定义!\n");

嗯,确实是这个问题。在帖子里我也说了,这个题目只考虑了正数。做为对着视频学习的初学者,我尽力了
至于“结果未定义”的说法是在完成小甲鱼作业过程中,小甲鱼的题目要求的输出内容。
我把自己的思路贴出来的想法是看看大家都有什么样的思路。感觉这个溢出问题在网上查帖子只找到两个办法,应该有别的办法没有被找到。
谢谢你的帮助和提醒。
我是用的虚拟机,前几天才在别人的帮助下学会了使用xshell操作,发帖的时候想要尝试<>里带编号发,怕被说水贴封了,没敢试。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-17 12:30

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表