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

Почему поведение реализации вычитающих символов специфично?

Это утверждение:

if('z' - 'a' == 25)

не гарантируется равноценным образом. Он зависит от компилятора. Кроме того, не гарантируется оценка так же, как это:

#if 'z' - 'a' == 25

даже если оба препроцессора и компилятора запущены на одном компьютере. Почему это?

4b9b3361

Ответ 1

OP спрашивает о прямой цитате из стандарта - N1570 §6.10.1p3,4 + примечание 168:

... выражение управляющей константы оценивается в соответствии с правилами 6.6.... Это включает в себя интерпретацию символьных констант, которые могут включать в себя преобразование управляющих последовательностей в исполнительные элементы набора символов. Является ли числовое значение для этих символьных констант совпадающим с значением, полученным, когда идентичная константа символа встречается в выражении (отличном от директивы #if или #elif), определяется реализацией. 168

[сноска 168] Таким образом, константное выражение в следующей директиве #if и инструкции if не гарантируется для оценки того же значения в этих двух контекстах.

#if 'z' - 'a' == 25
if ('z' - 'a' == 25)

Итак, да, это действительно не гарантировано.

Чтобы понять, почему это не гарантировано, сначала вам нужно знать, что стандарт C не требует, чтобы символьные константы 'a' и 'z' имели числовые значения, присвоенные этим символам ASCII. Большинство реализаций C в настоящее время используют ASCII или надмножество, но существует еще одна кодировка под названием EBCDIC, которая по-прежнему широко используется (только для мэйнфреймов IBM, но их все еще много). В EBCDIC не только 'a' и 'z' имеют разные значения из ASCII, а алфавит не является последовательной последовательностью! Поэтому выражение 'z' - 'a' == 25 может не оценивать истину в первую очередь.

Вам также необходимо знать, что стандарт C пытается поддерживать различие между текстовой кодировкой, используемой для исходного кода ( "набор исходных символов" ), и текстовым кодированием, которое программа будет использовать во время выполнения ( "набор символов выполнения" "). Это значит, что вы можете, по крайней мере в принципе, взять программу, источник которой закодирован в тексте ASCII и запустить ее без изменений на компьютере, использующем EBCDIC, просто путем кросс-компиляции соответствующим образом; вам не нужно сначала преобразовывать исходный текст в EBCDIC.

Теперь компилятор должен понимать оба набора символов, если они разные, но исторически, препроцессор C (этапы перевода с 1 по 4), а "собственный компилятор" (этапы с 5 по 7) были двумя отдельными программами, а выражения #if - это единственное место, где препроцессор должен был знать о наборе символов выполнения. Таким образом, определив, соответствует ли установленный препроцессором "набор символов выполнения", который используется соответствующим компилятором, стандарт разрешает препроцессору выполнять всю свою работу в наборе исходных символов, что делает жизнь немного легче назад в 1989 году.

Сказав все это, я был бы очень удивлен, если найду современный компилятор, который не заставил бы оба выражения оценивать одинаковое значение, даже если наборы символов исполнения и исходного кода грубо несовместимы. Современные компиляторы, как правило, имеют интегрированные препроцессоры - этапы с 1 по 7 выполняются одной и той же программой, и даже если они этого не делают, инженерное бремя специализации препроцессора в соответствии с его исполнением, установленным для самого компилятора, тривиально в настоящее время.

Ответ 2

Потому что не все компьютеры используют ascii или unicode.

В прошлом стандарт, называемый ebcdic, был обычным явлением. В ebcdic 500 значение 'z' равно 169, а значение 'a' равно 130. Тогда выражение 'z'-'a' будет оцениваться до 39.

Это объясняет, почему вы не можете принять определенное значение для выражения типа 'a' или даже 'z'-'a'. Однако он не объясняет, почему два выражения в Q не гарантируются равными.

Препроцессор и компилятор - это две разные вещи. Препроцессор имеет дело с кодировкой, используемой в исходном коде, тогда как компилятор нацелен на машину, для которой вы компилируете. См. Ответ zwol для более подробного объяснения.

Ответ 3

Чтобы расширить другие правильные ответы, реальный пример компилятора, не использующего ASCII C, который все еще используется, IBM z/OS XL C/С++. По умолчанию предполагается, что исходные файлы находятся в кодовой странице IBM 1047 (версия EBCDIC с тем же репертуаром, что и Latin-1). Однако он имеет несколько разных параметров компилятора для поддержки не только ASCII, но также "гибридного кода" или исходных файлов, содержащих данные более чем в одной кодировке. (Эти программы существуют потому, что компиляторы MVS требовали, чтобы синтаксические утверждения были только в кодировке IBM-1047.)

Из документации видно, что можно было бы гадать с командами типа #pragma CONVLIT(suspend) таким образом, чтобы действительно эти два оператора оценивали по-разному в этом компиляторе. У меня нет копии для проверки MCVE.