Преобразование шестнадцатеричной строки в длинную

Я пытаюсь выполнить преобразование шестнадцатеричных чисел в целочисленные на 32-битной машине. Вот код, который я тестирую,

int main(int argc,char **argv)
{
    char *hexstring = "0xffff1234";
    long int n;

    fprintf(stdout, "Conversion results of string: %s\n", hexstring);
    n = strtol(hexstring, (char**)0, 0); /* same as base = 16 */
    fprintf(stdout, "strtol = %ld\n", n);
    n = sscanf(hexstring, "%x", &n);
    fprintf(stdout, "sscanf = %ld\n", n);
    n = atol(hexstring);
    fprintf(stdout, "atol = %ld\n", n);
    fgetc(stdin);

    return 0;
  }

Вот что я получаю:

 strtol = 2147483647 /* = 0x7fffffff -> overflow!! */
 sscanf = 1 /* nevermind */
 atol = 0   /* nevermind */

Как видите, с помощью strtol я получаю переполнение (я также проверял с помощью errno), хотя я ожидаю, что ничего не произойдет, поскольку 0xffff1234 является допустимым целочисленным 32-битным значением. Я бы либо ожидал 4294906420, либо -60876

Что мне не хватает?


person jpmuc    schedule 24.07.2012    source источник
comment
Вы уверены, что установка базового числа на 0 равна 16. Я имею в виду замену strtol(hexstring, (char*)0, 0)* на strtol(hexstring, (char*< /i>)0, 16)*   -  person A.G.    schedule 24.07.2012
comment
@ А.Г. установка базы в 0 означает, что strtol пытается выяснить это самостоятельно с начала входной строки, использовать ли базу 16, 10 или 8.   -  person Daniel Fischer    schedule 24.07.2012
comment
точно, забыл! Спасибо !   -  person A.G.    schedule 24.07.2012


Ответы (2)


Если вам не нужен эффект переполнения, не используйте знаковый вариант функции. Вместо этого используйте strtoul().

Со следующим кодом:

#include <stdio.h>
int main(int argc,char **argv) {
    char *hexstring = "0xffff1234";
    long sn;
    unsigned long un;

    fprintf(stdout, "Conversion results of string: %s\n", hexstring);

    sn = strtoul(hexstring, (char**)0, 0);
    fprintf(stdout, "strtoul   signed = %ld\n", sn);

    un = strtoul(hexstring, (char**)0, 0);
    fprintf(stdout, "strtoul unsigned = %lu\n", un);

    return 0;
}

Я получил:

Conversion results of string: 0xffff1234
strtoul   signed = -60876
strtoul unsigned = 4294906420

Вы не можете контролировать это при вызове strtol() и его собратьев, поскольку поведение находится внутри этих функций. В стандарте сказано следующее:

Функции strtol, strtoll, strtoul и strtoull возвращают преобразованное значение, если оно есть. Если преобразование не может быть выполнено, возвращается ноль. Если правильное значение находится за пределами диапазона представляемых значений, возвращается LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX или ULLONG_MAX (в соответствии с типом возвращаемого значения и знаком значения, если он есть), а значение макроса ERANGE равно хранится в errno.

person paxdiablo    schedule 24.07.2012
comment
Это правильное решение, хорошо. Мех, у меня это было прямо передо мной в документах, но я все равно не видел: D +1. :) - person ATaylor; 24.07.2012
comment
@juampa Думаю, переполнение происходит внутри функции. Зачем еще две функции? Если вы, однако, выведете содержимое 'n' как подписанное, вы получите -60876 ... мда, его редактирование опередило меня: D - person ATaylor; 24.07.2012

Минимальный диапазон long составляет от -2147483647 до 2147483647. Диапазон long в вашей реализации, вероятно, составляет от -2147483648 до 2147483647.

В определении strtol() говорится, что если преобразованное значение выходит за пределы диапазона long, LONG_MIN или LONG_MAX возвращается в соответствии со знаком преобразованного значения, а ERANGE сохраняется в errno. В этом случае преобразованное значение 0xffff1234 выходит за пределы диапазона — оно больше LONG_MAX, поэтому возвращается LONG_MAX.

Кстати, ваш вызов sscanf() перезаписывает преобразованное значение в n возвращаемым значением sscanf(), которое равно 1, что указывает на 1 успешное преобразование.

person caf    schedule 24.07.2012