сложение целых чисел без знака и неопределенное поведение в C90

Решено!

Соответствующий отрывок можно найти в C90 ISO 9899:1990 6.1.2.5 Типы:

«[..] Вычисление, включающее операнды без знака, никогда не может переполниться, потому что [...]»

Поэтому 9899:1990 6.3 не может применяться и, следовательно, не может быть неопределенным поведением.

Спасибо Киту Томпсону за помощь в чтении. :-)

[2014-03-14] По-видимому, короткое целое число без знака может переполниться, что приведет к неопределенному поведению в зависимости от целевой среды. Это может произойти, если короткое целое число без знака будет арифметически преобразовано в целое число. Подробности см. в обновленном ответе и комментариях supercat. Спасибо обоим. :-)

Оригинальный вопрос:

Прежде всего, извините за мой плохой английский... Я стараюсь изо всех сил. Это мой первый вопрос, и я думаю, довольно глупый.

По каким-то печальным причинам моя компания все еще придерживалась ANSI C90 (ANSI/ISO 9899:1990), поэтому у меня в руках старая копия этого стандарта, но до сих пор отсутствуют исправления и исправления.

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

Что произойдет, если у меня возникнет переполнение целого числа без знака при добавлении. Пожалуйста, посмотрите этот фрагмент кода:

uint32_t a,b,c;
b = UINT_MAX;
c = UINT_MAX;
a = b + c; /* Overflow here - undefined behavior? */

Все, что я когда-либо знал об этом, это то, что это целое число без знака просто переносится столько, сколько нужно, и все в порядке, хотя и не всегда предназначено.

Сейчас искал соответствующие детали в стандарте.

Конечно, существует ISO 9899:1990 6.2.1.2, в котором описывается перенос целых чисел без знака всякий раз, когда они преобразуются. И есть немного 6.2.1.5 «обычных арифметических преобразований», которые описывают, как типы становятся шире, в основном, что оба операнда выражения имеют один и тот же тип.

Теперь есть 6.3 «Выражения», которые касаются меня. я цитирую:

"[...] Если во время вычисления выражения возникает исключение (то есть, если результат не определен математически или НЕ В ДИАПАЗОНЕ ПРЕДСТАВЛЯЕМЫХ ЗНАЧЕНИЙ ДЛЯ ЕГО ТИПА), поведение НЕОПРЕДЕЛЕНО. [...]"

И глава 6.3.6 об аддитивных операторах говорит:

  • обычные арифметические преобразования операндов, которые не применяются, так как все uint32_t.
  • и результатом является сумма обоих, что явно не вписывается в uint32_t.
  • ОТЛИЧНО

Ничего не сказано о том, что результирующее значение преобразуется в тип результата, поэтому 6.2.1.2 не применяется. Но значение то явно зашкаливает, где 6,3 ступени.

Насколько я понимаю, это неопределенное поведение в соответствии с ISO 9899:1990. Что я пропустил? Есть ли что-то в Corrigendae? Я пропустил строку или слово в стандарте?

Я действительно смущен сейчас. :-)

С уважением, Марк

Обновление вопроса

[2014-03-03] Решено

[2014-03-03] Итак, благодаря Acme у меня теперь есть четкий и полный ответ для ANSI C99 (см. ответ на: "Является ли вычитание целого числа без знака определенным поведением": это четко определенное поведение, как и ожидалось. И хотя я ожидаю этого для ANSI C90 кроме того, я до сих пор не могу прочитать это из текста ISO 9899:1990, учитывая текстовые отрывки выше. Поэтому я считаю, что на данный момент мой вопрос до сих пор остается без ответа.

Редактировать: просто опечатки | Edit2: добавить теги C90 и стандартов | Edit3: добавить обновление вопроса | Edit4: добавить решенный раздел | Edit5: добавьте ссылки на ответы :-) | Edit6: обновить короткие целочисленные переполнения без знака | Edit7: некоторые опечатки


person Mark A.    schedule 28.02.2014    source источник
comment
Я полагаю, что lysator.liu.se/c имеет (рядом с окончательным) черновики того, что стало ANSI С/С90. Возможно, часто задаваемые вопросы о comp.lang.c помогут найти копию.   -  person vonbrand    schedule 03.03.2014
comment
спасибо vonbrand - у меня копия стандартная, но плохо читается.   -  person Mark A.    schedule 03.03.2014
comment
Наконец я был прав. Это БЫЛ глупый вопрос! :-)   -  person Mark A.    schedule 03.03.2014


Ответы (1)


Существенных изменений в этой области от C90 до C99 или от C99 до C11 не произошло.

C90 6.1.2.5 (номер абзаца отсутствует, но это первый абзац на странице 23 PDF-файла) говорит:

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

Почти такая же формулировка (с фразой «результирующий целочисленный тип без знака», измененной на «результирующий тип», появляется в 6.2.5, параграф 9 в стандартах C99 и C11.

Единственное существенное изменение (о котором я знаю) — это добавление в C99, касающееся подписанного преобразования. В C90, если значение со знаком или без знака преобразуется в целочисленный тип со знаком, а результат не может быть представлен в целевом типе, результат определяется реализацией. В C99 и C11 либо результат определяется реализацией, либо выдается сигнал, определяемый реализацией. (Я не знаю ни одного компилятора, который использует разрешение на создание сигнала.)

Как указано в комментарии supercat, умножение двух значений unsigned short может переполниться. Предположим, что short — 16 бит, int — 32 бита, и типичными являются целочисленные представления (дополнение до 2 для типов со знаком, биты заполнения отсутствуют). Затем дано:

unsigned short US = USHRT_MAX; // 65536

оба операнда в выражении

us * us

повышаются с unsigned short до int (поскольку int может содержать все возможные значения типа unsigned short), но произведение, равное почти 232, не помещается в 32-битный signed int.

(Во время процесса стандартизации C в конце 1980-х комитету пришлось выбирать между «сохранением значения» и «сохранением беззнакового» целочисленного продвижения. Они выбрали первое. С семантикой сохранения без знака операнды unsigned short будут повышены до unsigned int. , и этой проблемы не возникнет.)

person Keith Thompson    schedule 03.03.2014
comment
Ах, вот недостающее звено. Я просто должен читать более терпеливо. Думаю, я просто не предусмотрел вычисления и правила переполнения в главе о типах. Сам - большое спасибо. - person Mark A.; 03.03.2014
comment
Вычисление, включающее два операнда без знака, размер которых не меньше int, не может привести к переполнению. Произведение двух беззнаковых операндов, меньших int, может, однако, переполниться, если int недостаточно велико для хранения результата. - person supercat; 14.03.2014
comment
@supercat: Это может произойти только в том случае, если int шире, чем short, но менее чем в два раза шире (что, безусловно, законно, но, вероятно, не существует в реальном мире). - person Keith Thompson; 14.03.2014
comment
@KeithThompson: Это также может произойти, если он ровно в два раза шире. На машине с 64-битным целым числом uint32_t a = 3037000500u; uint32_b = a*a; будет генерировать неопределенное поведение. Я не уверен в каком-либо хорошем эффективном портативном способе умножения двух uint32_t, чтобы получить результат uint32_t. Приведение к uint64_t сработает, но это ужасно. Приведение к unsigned завершится ошибкой на встроенных контроллерах, где int меньше 32 бит. - person supercat; 14.03.2014
comment
@supercat: Ты прав (и я понял это незадолго до того, как прочитал твой комментарий). Ик. - person Keith Thompson; 14.03.2014
comment
@KeithThompson: несоответствие поведения, сохраняющего ценность и сохраняющее беззнаковость, ИМХО ужасно; C крайне нуждается, IMHO, в средстве объявления типов, которые должны вести себя как члены алгебраических колец (например, набор целых чисел, конгруэнтных по модулю 4294967296), в отличие от типов, которые действуют как числа. Типы колец не должны не продвигаться вперед, но всегда должны иметь точно указанный размер. Если машина имеет, например. 36-битный int код, использующий uring32_t, должен выполнять 36-битную математику и маскировать результат. Идея о том, что компилятор должен продвигать такие значения, чтобы производить более быстрый код... - person supercat; 14.03.2014
comment
... в отличие от кода, который обертывается точно так, как указано, кажется мне абсурдным. Обратите внимание, что добавление целого числа любого размера к кольцу должно привести к члену того же кольца. Кольца должны быть неявно назначаемыми только тем же самым кольцом или кольцами меньшего размера; преобразование в числа или в более крупные кольца должно выполняться с помощью приведения. - person supercat; 14.03.2014
comment
Будет ли это переполнение вызывать неопределенное поведение или поведение, определенное реализацией? - person Mark A.; 14.03.2014
comment
@MarkA.: поведение переполнения целого числа со знаком не определено. С другой стороны, преобразование целочисленного значения в тип со знаком, когда значение не может быть представлено, приводит к результату, определяемому реализацией, или вызывает сигнал, определяемый реализацией, который затем может привести к неопределенному поведение. - person Keith Thompson; 14.03.2014
comment
@Keith Спасибо за разъяснения. С каждым днем ​​я люблю Си все больше и больше. Должно быть что-то вроде стокгольмского синдрома :-) - person Mark A.; 14.03.2014
comment
@MarkA.: Вы неправильно написали «Синдром». - person Keith Thompson; 14.03.2014
comment
@KeithThompson Спасибо, но я больше ничего не могу с этим поделать. :-) - person Mark A.; 14.03.2014
comment
Интересно, будут ли разумные люди в Комитете возражать против правила, определяющего, что если результат сложения, умножения, сдвига влево, побитового оператора или унарного плюса или минуса приводится или приводится к беззнаковому типу, меньшему, чем int, компиляторы должны либо заставьте его вести себя как беззнаковую операцию и принудьте его операнды аналогичным образом, либо определите __NON_MODULAR_UNSIGNED_SHORT и предусмотрите, что строго соответствующие программы не будут компилироваться при наличии такого флага. Обратите внимание, что, вероятно, более 99% компиляторов, написанных до 2010 года, уже соответствовали этому требованию. - person supercat; 13.05.2015