Implicit conversions
Когда выражение используется в контексте, где ожидается значение другого типа, может произойти преобразование :
int n = 1L; // выражение 1L имеет тип long, ожидается int n = 2.1; // выражение 2.1 имеет тип double, ожидается int char* p = malloc(10); // выражение malloc(10) имеет тип void*, ожидается char*
Преобразования происходят в следующих ситуациях:
Преобразование как при присваивании
- В операторе присваивания значение правого операнда преобразуется в неквалифицированный тип левого операнда.
- При скалярной инициализации значение выражения инициализатора преобразуется в неквалифицированный тип инициализируемого объекта.
- В выражении вызова функции с прототипом значение каждого аргумента преобразуется в тип неквалифицированных объявленных типов соответствующего параметра.
- В операторе return значение операнда return преобразуется в объект, имеющий возвращаемый тип функции.
Обратите внимание, что фактическое присваивание, помимо преобразования, также удаляет лишний диапазон и точность из типов с плавающей запятой и запрещает перекрытия; эти характеристики не применяются к преобразованию как при присваивании.
Продвижения аргументов по умолчанию
В выражении вызова функции когда вызов осуществляется
Каждый аргумент целочисленного типа подвергается целочисленному повышению , и каждый аргумент типа float неявно преобразуется в тип double .
int add_nums(int count, ...); int sum = add_nums(2, 'c', true); // функция add_nums вызывается с тремя значениями int: (2, 99, 1)
|
Обратите внимание, что float complex и float imaginary не преобразуются в double complex и double imaginary в данном контексте. |
(since C99) |
Обычные арифметические преобразования
Аргументы следующих арифметических операторов подвергаются неявным преобразованиям с целью получения общего вещественного типа , который является типом, в котором выполняется вычисление:
- бинарные арифметические операторы * , / , % , + , - ,
- операторы сравнения < , > , <= , >= , == , ! = ,
- бинарные побитовые операторы & , ^ , | ,
- условный оператор ?: .
|
1)
Если один операнд имеет десятичный плавающий тип, другой операнд не должен иметь стандартный плавающий,
комплексный или мнимый тип.
|
(начиная с C23) |
-
- целочисленный или вещественный тип с плавающей точкой в long double
| (since C99) |
-
- целочисленный или вещественный тип с плавающей точкой преобразуется в double
| (since C99) |
-
- целочисленный тип в float (единственный возможный вещественный тип — float, который остается без изменений)
| (начиная с C99) |
-
- Если типы одинаковы, этот тип является общим типом.
-
Иначе, типы различны:
- Если типы имеют одинаковую знаковость (оба знаковые или оба беззнаковые), операнд, чей тип имеет меньший conversion rank [1] неявно преобразуется [2] к другому типу.
-
Иначе, операнды имеют различную знаковость:
- Если беззнаковый тип имеет conversion rank больше или равный рангу знакового типа, тогда операнд со знаковым типом неявно преобразуется к беззнаковому типу.
-
Иначе, беззнаковый тип имеет
conversion rank
меньше знакового типа:
- Если знаковый тип может представить все значения беззнакового типа, тогда операнд с беззнаковым типом неявно преобразуется к знаковому типу.
- Иначе, оба операнда проходят неявное преобразование к беззнаковому аналогу типа знакового операнда.
- ↑ См. integer promotions ниже для правил определения ранга.
- ↑ См. "integer conversions" в разделе implicit conversion semantics ниже.
1.f + 20000001; // int преобразуется в float, получается 20000000.00 // сложение и последующее округление до float дает 20000000.00 (char)'a' + 1L; // сначала char 'a', который равен 97, продвигается до int // разные типы: int и long // одинаковая знаковость: оба знаковые // разный ранг: long имеет больший ранг чем int // следовательно, int 97 преобразуется в long 97 // результат 97 + 1 = 98 типа signed long 2u - 10; // разные типы: unsigned int и signed int // разная знаковость // одинаковый ранг // следовательно, signed int 10 преобразуется в unsigned int 10 // поскольку арифметическая операция выполняется для беззнаковых целых // (см. тему "Арифметические операторы"), вычисление выполняется как (2 - 10) // по модулю (2 в степени n), где n - количество битов значения unsigned int // если unsigned int имеет длину 32 бита и нет битов заполнения в его объектном // представлении, то результат (-8) по модулю (2 в степени 32) = 4294967288 // типа unsigned int 5UL - 2ULL; // разные типы: unsigned long и unsigned long long // одинаковая знаковость // разный ранг: ранг unsigned long long больше // следовательно, unsigned long 5 преобразуется в unsigned long long 5 // поскольку арифметическая операция выполняется для беззнаковых целых // (см. тему "Арифметические операторы"), // если unsigned long long имеет длину 64 бита, то // результат (5 - 2) по модулю (2 в степени 64) = 3 типа // unsigned long long 0UL - 1LL; // разные типы: unsigned long и signed long long // разная знаковость // разный ранг: ранг signed long long больше // если ULONG_MAX > LLONG_MAX, то signed long long не может представить все // unsigned long, следовательно, это последний случай: оба операнда преобразуются // в unsigned long long unsigned long 0 преобразуется в unsigned long long 0 // long long 1 преобразуется в unsigned long long 1 поскольку арифметическая // операция выполняется для беззнаковых целых // (см. тему "Арифметические операторы"), // если unsigned long long имеет длину 64 бита, то // вычисление (0 - 1) по модулю (2 в степени 64) // таким образом, результат 18446744073709551615 (ULLONG_MAX) типа // unsigned long long
|
Тип результата определяется следующим образом:
double complex z = 1 + 2*I; double f = 3.0; z + f; // z остается как есть, f преобразуется в double, результат - double complex |
(начиная с C99) |
Как и всегда, результат операции с плавающей запятой может иметь больший диапазон и точность, чем указано его типом (см. FLT_EVAL_METHOD ).
|
Примечание: действительные и мнимые операнды не преобразуются неявно в комплексные числа, поскольку это потребовало бы дополнительных вычислений и могло бы привести к нежелательным результатам в некоторых случаях с бесконечностями, NaN и знаковыми нулями. Например, если бы действительные числа преобразовывались в комплексные, 2.0×(3.0+i∞) вычислялось бы как (2.0+i0.0)×(3.0+i∞) ⇒ (2.0×3.0–0.0×∞) + i(2.0×∞+0.0×3.0) ⇒ NaN+i∞ вместо корректного 6.0+i∞. Если бы мнимые числа преобразовывались в комплексные, i2.0×(∞+i3.0) вычислялось бы как (0.0+i2.0) × (∞+i3.0) ⇒ (0.0×∞ – 2.0×3.0) + i(0.0×3.0 + 2.0×∞) ⇒ NaN + i∞ вместо –6.0 + i∞. |
(since C99) |
Примечание: независимо от обычных арифметических преобразований, вычисление может всегда выполняться в более узком типе, чем указано этими правилами, в соответствии с правилом as-if .
Преобразования значений
Преобразование lvalue
Любое lvalue выражение любого не-массивного типа, когда используется в любом контексте, кроме
- в качестве операнда оператора взятия адреса (если разрешено),
- в качестве операнда префиксных/постфиксных операторов инкремента и декремента ,
- в качестве левого операнда оператора доступа к члену (точка),
- в качестве левого операнда операторов присваивания и составного присваивания ,
-
в качестве операнда
sizeof,
подвергается
lvalue-преобразованию
: тип остается тем же, но теряет
const
/
volatile
/
restrict
-квалификаторы и
atomic
свойства, если они присутствуют. Значение остается тем же, но теряет свои lvalue-свойства (адрес не может быть получен).
Если lvalue имеет неполный тип, поведение не определено.
Если lvalue обозначает объект автоматической длительности хранения, адрес которого никогда не брался, и если этот объект был неинициализирован (не объявлен с инициализатором и до использования не производилось присваивания ему), поведение не определено.
Это преобразование моделирует загрузку значения объекта из его местоположения в память.
volatile int n = 1; int x = n; // преобразование lvalue для n читает значение n volatile int* p = &n; // нет преобразования lvalue: не читает значение n
Преобразование массива в указатель
Любое lvalue expression (until C99) expression (since C99) типа array , когда используется в любом контексте, кроме
- в качестве операнда оператора взятия адреса ,
-
в качестве операнда
sizeof, -
в качестве операнда
typeofиtypeof_unqual(начиная с C23) , - в качестве строкового литерала для инициализации массива ,
подвергается преобразованию в не-lvalue указатель на свой первый элемент.
Если массив был объявлен
register
, поведение не определено.
Неконстантный массив, или любой из его элементов , недоступен (до C99) , имеет временное время жизни (начиная с C99) .
int a[3], b[3][4]; int* p = a; /* преобразование в &a[0] */ int (*q)[4] = b; /* преобразование в &b[0] */ struct S { int a[1]; }; struct S f(void) { struct S result = {{0}}; /* {0} начиная с C99 */ return result; } void g(void) { int* p = f().a; /* ошибка до C99; OK начиная с C99 */ int n = f().a[0]; /* ошибка до C99; OK начиная с C99 */ f().a[0] = 13; /* ошибка до C99; UB начиная с C99 */ (void)p, (void)n; } int main(void) { return 0; }
Преобразование функции в указатель
Любое выражение-обозначение функции, когда оно используется в любом контексте, кроме
- в качестве операнда оператора взятия адреса ,
-
в качестве операнда
sizeof, -
в качестве операнда
typeofиtypeof_unqual(начиная с C23) ,
подвергается преобразованию в не-lvalue указатель на функцию, обозначенную выражением.
int f(int); int (*p)(int) = f; // преобразование в &f (***p)(1); // многократное разыменование до f и преобразование обратно в &f
Семантика неявного преобразования
Неявное преобразование, будь то как при присваивании или обычное арифметическое преобразование , состоит из двух этапов:
Совместимые типы
Преобразование значения любого типа в любой совместимый тип всегда является no-op и не изменяет представление.
uint8_t (*a)[10]; // если uint8_t является typedef для unsigned char unsigned char (*b)[] = a; // тогда эти типы указателей совместимы
Целочисленные повышения
Целочисленное повышение — это неявное преобразование значения любого целочисленного типа с рангом меньшим или равным рангу int , или битового поля типа _Bool (until C23) bool (since C23) , int , signed int , unsigned int , в значение типа int или unsigned int .
Если int может представить весь диапазон значений исходного типа (или диапазон значений исходного битового поля), значение преобразуется в тип int . В противном случае значение преобразуется в unsigned int .
|
Значение из битового поля целочисленного типа с точной битовой шириной преобразуется в соответствующий целочисленный тип с точной битовой шириной. В остальных случаях целочисленные типы с точной битовой шириной освобождаются от правил целочисленных повышений. |
(since C23) |
Целочисленные повышения сохраняют значение, включая знак:
int main(void) { void f(); // объявление функции в старом стиле // начиная с C23, void f(...) имеет такое же поведение относительно продвижений char x = 'a'; // целочисленное преобразование из int в char f(x); // целочисленное продвижение из char обратно в int } void f(x) int x; {} // функция ожидает int
rank выше — это свойство каждого целочисленного типа и определяется следующим образом:
|
8)
ранг знакового целочисленного типа с точной битовой шириной должен быть больше ранга любого стандартного целочисленного типа с меньшей шириной или любого целочисленного типа с точной битовой шириной с меньшей шириной.
9)
ранг любого целочисленного типа с точной битовой шириной относительно расширенного целочисленного типа той же ширины определяется реализацией.
|
(начиная с C23) |
Примечание: целочисленные повышения применяются только
- в рамках обычных арифметических преобразований (см. выше),
- в рамках стандартных продвижений аргументов (см. выше),
- к операнду унарных арифметических операторов + и - ,
- к операнду унарного побитового оператора ~ ,
- к обоим операндам операторов сдвига << и >> .
Преобразование в булев типЗначение любого скалярного типа может быть неявно преобразовано в _Bool (до C23) bool (начиная с C23) . Значения, которые сравниваются равными целочисленному константному выражению со значением ноль (до C23) являются нулевыми (для арифметических типов), нулевыми указателями (для типов указателей) или имеют тип nullptr_t (начиная с C23) преобразуются в 0 (до C23) false (начиная с C23) , все остальные значения преобразуются в 1 (до C23) true (начиная с C23) . bool b1 = 0.5; // b1 == 1 (0.5 converted to int would be zero) bool b2 = 2.0*_Imaginary_I; // b2 == 1 (but converted to int would be zero) bool b3 = 0.0 + 3.0*I; // b3 == 1 (but converted to int would be zero) bool b4 = 0.0 / 0.0; // b4 == 1 (NaN does not compare equal to zero) bool b5 = nullptr; // b5 == 0 (since C23: nullptr is converted to false) |
(начиная с C99) |
Преобразования целочисленных типов
Значение любого целочисленного типа может быть неявно преобразовано в любой другой целочисленный тип. За исключением случаев, охватываемых повышениями и булевыми преобразованиями выше, правила следующие:
- если целевой тип может представить значение, значение остается неизменным,
-
иначе, если целевой тип беззнаковый, значение
2
b
, где b - количество битов значения в целевом типе, многократно вычитается или добавляется к исходному значению до тех пор, пока результат не поместится в целевой тип. Другими словами, беззнаковые целые числа реализуют модульную арифметику. - иначе, если целевой тип знаковый, поведение определяется реализацией (что может включать генерацию сигнала).
char x = 'a'; // int → char, результат не изменён unsigned char n = -123456; // цель беззнаковая, результат 192 (то есть -123456+483*256) signed char m = 123456; // цель знаковая, результат определяется реализацией assert(sizeof(int) > -1); // assert завершается неудачей: // оператор > требует преобразования -1 в size_t, // цель беззнаковая, результат равен SIZE_MAX
Преобразования вещественных чисел в целые
Любое конечное значение вещественного типа с плавающей запятой может быть неявно преобразовано в любой целочисленный тип. За исключением случаев, описанных в преобразовании к булевому типу выше, правила следующие:
- Дробная часть отбрасывается (усечение в сторону нуля).
-
- Если результирующее значение может быть представлено целевым типом, используется это значение
- в противном случае поведение не определено.
int n = 3.14; // n == 3 int x = 1e10; // неопределённое поведение для 32-битного int
Значение любого целочисленного типа может быть неявно преобразовано в любой вещественный тип с плавающей точкой.
- если значение может быть точно представлено целевым типом, оно остается неизменным.
- если значение может быть представлено, но не может быть представлено точно, результат определяется реализацией как ближайшее большее или ближайшее меньшее значение, хотя при поддержке арифметики IEEE выполняется округление к ближайшему. Не определено, FE_INEXACT возбуждается ли в этом случае.
- если значение не может быть представлено, поведение не определено, хотя при поддержке арифметики IEEE FE_INVALID возбуждается и результирующее значение не определено.
Результат этого преобразования может иметь больший диапазон и точность, чем указывает его целевой тип (см. FLT_EVAL_METHOD ).
Если требуется контроль над FE_INEXACT при преобразованиях из чисел с плавающей точкой в целые, могут использоваться функции rint и nearbyint .
double d = 10; // d = 10.00 float f = 20000001; // f = 20000000.00 (FE_INEXACT) float x = 1 + (long long)FLT_MAX; // неопределённое поведение
Преобразования вещественных чисел с плавающей точкой
Значение любого вещественного типа с плавающей точкой может быть неявно преобразовано в любой другой вещественный тип с плавающей точкой.
- Если значение может быть точно представлено целевым типом, оно остаётся неизменным.
- Если значение может быть представлено, но не может быть представлено точно, результат будет ближайшим большим или ближайшим меньшим значением (другими словами, направление округления определяется реализацией), хотя при поддержке арифметики IEEE округление происходит к ближайшему.
-
Если значение не может быть представлено, поведение не определено
.Этот раздел не завершён
Причина: проверить стандарт IEEE, требуется ли соответствующая бесконечность с правильным знаком
Результат этого преобразования может иметь больший диапазон и точность, чем указывает его целевой тип (см. FLT_EVAL_METHOD ).
double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625 float f = d; // f = 0.100000001490116119384765625 float x = 2 * (double)FLT_MAX; // неопределенное поведение
Преобразования комплексных типовЗначение любого комплексного типа может быть неявно преобразовано в любой другой комплексный тип. Действительная и мнимая части следуют правилам преобразования для вещественных типов с плавающей точкой. Преобразования мнимых типовЗначение любого мнимого типа может быть неявно преобразовано в любой другой мнимый тип. Мнимая часть следует правилам преобразования для вещественных типов с плавающей точкой. double imaginary d = 0.1*_Imaginary_I; float imaginary f = d; // f is 0.100000001490116119384765625*I Вещественно-комплексные преобразованияЗначение любого вещественного типа с плавающей точкой может быть неявно преобразовано в любой комплексный тип.
Значение любого комплексного типа может быть неявно преобразовано в любой вещественный тип с плавающей точкой.
Примечание: при преобразовании из комплексного в вещественный тип, NaN в мнимой части не распространяется на вещественный результат. double complex z = 0.5 + 3*I; float f = z; // the imaginary part is discarded, f is set to 0.5 z = f; // sets z to 0.5 + 0*I Вещественно-мнимые преобразованияЗначение любого мнимого типа может быть неявно преобразовано в любой вещественный тип (целочисленный или с плавающей точкой). Результатом всегда является положительный (или беззнаковый) ноль, за исключением случаев, когда целевой тип - _Bool (до C23) bool (начиная с C23) , в этом случае применяются правила булевого преобразования. Значение любого вещественного типа может быть неявно преобразовано в любой мнимый тип. Результатом всегда является положительный мнимый ноль. double imaginary z = 3*I; bool b = z; // Boolean conversion: sets b to true float f = z; // Real-imaginary conversion: sets f to 0.0 z = 3.14; // Imaginary-real conversion: sets z to 0*_Imaginary_I Комплексно-мнимые преобразованияЗначение любого мнимого типа может быть неявно преобразовано в любой комплексный тип.
Значение любого комплексного типа может быть неявно преобразовано в любой мнимый тип.
double imaginary z = I * (3*I); // the complex result -3.0+0i loses real part // sets z to 0*_Imaginary_I |
(начиная с C99) |
Преобразования указателей
Указатель на void может быть неявно преобразован в указатель на любой объектный тип и обратно со следующей семантикой:
- Если указатель на объект преобразуется в указатель на void и обратно, его значение сравнивается равным исходному указателю.
- Никаких других гарантий не предоставляется.
int* p = malloc(10 * sizeof(int)); // malloc возвращает void*
Указатель на неквалифицированный тип может быть неявно преобразован в указатель на квалифицированную версию этого типа (другими словами,
const
,
volatile
и
restrict
квалификаторы могут быть добавлены). Исходный указатель и результат сравнения равны.
int n; const int* p = &n; // &n имеет тип int*
Любое целочисленное константное выражение со значением 0 , а также целочисленное указательное выражение со значением ноль, приведённое к типу void * , может быть неявно преобразовано в любой указательный тип (как указатель на объект, так и указатель на функцию). Результатом является нулевое значение указателя данного типа, гарантированно неравное любому ненулевому значению указателя этого типа. Это целочисленное или void * выражение известно как нулевой указатель-константа , и стандартная библиотека предоставляет одно определение этой константы в виде макроса NULL .
int* p = 0; double* q = NULL;
` сохранен без изменений, как и требовалось. HTML-теги и атрибуты также остались нетронутыми.
Примечания
Хотя переполнение знакового целого числа в любом арифметическом операторе является неопределенным поведением, переполнение знакового целочисленного типа при целочисленном преобразовании является лишь неуточнённым поведением.
С другой стороны, хотя переполнение беззнакового целого числа в любом арифметическом операторе (и при целочисленном преобразовании) является хорошо определённой операцией и следует правилам модульной арифметики, переполнение беззнакового целого числа при преобразовании из плавающего типа в целочисленный является неопределённым поведением: значения вещественного плавающего типа, которые могут быть преобразованы в беззнаковое целое, находятся в открытом интервале
(
-1
,
Unnn_MAX
+ 1
)
.
unsigned int n = -1.0; // неопределённое поведение
Преобразования между указателями и целыми числами (за исключением преобразования из указателя в _Bool (до C23) bool (начиная с C23) и (начиная с C99) преобразования из целочисленного константного выражения со значением ноль в указатель), между указателями на объекты (за исключением случаев, когда один из указателей является указателем на void) и преобразования между указателями на функции (за исключением случаев, когда функции имеют совместимые типы) никогда не являются неявными и требуют использования оператора приведения типа .
Не существует преобразований (неявных или явных) между указателями на функции и указателями на объекты (включая void * ) или целыми числами.
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
-
- 6.3 Преобразования (стр: 44-50)
- Стандарт C17 (ISO/IEC 9899:2018):
-
- 6.3 Преобразования (стр: 37-41)
- Стандарт C11 (ISO/IEC 9899:2011):
-
- 6.3 Преобразования (стр: 50-56)
- Стандарт C99 (ISO/IEC 9899:1999):
-
- 6.3 Преобразования (стр: 42-48)
- Стандарт C89/C90 (ISO/IEC 9899:1990):
-
- 3.2 Преобразования
Смотрите также
|
C++ документация
для
Неявные преобразования
|