сбой функции glibc strtoull ()

У меня проблема с функцией библиотеки 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 

glibc, прикрепленный к 32-битному exe,

   # 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

glibc, прикрепленный к 64-битному exe,

 # 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 показывает правильный вывод, но в некоторых системах он тоже ведет себя ненормально. Почему strtoull в 32-битном exe так себя ведет и как решить эту проблему?


person RKum    schedule 01.10.2013    source источник
comment
5368709120 & (0xffffffff) = 1073741824   -  person Leeor    schedule 01.10.2013
comment
Ваш код делает #include <stdlib.h>, не так ли?   -  person alk    schedule 01.10.2013
comment
нет, я не. В том числе решает проблему.   -  person RKum    schedule 01.10.2013


Ответы (3)


Итак, мы установили, что это совершенно очевидно происходит из-за переполнения, поскольку значение соответствует тому, что произошло бы, если бы оно было преобразовано в 32-битное int.

Однако это не объясняет всего - вы использовали strtoull, а не более короткий strtoul, и он действительно работает с 64-битным двоичным кодом. Во всяком случае, я был удивлен, увидев, что вы даже можете вызвать более длинную версию в своей 32-битной сборке (кстати, как вы ее собрали, с -m32? Или на специальной машине?)

Эта ссылка повышает вероятность того, что существует какое-то явление связи, из-за которого strtoull объявляется как int strtoll() (предположительно, система не может поддерживать исходную версию библиотеки), поэтому мы получаем значение, неявно переданное через int, прежде чем копировать его обратно в unsigned long long.

В любом случае - компилятор должен был предупредить об этом, попробуйте установить его на c99 и повысить уровень предупреждений, возможно, это заставит его кричать

person Leeor    schedule 01.10.2013
comment
Согласованный. Это может быть что-то столь же глупое, как отсутствие заголовков для printf и понижение параметра до int. - person domen; 01.10.2013
comment
Я не включил stdlib.h. В том числе решает проблему. - person RKum; 01.10.2013

Думаю, это из-за переполнения. int в 32-битном формате не может содержать такое большое число (макс. 4294967296). Как сказал Лиор, 5368709120 & (0xffffffff) = 1073741824.

Тип int имеет минимальную ширину 32 бита и только 32 бита в большинстве (если не во всех) системах.

person CS Pei    schedule 01.10.2013

Скорее всего, вы забыли #include <stdlib.h> и, вероятно, не включили какие-либо предупреждения компилятора (например, при использовании необъявленных функций).

Когда компилятор C видит вызов необъявленной функции, он слепо принимает int f(int) в качестве прототипа. В вашем случае возвращаемое значение strtoull() будет int, поэтому значение будет усечено до 32-битного.

(Это действительно довольно странно, что вы получаете правильный результат в 64-битной системе, где int обычно также просто 32-битный.)

person Johannes Overmann    schedule 06.10.2020