Неявные преобразования
Когда выражение используется в контексте, где ожидается значение другого типа, может произойти преобразование:
int n = 1L; // expression 1L has type long, int is expected n = 2.1; // expression 2.1 has type double, int is expected char *p = malloc(10); // expression malloc(10) has type void*, char* is expected
Преобразования происходят в следующих ситуациях:
Преобразование как при присваивании
- В операторе присваивания значение правого операнда преобразуется к неквалифицированному типу левого операнда.
- При инициализации скаляра значение выражения инициализатора преобразуется к неквалифицированному типу инициализируемого объекта.
- В выражении вызова функции, для функции с прототипом, значение каждого выражения аргумента преобразуется к типу неквалифицированного объявленного типа соответствующего параметра.
- В операторе возврата значение операнда
returnпреобразуется в объект, имеющий тип возвращаемого значения функции.
Обратите внимание, что фактическое присваивание, помимо преобразования, также удаляет дополнительный диапазон и точность из типов с плавающей точкой и запрещает перекрытия; эти характеристики не применяются к преобразованию как при присваивании.
Неявные повышения аргументов по умолчанию
В выражении вызова функции, когда вызов производится к
Каждый аргумент целочисленного типа подвергается целочисленному повышению (см. ниже), а каждый аргумент типа float неявно преобразуется к типу double
int add_nums(int count, ...); int sum = add_nums(2, 'c', true); // add_nums is called with three ints: (2, 99, 1)
| Обратите внимание, что | (с C99) |
Обычные арифметические преобразования
Аргументы следующих арифметических операторов подвергаются неявным преобразованиям для получения общего вещественного типа, который является типом, в котором выполняется вычисление:
- бинарные арифметические операторы *, /, %, +, -
- операторы сравнения <, >, <=, >=, ==, !=
- бинарные поразрядные арифметические операторы &, ^, |,
- условный оператор ?:
| 1) Если один из операндов имеет десятичный тип с плавающей точкой, то другой операнд не должен иметь стандартный тип с плавающей точкой, комплексный или мнимый тип.
| (с C23) |
long double, long double complex, или long double imaginary (с C99), другой операнд неявно преобразуется следующим образом: - целочисленный или вещественный тип с плавающей точкой в
long double
| (с C99) |
double, double complex, или double imaginary (с C99), другой операнд неявно преобразуется следующим образом: - целочисленный или вещественный тип с плавающей точкой в
double
| (с C99) |
float, float complex, или float imaginary (с C99), другой операнд неявно преобразуется следующим образом: - целочисленный тип в
float(единственный возможный вещественный тип — float, который остаётся без изменений)
| (с C99) |
- Если типы одинаковы, то этот тип является общим типом.
- В противном случае, типы разные:
- Если типы имеют одинаковую знаковость (оба со знаком или оба без знака), операнд, тип которого имеет меньший ранг преобразования1, неявно преобразуется2 в другой тип.
- В противном случае, операнды имеют разную знаковость:
- Если тип без знака имеет ранг преобразования не меньше ранга типа со знаком, то операнд с типом со знаком неявно преобразуется в тип без знака.
- В противном случае, тип без знака имеет ранг преобразования меньше ранга типа со знаком:
- Если тип со знаком может представлять все значения типа без знака, то операнд с типом без знака неявно преобразуется в тип со знаком.
- В противном случае, оба операнда подвергаются неявной конверсии в тип без знака, соответствующий типу операнда со знаком.
- 1. См. «Целочисленное повышение» ниже для правил ранжирования.
- 2. См. «Целочисленные преобразования» в разделе «Семантика неявных преобразований» ниже.
1.f + 20000001; // int is converted to float, giving 20000000.00
// addition and then rounding to float gives 20000000.00
(char)'a' + 1L; // first, char 'a', which is 97, is promoted to int
// different types: int and long
// same signedness: both signed
// different rank: long is of greater rank than int
// therefore, int 97 is converted to long 97
// the result is 97 + 1 = 98 of type signed long
2u - 10; // different types: unsigned int and signed int
// different signedness
// same rank
// therefore, signed int 10 is converted to unsigned int 10
// since the arithmetic operation is performed for unsigned integers
// (see "Arithmetic operators" topic), the calculation performed is (2 - 10)
// modulo (2 raised to n), where n is the number of value bits of unsigned int
// if unsigned int is 32-bit long and there is no padding bits in its object
// representation, then the result is (-8) modulo (2 raised to 32) = 4294967288
// of type unsigned int
5UL - 2ULL; // different types: unsigned long and unsigned long long
// same signedness
// different rank: rank of unsigned long long is greater
// therefore, unsigned long 5 is converted to unsigned long long 5
// since the arithmetic operation is performed for unsigned integers
// (see "Arithmetic operators" topic),
// if unsigned long long is 64-bit long, then
// the result is (5 - 2) modulo (2 raised to 64) = 3 of type
// unsigned long long
0UL - 1LL; // different types: unsigned long and signed long long
// different signedness
// different rank: rank of signed long long is greater.
// if ULONG_MAX > LLONG_MAX, then signed long long cannot represent all
// unsigned long therefore, this is the last case: both operands are converted
// to unsigned long long unsigned long 0 is converted to unsigned long long 0
// long long 1 is converted to unsigned long long 1 since the arithmetic
// operation is performed for unsigned integers
// (see "Arithmetic operators" topic),
// if unsigned long long is 64-bit long, then
// the calculation is (0 - 1) modulo (2 raised to 64)
// thus, the result is 18446744073709551615 (ULLONG_MAX) of type
// unsigned long long| Тип результата определяется следующим образом:
double complex z = 1 + 2*I; double f = 3.0; z + f; // z remains as-is, f is converted to double, the result is 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∞. | (с C99) |
Примечание: независимо от обычных арифметических преобразований, вычисление всегда может выполняться в типе уже, чем указано этими правилами, в соответствии с правилом «как если бы» (as-if rule)
Преобразования значений
Преобразование lvalue
Любое выражение lvalue любого не массивно-го типа, когда используется в любом контексте, кроме
- в качестве операнда оператора обращения к адресу (если разрешено)
- в качестве операнда префиксных/постфиксных операторов инкремента и декремента.
- в качестве левого операнда оператора обращения к члену (точка).
- в качестве левого операнда операторов присваивания и составного присваивания.
- в качестве операнда sizeof
подвергается преобразованию lvalue: тип остаётся тем же, но теряет const/volatile/restrict-квалификаторы и атомарные свойства, если таковые имеются. Значение остаётся тем же, но теряет свои свойства lvalue (адрес больше не может быть взят).
Если lvalue имеет неполный тип, поведение неопределено.
Если lvalue указывает на объект с автоматической длительностью хранения, адрес которого никогда не был взят, и если этот объект был неинициализирован (не объявлен с инициализатором и присвоение ему не производилось до использования), поведение неопределено.
Это преобразование моделирует загрузку значения объекта из его расположения в памяти.
volatile int n = 1; int x = n; // lvalue conversion on n reads the value of n volatile int* p = &n; // no lvalue conversion: does not read the value of n
Преобразование массива в указатель
Любое выражение lvalue типа массива, когда используется в любом контексте, кроме
- в качестве операнда оператора адресации
- в качестве операнда оператора sizeof
- в качестве операнда операторов typeof и typeof_unqual (с C23)
- в качестве строковой константы, используемой для инициализации массива
преобразуется в указатель на первый элемент массива, не являющийся ссылкой.
Если массив объявлен как register, поведение не определено.
int a[3], b[3][4]; int* p = a; // conversion to &a[0] int (*q)[4] = b; // conversion to &b[0]
Преобразование функции в указатель
Любое выражение, являющееся именем функции, при использовании в контексте, отличном от
- в качестве операнда оператора адресации
- в качестве операнда оператора sizeof
- в качестве операнда операторов typeof и typeof_unqual (с C23)
преобразуется в указатель на функцию, на которую ссылается выражение, не являющийся ссылкой.
int f(int); int (*p)(int) = f; // conversion to &f (***p)(1); // repeated dereference to f and conversion back to &f
Семантика неявных преобразований
Неявное преобразование, будь то как при присваивании или обычное арифметическое преобразование, состоит из двух этапов:
Совместимые типы
Преобразование значения любого типа в любой совместимый тип всегда является пустым преобразованием и не изменяет представление.
uint8_t (*a)[10]; // if uint8_t is a typedef to unsigned char unsigned char (*b)[] = a; // then these pointer types are compatible
Преобразования целочисленных типов
Целочисленное продвижение — это неявное преобразование значения любого целочисленного типа с рангом меньше или равным рангу int или битового поля типа _Bool(до C23)bool(с C23), int, signed int, unsigned int, к значению типа int или unsigned int.
Если int может представить весь диапазон значений исходного типа (или диапазон значений исходного битового поля), значение преобразуется к типу int. В противном случае значение преобразуется к типу unsigned int.
| Значение из битового поля битового целочисленного типа преобразуется в соответствующий битовый целочисленный тип. В противном случае битовые целочисленные типы освобождаются от правил целочисленного продвижения. | (с C23) |
Целочисленные продвижения сохраняют значение, включая знак:
int main(void)
{
void f(); // old-style function declaration
// since C23, void f(...) has the same behavior wrt promotions
char x = 'a'; // integer conversion from int to char
f(x); // integer promotion from char back to int
}
void f(x) int x; {} // the function expects intРанг выше — свойство каждого целочисленного типа и определяется следующим образом:
signed char < rank of short < rank of int < rank of long int < rank of long long int.
__int64 < ранг long long int, но ранг long long < ранг __int128 из-за правила (1))char равен рангу signed char и рангу unsigned char
_Bool(до C23)bool(с C23) меньше ранга любого другого стандартного целочисленного типа| 8) ранг битового целочисленного signed типа должен быть больше ранга любого стандартного целочисленного типа с меньшей шириной или любого битового целочисленного типа с меньшей шириной. 9) ранг любого битового целочисленного типа относительно расширенного целочисленного типа той же ширины определяется реализацией. | (с 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) |
Преобразования целочисленных типов
Значение любого целочисленного типа может быть неявно преобразовано в любой другой целочисленный тип. За исключением случаев, охватываемых продвижениями и преобразованиями в булевы типы выше, правила следующие:
- если целевой тип может представить значение, значение не изменяется
- в противном случае, если целевой тип беззнаковый, значение 2b, где b — количество битов значения в целевом типе, многократно вычитается или прибавляется к исходному значению, пока результат не поместится в целевой тип. Другими словами, беззнаковые целые числа реализуют арифметику по модулю.
- в противном случае, если целевой тип со знаком, поведение не определено (что может включать поднятие сигнала)
char x = 'a'; // int -> char, result unchanged
unsigned char n = -123456; // target is unsigned, result is 192 (that is, -123456+483*256)
signed char m = 123456; // target is signed, result is implementation-defined
assert(sizeof(int) > -1); // assert fails:
// operator > requests conversion of -1 to size_t,
// target is unsigned, result is SIZE_MAXПреобразования вещественного числа в целое
Конечное значение любого вещественного типа с плавающей точкой может быть неявно преобразовано в любой целочисленный тип. За исключением случаев, охватываемых преобразованием в булевы типы выше, правила следующие:
- Дробная часть отбрасывается (обрезается к нулю).
- Если полученное значение может быть представлено целевым типом, используется это значение
- в противном случае поведение не определено
int n = 3.14; // n == 3 int x = 1e10; // undefined behavior for 32-bit int
Значение любого целочисленного типа может быть неявно преобразовано в любой вещественный тип с плавающей точкой.
- если значение может быть представлено точно целевым типом, оно не изменяется
- если значение может быть представлено, но не точно, результатом является ближайшее большее или ближайшее меньшее значение (направление округления определяется реализацией), хотя при поддержке арифметики IEEE округление происходит до ближайшего
- если значение не может быть представлено, поведение не определено, хотя при поддержке арифметики 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; // undefined behavior
Преобразования вещественных типов с плавающей точкой
Значение любого вещественного типа с плавающей точкой может быть неявно преобразовано в любой другой вещественный тип с плавающей точкой.
- Если значение может быть представлено целевым типом точно, оно не изменяется
- если значение может быть представлено, но не точно, результатом является ближайшее большее или ближайшее меньшее значение (направление округления определяется реализацией), хотя при поддержке арифметики IEEE округление происходит до ближайшего
- если значение не может быть представлено, поведение не определено
Результат этого преобразования может иметь больший диапазон и точность, чем указывает его целевой тип (см. FLT_EVAL_METHOD).
double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625 float f = d; // f = 0.100000001490116119384765625 float x = 2*(double)FLT_MAX; // undefined
Преобразования комплексных типовЗначение любого комплексного типа может быть неявным образом преобразовано в любой другой комплексный тип. Действительная и мнимая части индивидуально следуют правилам преобразования для действительных плавающих типов. double complex d = 0.1 + 0.1*I; float complex f = d; // f is (0.100000001490116119384765625, 0.100000001490116119384765625) Преобразования мнимых типовЗначение любого мнимого типа может быть неявным образом преобразовано в любой другой мнимый тип. Мнимая часть следует правилам преобразования для действительных плавающих типов. 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 Преобразования действительных-мнимыхЗначение любого мнимого типа может быть неявным образом преобразовано в любой действительный тип (целый или с плавающей точкой). Результат всегда является положительным (или беззнаковым) нулем, за исключением случая, когда целевой тип Значение любого действительного типа может быть неявным образом преобразовано в любой мнимый тип. Результат всегда является положительным мнимым нулём. 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 returns void*
Указатель на неквалифицированный тип может быть неявным образом преобразован в указатель на квалифицированную версию этого типа (другими словами, квалификаторы const, volatile и restrict могут быть добавлены). Исходный указатель и результат сравнимы.
int n; const int* p = &n; // &n has type int*
Любое целое константное выражение со значением 0, а также целое выражение указателя со значением ноль, приведенное к типу void*, может быть неявным образом преобразовано в любой тип указателя (как указатель на объект, так и указатель на функцию). Результатом является значение нулевого указателя его типа, гарантированно не сравнимое с любым ненулевым значением указателя того же типа. Это целое или выражение void* известно как нулевой указатель константы, и стандартная библиотека предоставляет одно определение этой константы как макроса NULL.
int* p = 0; double* q = NULL;
Примечания
Хотя переполнение знакового целого числа в любом арифметическом операторе является неопределённым поведением, переполнение знакового целого типа при преобразовании целых чисел является просто неопределённым поведением.
С другой стороны, хотя переполнение беззнакового целого числа в любом арифметическом операторе (и при преобразовании целых чисел) является хорошо определённой операцией и следует правилам модульной арифметики, переполнение беззнакового целого числа при преобразовании из с плавающей точкой в целое является неопределённым поведением: значения действительного типа с плавающей точкой, которые могут быть преобразованы в беззнаковое целое, находятся в открытом интервале (-1; Unnn_MAX+1).
unsigned int n = -1.0; // undefined behavior
Преобразования между указателями и целыми числами (кроме от указателя к _Bool(до C23)bool(с C23) и (с C99)от целого константного выражения со значением ноль до указателя), между указателями на объекты (кроме случаев, когда либо от, либо до является указателем на void) и преобразования между указателями на функции (кроме случаев, когда функции имеют совместимые типы) никогда не являются неявными и требуют оператора приведения типов.
Нет преобразований (явных или неявных) между указателями на функции и указателями на объекты (включая void*) или целые числа.
Ссылки
- Стандарт C23 (ISO/IEC 9899:2023):
- 6.3 Преобразования (стр: TBD)
- Стандарт 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++ для Неявных преобразований |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/conversion