2016-03-02 2 views
0

Привет, я написал небольшую тестовую программу, чтобы проверить, как функция, которую я написал, для преобразования строки (шестнадцатеричного числа) в целое число без знака, и я обнаружил, что код ведет себя по-разному в зависимости от используемого компилятора или системы.Почему strtoul не работает должным образом?

я составил код, приведенный ниже на:
(1) ideone С ++ 4.3.2 https://ideone.com/LlcNWw
(2) G ++ 4.4.7 на centos6 (64bits)
(3) G ++ 4.6.3 по принципу ubuntu12 (64бит)
(4) г ++ 4.9.3 в виде Cygwin (32бит) среды

Как и ожидалось (1) и (4) возвращение и это именно правильный результат, как 1-го значения «0x210000000» является большой для 32-битного значения ....

Error while converting Id (0x210000000). 
success 

но (2) и (3) возвращает

success 
success 

Таким образом возникает вопрос, почему же простой код C сборка на другой платформе с другим компилятором возвращает тот же результат ... и strtoul почему»(„0x210000000“ , ....) 'не устанавливает «errno» в «ERANGE», чтобы сказать, что бит 33-37 находится за пределами допустимого диапазона.

более

след на платформу (3) дают:

Id (0x210000000) as ul = 0x10000000 - str_end - errno 0. 
sucess 
Id (0x10000000) as ul = 0x10000000 - str_end - errno 0. 
sucess 




    /* strtoul example */ 
#include <stdio.h>  /* printf, NULL */ 
#include <stdlib.h>  /* strtoul */ 
#include <errno.h> 

signed int GetIdentifier(const char* idString) 
{ 
    char *str_end; 
    int id = -1; 
    errno = 0; 
    id = strtoul(idString, &str_end, 16); 
    if (*str_end != '\0' || (errno == ERANGE)) 
    { 
    printf("Error while converting Id (%s).\n", idString); 
    return -1; 
    } 

    // Return error if converted Id is more than 29-bit 
    if(id > 0x1FFFFFFF) 
    { 
    printf("Error: Id (%s) should fit on 29 bits (maximum value: 0x1FFFFFFF).\n", idString); 
    return -1; 
    } 
    printf("sucess\n"); 
    return id; 
} 


int main() 
{ 
    GetIdentifier("0x210000000"); 
    GetIdentifier("0x10000000"); 

    return 0; 
} 
+1

0x210000000 => более 4 октетов, а не UL, но ULL – Garf365

+1

Различные целые типы и их размеры упоминаются очень рано в любой порядочной книге программирования C начального уровня. И вообще, использование 'int' или любого другого подписанного типа для хранения шестнадцатеричного числа не имеет никакого смысла. Как вы думаете, strto ** ul ** делает? Убедитесь, что предупреждения компилятора включены. – Lundin

+0

BTW: 'if (* str_end! = '\ 0' || (errno == ERANGE))' не удается обнаружить никакого преобразования, как в 'id = strtoul (" ", & str_end, 16);' Предложить 'if (str_end == idString || * str_end! = '\ 0' || (errno == ERANGE)) ' – chux

ответ

8

Значение 0x210000000 больше, чем 32 бита, а на 32-битных системах long обычно составляет 32 бита, который означает, что вы не можете использовать strtoul для правильно преобразуйте строку. Вы должны использовать strtoull и использовать unsigned long long, который гарантированно будет не менее 64 бит.

Конечно, long long и strtoull было представлено на C99, поэтому вам, возможно, потребуется добавить, например, -std=c99 (или используйте более поздний стандарт, например C11), чтобы он правильно строился.


Проблема, по-видимому, является то, что вы предполагаете, что long является всегда 32 бита, когда на самом деле это определяется как по крайней мере 32 бита. См. this reference для минимального размера битов стандартных целых типов.

На некоторых платформах и компиляторах long может быть больше 32 бит. Linux на 64-битном оборудовании является типичной такой платформой, где long больше, а именно 64 бит, что, конечно, достаточно хорошо, чтобы соответствовать 0x210000000, что приводит к strtoul, не давая ошибки.

+0

downvoted, я отметил, что результат ОЖИДАЕМЫЙ был ошибкой для '0x210000000'. Я не новичок, и если бы я хотел получить 64-битное значение, у меня будет функция «ull» ... пожалуйста, перечитайте вопрос – alexbuisson

+2

@alexbuisson. Тогда я действительно не задаю ваш вопрос. Если вы ожидаете, что большое значение будет терпеть неудачу на 32-битных системах, вы, конечно же, ожидаете, что он добьется успеха на 64-битных системах, верно? В случае 2 и 3, какой результат * вы ожидали? Я не вижу, какую «ошибку» вы могли бы сделать иначе, чем не ожидать, что 'long' будет 64 бит в 64-битных системах. Тип 'long', как и все другие типы, может иметь разные размеры и * не менее * 32 бит, но может быть больше и напр. Linux в 64-битной системе с использованием GCC или Clang, тогда 'long' - 64 бит. –

1

Ваш код неверен в том случае, если успешный звонок не изменит значение errno. Согласно Linux errno man page:

Файл заголовка <errno.h> определяет число переменных errno, который устанавливается системными вызовами и некоторые функции библиотеки в случае ошибки, чтобы указать, что пошло не так. Его значение является значительным , только возвращаемое значение вызова указывает на ошибку (т., -1 от большинство системных вызовов; -1 или NULL из большинства библиотечных функций); a функция, которой успешно удается изменить errno.

(POSIX делает место более строгие ограничения на errno модификации путем успешных вызовов, но Linux не строго придерживаться POSIX во многих случаях, и в конце концов, G Nu в N OT U Никс .. .)

The strtoul man page states:

strtoul() функция возвращает либо результат преобразования или, если был знак предшествующего минуса, отрицание результата преобразование представлено как значение без знака, если только исходное значение (неотмеченное) не переполнится; в последнем случае strtoul() возвращает ULONG_MAX и устанавливает errno в ERANGE. Точно такой же имеет место для strtoull() (с ULLONG_MAX вместо ULONG_MAX).

Если strtoul не возвращается ULONG_MAX, значение errno после вызова strtoul неопределенно.

+0

true, но добавив, что «id! = ULONG_MAX» не поможет, потому что в моем случае, когда «strtoul» процесс «0x210000000» в случаях (2) и (3), я получил: «errno» равен 0, id 'равно «0x10000000», так что это не «ULONG_MAX» – alexbuisson

+0

@alexbuisson Что говорят «errno» и «strtoul» в вашей установке? –

+1

Использование OP 'errno' отображается корректно для C11 7.5 3« Значение errno может быть установлено на ненулевое значение вызовом функции библиотеки, независимо от наличия или отсутствия ошибки **, если использование 'errno' не документировано в описание функции ** в настоящем Международном стандарте ». и §7.22.1.4 8 «Если правильное значение выходит за пределы диапазона представляемых значений, возвращается« LONG_MIN »,« LONG_MAX », ... ... и значение макроса« ERANGE »хранится в errno. " Поскольку 'errno' документируется с помощью' strtoul() ', он должен следовать за этим, а не общим,« есть ли ошибка ». – chux

Смежные вопросы