【问题标题】:unsigned long int accepts out of range valuesunsigned long int 接受超出范围的值
【发布时间】:2017-06-29 19:13:50
【问题描述】:

这是我对这段代码的第二个问题。 此代码来自“Sams 自学 C 编程”第 7 版第 66 页上的修改脚本。 把书上的剧本抄下来后,我(只是一点点)修改了它,让它更有趣。

  • 我添加了一些额外的常量和变量等,使其能够接受更大的输入/输出值。
  • 因为输入/输出是“unsigned long int”类型,其范围为 0 - 2147483647,我将其设置为 LIMIT。
  • 然后我在程序的最后添加了一个“else”来处理高于该值的所有输入。我只是想做一个实验,看看它是否会记录超出范围的值。
  • 我第一次发布关于代码中的数学错误的问题,但我自己没有找到。已解决。

以下是一些示例输出:

[bad_cat@KittyLitter LearningCode]$ gcc SamsC.04.seconds.c
[bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ): 
2147483646
2147483646 seconds is equal to 68 y 35 d, 3 h, 14 m, and 6 s
[bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ): 
2147483647
2147483647 seconds is equal to [bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ): 
2147483650
-2147483646 seconds is equal to error: -2147483646 is an excessive amount of seconds.
range for seconds must be between 0 and 2147483647!
If number of seconds exceeds 2147483647, then it is beyond range for type 'int'.
EXITING seconds program. 
[bad_cat@KittyLitter LearningCode]$ ./a.out
Enter the number of seconds ( > 0, < 2147483647 ): 
9876543210
1286608618 seconds is equal to 40 y 291 d, 7 h, 16 m, and 58 s
[bad_cat@KittyLitter LearningCode]$ 

代码如下:

/* Illustrates the modulus operator. */
/* inputs a number of seconds, and converts to hours, minutes, and seconds. */
// minute = 60 seconds                  : 60
// hour = 60 * 60 seconds               : 3600
// day = 24 * 60 * 60 seconds           : 86400
// year = 365 * 24 * 60 * 60 seconds    : 31536000

#include <stdio.h>                                      // from original script

/* Define constants */

#define SECS_PER_MIN 60                                 // from original script
#define MIN_PER_HOUR 60
#define HOURS_PER_DAY 24
#define DAYS_PER_YEAR 365
#define SECS_PER_YEAR 31536000
#define SECS_PER_HOUR 3600                              // from original script
#define SECS_PER_DAY 86400
#define LIMIT 2147483647

unsigned seconds, minutes, hours, days, years, secs_remain, mins_remain, hours_remain, days_remain;     // modified from original script

int main(void)                                                                                          // from original script
{
    seconds = minutes = hours = days = years = secs_remain = mins_remain = hours_remain = days_remain = 0;
    /* Input the number of seconds. */

    printf( "Enter the number of seconds ( > 0, < %d ): \n", LIMIT );                  // modified from original script
    scanf( "%d", &seconds );                                                                            // from original script

    years = seconds /SECS_PER_YEAR;
    days = seconds / SECS_PER_DAY;
    hours = seconds / SECS_PER_HOUR;                                                                    // from original script
    minutes = seconds / SECS_PER_MIN;                                                                   // from original script
    days_remain = days % DAYS_PER_YEAR;
    hours_remain = hours % HOURS_PER_DAY;
    mins_remain = minutes % MIN_PER_HOUR;                                                               // modified from original script
    secs_remain = seconds % SECS_PER_MIN;                                                               // from original script

    printf( "%d seconds is equal to ", seconds );                                                       // from original script

    if ( seconds < SECS_PER_HOUR )
    {
         printf( "%d m, and %d s\n", minutes, secs_remain );
         return 0;
    }
         else if((seconds >= SECS_PER_HOUR ) && (seconds < SECS_PER_DAY ))
         {
              printf( "%d h, %d m, and %d s\n", hours, mins_remain, secs_remain );                  // from original script
              return 0;                                                                             // from original script
         }    
         else if((seconds >= SECS_PER_DAY ) && (seconds < SECS_PER_YEAR ))
         {
              printf( "%d d, %d h, %d m, and %d s\n", days, hours_remain, mins_remain, secs_remain );
              return 0;
         }            
         else if((seconds >= SECS_PER_YEAR ) && (seconds < LIMIT ))
         {
             printf( "%d y %d d, %d h, %d m, and %d s\n", years, days_remain, hours_remain, mins_remain, secs_remain );
             return 0;
         }   
         else if(seconds > LIMIT )
         {
             printf("error: %d is an excessive amount of seconds.\n", seconds);
             printf("range for seconds must be between 0 and %d!\n", LIMIT );
             printf("If number of seconds exceeds %d, then it is beyond range for type 'int'.\n", LIMIT );
             printf("EXITING seconds program. \n");
             return 1;
         }
    }

谢谢,我已将所有 "%d" 更改为 "%u" 和 Limit。 我也把它放在一起以供将来参考:

#include <stdio.h>
#include <limits.h>

int main(void)
{
    printf("\n");

    printf("The number of bits in a byte =\t\t\t\t %d\n", CHAR_BIT);
    printf("The maximum number of bytes in a multi-byte character =\t %d\n", MB_LEN_MAX);

    printf("The minimum value of SIGNED CHAR =\t\t\t %d\n", SCHAR_MIN);
    printf("The maximum value of SIGNED CHAR =\t\t\t %d\n", SCHAR_MAX);
    printf("The maximum value of UNSIGNED CHAR =\t\t\t %d\n", UCHAR_MAX);

    printf("The minimum value of SHORT INT =\t\t\t %d\n", SHRT_MIN);
    printf("The maximum value of SHORT INT =\t\t\t %d\n", SHRT_MAX);
    printf("The maximum value for an UNSIGNED SHORT INT =\t\t %u\n", USHRT_MAX); 

    printf("The minimum value of INT =\t\t\t\t %d\n", INT_MIN);
    printf("The maximum value of INT =\t\t\t\t %d\n", INT_MAX);
    printf("The maximum value for an UNSIGNED INT =\t\t\t %u\n", UINT_MAX);

    printf("The minimum value of CHAR =\t\t\t\t %d\n", CHAR_MIN);
    printf("The maximum value of CHAR =\t\t\t\t %d\n", CHAR_MAX);

    printf("The minimum value of LONG =\t\t\t\t %ld\n", LONG_MIN);
    printf("The maximum value of LONG =\t\t\t\t %ld\n", LONG_MAX);
    printf("The maximum value for an UNSIGNED LONG INT =\t\t %Lu\n", ULONG_MAX);

    printf("\n");

    return(0);
}

【问题讨论】:

  • 问题是?
  • unsigned long int 的范围不是这样,除非你有一个非常奇怪的 31 位架构。
  • 您使用的是 64 位系统吗?然后 GCC 将为long 使用 64 位。
  • 为什么不阅读 scanf 的手册页并注意返回值以及 %d 的含义
  • 顺便说一下,我没有看到您在显示的代码中的任何地方使用unsigned long?您的所有变量都是 unsigned 类型,这意味着 unsigned int,自过去 30 年左右以来,在所有现代平台上都是 32 位。

标签: c variables int range


【解决方案1】:

您似乎正在尝试确保用户不会为您的 seconds 变量输入太大的值。因此,您将一个值读入seconds,然后(除其他外)您尝试检查它是否太大。但问题是,当你将一个太大的值读入seconds 时,你无法真正说出它是什么,因为该值太大,seconds 无法容纳,所以它被转换为其他一些价值!

现在,比这复杂一点,因为您已将 seconds 声明为 unsigned int。您的系统上seconds 的实际范围很可能是从 0 到 4294967295。因此,当您输入像 2147483648 这样的值时,seconds 可以保持这个值——尽管它大于LIMIT,所以你可以通过else 子句。

但是,当您通过else 子句时,您使用%d 打印出seconds 的值。但%d 用于有符号整数,而不是无符号整数。所以这就是为什么你会看到那些奇怪的负值。

我不确定这是否回答了您的问题,并且我不确定您真正希望您的代码做什么(也就是说,您是否真的更愿意使用有符号或无符号算术),所以我不提供任何“固定”代码供您尝试。但我希望这有助于解释发生了什么。如果您还有其他问题,请尽管提问。


附录:这里实际发生的情况是,当它们溢出时,无符号整数会从过高的值“环绕”回 0。此外,虽然不能保证,但有符号整数也会从它们自己的值开始环绕太高的价值回到大的、负的、太低的价值。

对于 32 位整数,值在 4294967296 的循环中循环。所以无符号可以从 0 到 4294967295,如果你尝试在 4294967296 中卡住,你会得到 0,如果你尝试 4294967297,你会得到 1,并且如果你尝试 4294967298,你会得到 2,等等。

签名后,合法值从 -2147483648 变为 +2147483647,如果您尝试插入 +2147483648,则会得到 -2147483648,如果尝试插入 +2147483649,则会得到 -2147483647,等等。

因此,由于 2147483650 大于 2147483647,因此它不适合,您得到的值是 2147483650 - 4294967296 = -2147483646。 9876543210呢?太大了,9876543210 - 4294967296 = 5581575914 仍然太大,但 5581575914 - 4294967296 = 1286608618 确实合适。所以这就是为什么你在上一个例子中得到了那个奇怪的数字 1286608618。

【讨论】:

  • 先生,我需要和你聊天吗?
  • 你能告诉我这个问题的答案吗stackoverflow.com/questions/42162536/…
  • @Steve Summit,谢谢,你把它固定下来了。我把所有的“%d”都改成了“%u”,然后改了LIMIT =“4294967295”,问题立马解决了。
  • @Vasqi 这些事情发生了什么?
  • @Vasqi 它定义了你的系统上的最大值。在其他系统上,它可能是 65535,或者可能是 18446744073709551615。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-29
  • 1970-01-01
  • 2012-01-27
  • 1970-01-01
  • 2019-07-15
  • 1970-01-01
相关资源
最近更新 更多