Почему краткость должна быть преобразована в int перед арифметическими операциями в C и C ++?

Из ответов, которые я получил от этот вопрос, похоже, что C ++ унаследовал это требование для преобразования short в int при выполнении арифметических операций из C. Могу ли я выбрать ваши мозги, чтобы Зачем это было введено в C в первую очередь? Почему бы просто не сделать эти операции как short?

Например (взято из предложения дипа в комментариях):

short s = 1, t = 2 ;
auto  x = s + t ;

x будет иметь тип ИНТ.

67

Решение

Если мы посмотрим на Обоснование международного стандарта — языки программирования — C в разделе 6.3.1.8 Обычные арифметические преобразования это говорит (Акцент шахты идет вперед):

Правила в Стандарте для этих преобразований незначительны
модификации тех, кто в К&R: модификации учитывают добавленные
типы и правила сохранения значений. Явная лицензия была добавлена ​​к
выполнять расчеты в «более широком» виде, чем это абсолютно необходимо,
так как это может иногда производить меньший и более быстрый код, а не
чаще упоминайте правильный ответ
. Расчеты также могут быть
выполняется в «более узком» виде по правилу «как будто» до тех пор, пока
конечный результат получен. Явное приведение всегда можно использовать для получения
значение в желаемом типе

Раздел 6.3.1.8 от проект стандарта C99 покрывает Обычные арифметические преобразования который применяется к операндам арифметических выражений для примера раздела 6.5.6 Аддитивные операторы говорит:

Если оба операнда имеют арифметический тип, обычная арифметика
преобразования
выполняются на них.

Мы находим похожий текст в разделе 6.5.5 Мультипликативные операторы также. В случае короткая операнд, первый целочисленные акции применяются из раздела 6.3.1.1 Булевы, символы и целые числа который говорит:

Если int может представлять все значения исходного типа, значение
преобразован в int; в противном случае он конвертируется в беззнаковое целое.
Они называются целочисленными акциями.48) Все остальные типы
без изменений целочисленными акциями.

Обсуждение из раздела 6.3.1.1 из Обоснование или международный стандарт — языки программирования — C на целочисленные акции на самом деле более интересно, я собираюсь выборочно цитировать б / к, слишком долго, чтобы полностью цитировать:

Реализации попали в два основных лагеря который может быть охарактеризован
как сохранение без знака и сохранение значения.

[…]

беззнаковый сохраняющий подход призывает к продвижению двух меньших
неподписанные типы в unsigned int. Это простое правило и дает
тип, который не зависит от среды исполнения.

ценностный подход призывает к продвижению этих типов в
со знаком int, если этот тип может правильно представлять все значения
оригинальный тип, и в противном случае для продвижения этих типов без знака
внутр. Таким образом, если среда выполнения представляет короткое как нечто
меньше, чем int, unsigned short становится int; в противном случае это становится
без знака int.

Это может иметь некоторые довольно неожиданные результаты в некоторых случаях, как Непоследовательное поведение неявного преобразования между неподписанными и большими типами со знаком демонстрирует, что таких примеров гораздо больше. Хотя в большинстве случаев это приводит к тому, что операции работают должным образом.

38

Другие решения

Это не особенность языка, а ограничение физических архитектур процессоров, на которых работает код. int Тип typer в C обычно соответствует размеру вашего стандартного регистра процессора. Больше кремния занимает больше места и больше энергии, поэтому во многих случаях арифметика может быть выполнена только для типов данных «натурального размера». Это не всегда так, но большинство архитектур все еще имеют это ограничение. Другими словами, при добавлении двух 8-битных чисел в процессоре происходит 32-битная арифметика определенного типа, за которой следует либо простая битовая маска, либо другое соответствующее преобразование типов.

21

short а также char типы считаются стандартным видом «типов хранения», то есть поддиапазонов, которые вы можете использовать для экономии места, но которые не принесут вам никакой скорости, потому что их размер «неестественен» для процессора.

На некоторых процессорах это не так, но хорошие компиляторы достаточно умны, чтобы заметить, что если вы, например, добавьте константу к неподписанному символу и сохраните результат обратно в неподписанном символе, тогда нет необходимости проходить через unsigned char -> int преобразование.
Например, с g ++ код, сгенерированный для внутреннего цикла

void incbuf(unsigned char *buf, int size) {
for (int i=0; i<size; i++) {
buf[i] = buf[i] + 1;
}
}

просто

.L3:
addb    $1, (%rdi,%rax)
addq    $1, %rax
cmpl    %eax, %esi
jg  .L3
.L1:

где вы можете увидеть, что инструкция добавления без знака (addb) используется.

То же самое происходит, если вы выполняете вычисления между короткими целыми числами и сохраняете результат в коротких целых числах.

17

Связанный вопрос, кажется, достаточно хорошо его охватывает: процессор просто нет. 32-битный процессор имеет свои собственные арифметические операции, настроенные для 32-битных регистров. Процессор предпочитает работать в своем любимом размере, и для таких операций копирование небольшого значения в регистр собственного размера обходится дешево. (Для архитектуры x86 32-разрядные регистры названы так, как будто они являются расширенными версиями 16-разрядных регистров (eax в ax, ebx в bx, так далее); увидеть целочисленные инструкции x86).

Для некоторых чрезвычайно распространенных операций, в частности векторной / плавающей арифметики, могут существовать специальные инструкции, которые работают с другим типом или размером регистра. Для чего-то вроде короткого, заполнение (до) 16 битами нулей имеет очень малые затраты производительности, и добавление специализированных инструкций, вероятно, не стоит времени или места на кристалле (если вы хотите понять, почему; не уверен, что они заняли бы фактическое пространство, но это становится намного более сложным).

7
По вопросам рекламы [email protected]