Подтвердить что ты не робот

C и С++ гарантируют ASCII символов [a-f] и [A-F]?

Я просматриваю следующий код, чтобы проверить шестнадцатеричную цифру и преобразовать ее в целое число. Код очень умный, потому что он использует разницу между капиталом и младшими буквами - 32 и бит 5. Таким образом, код выполняет один дополнительный OR, но сохраняет один JMP и два CMP s.

static const int BIT_FIVE = (1 << 5);
static const char str[] = "0123456789ABCDEFabcdef";

for (unsigned int i = 0; i < COUNTOF(str); i++)
{
    int digit, ch = str[i];

    if (ch >= '0' && ch <= '9')
        digit = ch - '0';
    else if ((ch |= BIT_FIVE) >= 'a' && ch <= 'f')
        digit = ch - 'a' + 10;
    ...
}

Do C и С++ гарантируют ASCII или значения символов [a-f] и [A-F]? Здесь гарантия означает, что верхний и нижний наборы символов всегда будут отличаться постоянным значением, которое может быть представлено бит (для трюка выше). Если нет, что говорит об этом стандарт?

(Извините за тег C и С++. Меня интересует обе языковая позиция по этому вопросу).

4b9b3361

Ответ 1

Нет никаких гарантий относительно конкретных значений, но вам все равно, потому что ваше программное обеспечение, вероятно, никогда не столкнется с системой, которая несовместима таким образом с ASCII. Предположим, что пространство всегда 32 и что A всегда 65, это прекрасно работает в современном мире.

Стандарт C гарантирует, что буквы A-Z и a-z существуют и что они вписываются в один байт.

Это гарантирует, что 0-9 являются последовательными.

В обоих базовых наборах символов источника и исполнения значение каждого символа после 0 в приведенном выше списке десятичных цифр должно быть больше, чем значение предыдущего.

Обоснование

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

  • Одиночные байтовые кодировки, совместимые с ISO/IEC 646. Цифры 0-9 и буквы A-Z и a-z всегда занимают одинаковые позиции.

  • Многобайтовые кодировки символов (Big5, Shift JIS, ISO 2022). В этих кодировках ваша программа, вероятно, уже сломана, и вам придется потратить время на ее исправление, если вам это интересно. Однако парсинг-номера будут по-прежнему работать, как ожидалось.

  • Кодировки Unicode. Цифры 0-9 и буквы A-Z, a-z всегда занимают одинаковые позиции. Вы можете работать с кодовыми точками или кодовыми единицами свободно, и вы получите тот же результат, если работаете с кодовыми точками ниже 128 (которые вы есть). (Вы работаете с UTF-7? Нет, вы должны использовать это только для электронной почты.

  • EBCDIC

    . Цифрам и буквам присваиваются разные значения, чем их значения в ASCII, однако 0-9 и A-F, a-f все еще смежны. Даже тогда вероятность того, что ваш код будет работать в системе EBCDIC, будет в основном нулевой.

Итак, вот вопрос: думаете ли вы, что гипотетический пятый вариант будет изобретен в будущем, как-то менее совместимый/более сложный в использовании, чем Unicode?

Вы заботитесь о EBCDIC?

Мы могли бы мечтать о причудливых системах весь день... предположим, что CHAR_BIT равно 11 или sizeof(long) = 100, или предположим, что мы используем одну арифметику дополнения, или malloc() всегда возвращает NULL, или предположим, что пиксели на вашем монитор расположены в шестиугольной сетке. Предположим, что ваши номера с плавающей запятой не являются IEEE 754, предположим, что все ваши указатели данных имеют разные размеры. В конце концов, это не приближает нас к целям написания рабочего программного обеспечения на современных современных системах (с случайным исключением).

Ответ 2

Нет, это не так.

Стандарт C гарантирует, что существуют десятичные цифры и прописные и строчные буквы, а также ряд других символов. Он также гарантирует, что десятичные цифры являются смежными, например '0' + 9 == '9', и что все члены базового набора символов выполнения имеют неотрицательные значения. Это специально не гарантирует, что буквы смежны. (Для всех подробностей о деталях см. Проект N1570 стандарта C, раздел 5.2.1, гарантия того, что основные символы неотрицательны, находится в 6.2. 5p3, при обсуждении типа char.)

Предположение, что 'a'.. 'f' и 'a'.. 'f' имеют непрерывные коды, почти наверняка является разумным. В ASCII и на всех наборах символов, основанных на ASCII, 26 строчных букв являются смежными, как и 26 букв в верхнем регистре. Даже в EBCDIC, единственном значительном соперничестве с ASCII, алфавит в целом не соприкасается, но буквы 'a'.. 'f' и 'a'.. 'f' (EBCDIC имеет пробелы между 'i' и 'j' между 'r' и 's' между 'i' и 'j' и между 'r' и 's').

Однако предположение о том, что установка бита 5 представления будет преобразовывать заглавные буквы в нижний регистр, недействительна для EBCDIC. В ASCII коды для строчных и прописных букв различаются на 32; в EBCDIC они отличаются на 64.

Этот тип бит-twiddling для сохранения команды или двух может быть разумным в коде, который является частью стандартной библиотеки или тем, что известно как критически важное для производительности. Неявное предположение о наборе символов на основе ASCII должно ИМХО, по крайней мере, быть сделано явным комментарием. Статическая таблица поиска по 256 элементов, вероятно, будет еще быстрее за счет небольшого количества дополнительного хранилища.

Ответ 3

Для максимальной мобильности, ясности и скорости я бы предложил простой переключатель:

int hex_digit_value(char x)
{
    switch (x)
    {
    case '0': return 0;
    case '1': return 1;
    case '2': return 2;
    case '3': return 3;
    case '4': return 4;
    case '5': return 5;
    case '6': return 6;
    case '7': return 7;
    case '8': return 8;
    case '9': return 9;
    case 'A':
    case 'a': return 10;
    case 'B':
    case 'b': return 11;
    case 'C':
    case 'c': return 12;
    case 'D':
    case 'd': return 13;
    case 'E':
    case 'e': return 14;
    case 'F':
    case 'f': return 15;
    default: return -1;
    }
}

clang -O1 -S преобразует это в простой поиск таблицы:

    addl    $-48, %edi
    cmpl    $54, %edi
    ja  .LBB0_2

    movslq  %edi, %rax
    movl    .Lswitch.table(,%rax,4), %eax
    retq
.LBB0_2:
    movl    $-1, %eax
    retq

Для полноты, вот сводная таблица поиска:

.Lswitch.table:
.long   0                       # 0x0
.long   1                       # 0x1
.long   2                       # 0x2
.long   3                       # 0x3
.long   4                       # 0x4
.long   5                       # 0x5
.long   6                       # 0x6
.long   7                       # 0x7
.long   8                       # 0x8
.long   9                       # 0x9
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   10                      # 0xa
.long   11                      # 0xb
.long   12                      # 0xc
.long   13                      # 0xd
.long   14                      # 0xe
.long   15                      # 0xf
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   10                      # 0xa
.long   11                      # 0xb
.long   12                      # 0xc
.long   13                      # 0xd
.long   14                      # 0xe
.long   15                      # 0xf