Итак, если я правильно понял, интегральное продвижение предусматривает, что: char, wchar_t, bool, enum, short
типы ВСЕГДА конвертируются в int
(или же unsigned int
). Затем, если в выражении есть разные типы, будет применено дальнейшее преобразование.
Я хорошо понимаю это?
И если да, то мой вопрос: почему это хорошо? Зачем? Не становись char/wchar_t/bool/enum/short
ненужным? Я имею в виду, например:
char c1;
char c2;
c1 = c2;
Как я уже говорил, char
ВСЕГДА превращается в int
в этом случае после автоматического преобразования это выглядит так:
int c1;
int c2;
c1 = c2;
Но я не могу понять, почему это хорошо, если я знаю, что char
типа будет достаточно для моих нужд.
Конверсии, о которых вы спрашиваете, являются обычные арифметические преобразования и целочисленные акции, определено в разделе 6.3.1.8 последнего стандарта ISO C. Они применяются к операндам большинства бинарных операторов («двоичный» означает, что они принимают два операнда, такие как +
, *
, так далее.). (Правила схожи для C ++. В этом ответе я просто ссылаюсь на стандарт C.)
Вкратце обычные арифметические преобразования являются:
long double
другой операнд преобразуется в long double
,double
другой операнд преобразуется в double
,float
другой операнд преобразуется в float
, целочисленные акции определены в разделе 6.3.1.1 стандарта С. Для типа более узкого, чем int
если тип int
может содержать все значения типа, тогда выражение этого типа преобразуется в int
; в противном случае он преобразуется в unsigned int
, (Обратите внимание, что это означает, что выражение типа unsigned short
может быть преобразован либо в int
или unsigned int
в зависимости от относительных диапазонов типов.)
Целочисленные продвижения также применяются к аргументам функции, когда в объявлении не указан тип параметра. Например:
short s = 2;
printf("%d\n", s);
продвигает short
значение для int
, Это продвижение не происходит для невариантных функций.
Быстрый ответ на вопрос, почему это сделано, заключается в том, что стандарт так говорит.
Основная причина всей этой сложности состоит в том, чтобы допустить ограниченный набор арифметических операций, доступных на большинстве процессоров. С этим набором правил все арифметические операторы (кроме операторов сдвига, которые являются частным случаем) требуются только для работы с операндами того же типа. Здесь нет short + long
оператор сложения; вместо этого short
операнд неявно преобразуется в long
, И нет никаких арифметических операторов для типов, более узких, чем int
; если добавить два short
значения, оба аргумента повышены до int
, принося int
результат (который затем может быть преобразован обратно в short
).
Некоторые процессоры могут выполнять арифметику с узкими операндами, но не все могут это делать. Без этого единого набора правил, или компиляторы должны будут эмулировать узкую арифметику на процессорах, которые не поддерживают ее напрямую, или же Поведение арифметических выражений будет зависеть от того, какие операции поддерживает целевой процессор. Нынешние правила являются хорошим компромиссом между согласованностью между платформами и эффективным использованием операций ЦП.
Типы хранения никогда не конвертируются автоматически. Вы получаете автоматическое целочисленное продвижение, как только начинаете делать целочисленную арифметику (+
, -
, сдвиги, …) на эти переменные.
char c1, c2; // stores them as char
char c3 = c1 + c2; // equivalent to
char c3 = (char)((int)c1 + (int)c2);
если я хорошо это понял, интегральное продвижение предусматривает, что: char, wchar_t, bool, enum, короткие типы ВСЕГДА конвертируются в int (или в unsigned int).
Ваше понимание верно только отчасти: короткие типы действительно повышаются до int
, но только когда вы используете их в выражениях. Конвертация производится непосредственно перед использованием. Он также «отменяется», когда результат сохраняется обратно.
Способ хранения значений остается согласованным со свойствами типа, что позволяет вам контролировать способ использования памяти для переменных, которые вы храните. Например,
struct Test {
char c1;
char c2;
};
будет в четыре раза меньше, чем
struct Test {
int c1;
int c2;
};
в системах с 32-битным int
s.
Преобразование не выполняется при сохранении значения в переменной. Преобразование выполняется, если вы приведете значение или если вы явно выполните какую-либо операцию, например, какую-то арифметическую операцию с ним.
Это действительно зависит от вашей базовой архитектуры микропроцессора. Например, если ваш процессор 32-разрядный, это его собственный целочисленный размер. Использование собственного целочисленного размера в целочисленных вычислениях лучше оптимизировано.
Преобразование типов происходит при выполнении арифметических операций, операций сдвига, унарных операций. Посмотрите, что стандарт говорит об этом:
Если
int
может представлять все значения исходного типа (как ограничено шириной, для
битовое поле), значение преобразуется вin
т; в противном случае он преобразуется вunsigned
, Это называется целочисленные акции.58) Все остальные типы неизменны
int
целочисленные акции.58.Целочисленные продвижения применяются только: как часть обычных арифметических преобразований, к определенным выражениям аргументов, к операндам унарного числа.
+
,-
, а также~
операторы и оба операнда операторов сдвига,1 как указано в их соответствующих подпунктах
1. Акцент мой.