1

我遇到了 c 库函数 strtoull 的问题,它返回了错误的输出。

   int main(int argc, char *argv[])
   {
    unsigned long long int intValue;

    if(atoi(argv[2]) == 1)
    {   
        intValue = strtoull((const char *)argv[1], 0, 10 );
    }
    else
    {
//  ...
    }

    printf("intValue of %s is %llu \n", argv[1], intValue);

    return 0;
    }   

我构建了它们并生成了 32 位和 64 位可执行文件作为 str32_new 和 str64_new。但是从 32 位 exe 收到的输出是错误的,因为返回了错误的数字:

strtoull 应该为传递的字符串“5368709120”返回编号 5368709120,但它返回给我 1073741824。

# ./str32_new "5368709120" 1
intValue of 5368709120 is 1073741824 

我注意到,当我从字符串中减少一个字符时,它会显示正确的输出。

# ./str32_new "536870912" 1
intValue of 536870912 is 536870912 

附加到 32 位 exe 的 glibc 是

   # readelf -Wa /home/str32_new | grep strt
  [39] .shstrtab         STRTAB          00000000 002545 000190 00      0   0  1
  [41] .strtab           STRTAB          00000000 0032f8 0002a4 00      0   0  1
  0804a014  00000607 R_386_JUMP_SLOT        00000000   strtoull
     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND strtoull@GLIBC_2.0 (2)
    55: 00000000     0 FILE    LOCAL  DEFAULT  ABS strtoull.c
    75: 00000000     0 FUNC    GLOBAL DEFAULT  UND strtoull@@GLIBC_2.0
    77: 08048534   915 FUNC    GLOBAL DEFAULT   15 my_strtoull

附加到 64 位 exe 的 glibc 是

 # readelf -Wa /home/str64_new | grep strt
  [39] .shstrtab         STRTAB          0000000000000000 001893 000192 00      0   0  1
  [41] .strtab           STRTAB          0000000000000000 002cd0 00029b 00      0   0  1
  0000000000601028  0000000700000007 R_X86_64_JUMP_SLOT     0000000000000000 strtoull + 0
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtoull@GLIBC_2.2.5 (2)
    57: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS strtoull.c
    73: 00000000004006cc   804 FUNC    GLOBAL DEFAULT   15 my_strtoull
    82: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtoull@@GLIBC_2.2.5

64 位 exe 显示正确的输出,但在某些系统上它也表现异常。为什么 32 位 exe 中的 strtoull 行为如此以及如何解决此问题?

4

3 回答 3

2

好的,所以我们已经确定这很明显是由于溢出而发生的,因为该值与转换为 32 位 int 时会发生的情况相匹配。

然而,这并不能解释一切——您确实使用了 strtoull,而不是较短的 strtoul,它确实适用于 64 位二进制文​​件。如果有的话,我很惊讶地看到您甚至能够在您的 32 位构建中调用更长的版本(顺便说一下,您是如何构建它的,使用 -m32?还是在特殊机器上?)

这个链接,提出了一些链接现象使得 strtoull 被声明为int strtoll()(大概系统不支持原始 lib 版本)的可能性,所以我们通过 int 隐式转换的值,然后复制回你的 unsigned long long .

无论哪种方式 - 编译器都应该警告这一点,尝试将其设置为 c99 并提高警告级别,也许这会让它大喊大叫

于 2013-10-01T15:33:31.913 回答
1

我认为这是由于溢出。int在 32 位中不能容纳这么大的数字(最大值是4294967296)。正如李奥所说,5368709120 & (0xffffffff) = 1073741824.

该类型int至少为 32 位宽,并且在大多数(如果不是全部)系统上只有 32 位宽。

于 2013-10-01T14:56:33.247 回答
0

您很可能忘记了,#include <stdlib.h>并且您可能没有启用任何编译器警告(例如使用未声明的函数)。

当 C 编译器看到对未声明函数的函数调用时,它会盲目地将其假定int f(int)为原型。在您的情况下,返回值strtoull()将是 int,因此该值将被截断为 32 位。

(在 64 位系统上得到正确结果确实很奇怪,其中 int 通常也只是 32 位。)

于 2020-10-06T19:35:12.563 回答