Namespaces
Variants

Implicit conversions

From cppreference.net

Когда выражение используется в контексте, где ожидается значение другого типа, может произойти преобразование :

int n = 1L; // выражение 1L имеет тип long, ожидается int
n = 2.1; // выражение 2.1 имеет тип double, ожидается int
char* p = malloc(10); // выражение malloc(10) имеет тип void*, ожидается char*

Преобразования происходят в следующих ситуациях:

Содержание

Преобразование как при присваивании

  • В операторе присваивания значение правого операнда преобразуется в неквалифицированный тип левого операнда.
  • При скалярной инициализации значение выражения инициализатора преобразуется в неквалифицированный тип инициализируемого объекта.
  • В выражении вызова функции с прототипом значение каждого аргумента преобразуется в тип неквалифицированных объявленных типов соответствующего параметра.
  • В операторе return значение операнда return преобразуется в объект, имеющий возвращаемый тип функции.

Обратите внимание, что фактическое присваивание, помимо преобразования, также удаляет лишний диапазон и точность из типов с плавающей запятой и запрещает перекрытия; эти характеристики не применяются к преобразованию как при присваивании.

Продвижения аргументов по умолчанию

В выражении вызова функции когда вызов осуществляется

1) функция без прототипа function without a prototype (until C23) ,
2) функция с переменным числом аргументов variadic function , где выражение аргумента является одним из завершающих аргументов, которые сопоставляются с параметром-многоточием.

Каждый аргумент целочисленного типа подвергается целочисленному повышению , и каждый аргумент типа 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) Если один операнд имеет десятичный плавающий тип, другой операнд не должен иметь стандартный плавающий,

комплексный или мнимый тип.

  • Сначала, если тип любого операнда - _Decimal128 , другой операнд преобразуется в _Decimal128 .
  • Иначе, если тип любого операнда - _Decimal64 , другой операнд преобразуется в _Decimal64 .
  • Иначе, если тип любого операнда - _Decimal32 , другой операнд преобразуется в _Decimal32 .
(начиная с C23)
2) В противном случае, если один операнд имеет тип long double , long double complex , или long double imaginary (since C99) , другой операнд неявно преобразуется следующим образом:
  • целочисленный или вещественный тип с плавающей точкой в long double
(since C99)
3) В противном случае, если один операнд имеет тип double , double complex , или double imaginary (since C99) , другой операнд неявно преобразуется следующим образом:
  • целочисленный или вещественный тип с плавающей точкой преобразуется в double
  • комплексный тип преобразуется в double complex
  • мнимый тип преобразуется в double imaginary
(since C99)
4) В противном случае, если один операнд имеет тип float , float complex , или float imaginary (начиная с C99) , другой операнд неявно преобразуется следующим образом:
  • целочисленный тип в float (единственный возможный вещественный тип — float, который остается без изменений)
  • комплексный тип остается float complex
  • мнимый тип остается float imaginary
(начиная с C99)
5) В противном случае оба операнда являются целочисленными. Оба операнда проходят integer promotions ; затем, после целочисленного продвижения, применяется один из следующих случаев:
  • Если типы одинаковы, этот тип является общим типом.
  • Иначе, типы различны:
    • Если типы имеют одинаковую знаковость (оба знаковые или оба беззнаковые), операнд, чей тип имеет меньший conversion rank  [1] неявно преобразуется [2] к другому типу.
    • Иначе, операнды имеют различную знаковость:
      • Если беззнаковый тип имеет conversion rank больше или равный рангу знакового типа, тогда операнд со знаковым типом неявно преобразуется к беззнаковому типу.
      • Иначе, беззнаковый тип имеет conversion rank меньше знакового типа:
        • Если знаковый тип может представить все значения беззнакового типа, тогда операнд с беззнаковым типом неявно преобразуется к знаковому типу.
        • Иначе, оба операнда проходят неявное преобразование к беззнаковому аналогу типа знакового операнда.
  1. См. integer promotions ниже для правил определения ранга.
  2. См. "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

Тип результата определяется следующим образом:

  • если оба операнда являются комплексными, тип результата - комплексный;
  • если оба операнда являются мнимыми, тип результата - мнимый;
  • если оба операнда являются вещественными, тип результата - вещественный;
  • если два операнда с плавающей точкой имеют различные домены типов (комплексный vs вещественный, комплексный vs мнимый, или мнимый vs вещественный), тип результата - комплексный.
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 выражение любого не-массивного типа, когда используется в любом контексте, кроме

подвергается 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 , когда используется в любом контексте, кроме

подвергается преобразованию в не-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; }

Преобразование функции в указатель

Любое выражение-обозначение функции, когда оно используется в любом контексте, кроме

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

int f(int);
int (*p)(int) = f; // преобразование в &f
(***p)(1); // многократное разыменование до f и преобразование обратно в &f

Семантика неявного преобразования

Неявное преобразование, будь то как при присваивании  или обычное арифметическое преобразование  , состоит из двух этапов:

1) преобразование значения (если применимо),
2) одно из преобразований, перечисленных ниже (если оно может создать целевой тип).

Совместимые типы

Преобразование значения любого типа в любой совместимый тип всегда является 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 выше — это свойство каждого целочисленного типа и определяется следующим образом:

1) ранги всех знаковых целочисленных типов различны и увеличиваются с их точностью: ранг signed char < ранг short < ранг int < ранг long int < ранг long long int
2) ранги всех знаковых целочисленных типов равны рангам соответствующих беззнаковых целочисленных типов
3) ранг любого стандартного целочисленного типа больше ранга любого расширенного целочисленного типа или биточно-точного целочисленного типа (начиная с C23) того же размера (то есть ранг __int64 < ранг long long int , но ранг long long < ранг __int128 согласно правилу (1) )
4) ранг char равен рангу signed char и рангу unsigned char
5) ранг _Bool (until C23) bool (since C23) меньше ранга любого другого стандартного целочисленного типа
6) ранг любого перечислимого типа равен рангу его совместимого целочисленного типа
7) ранжирование транзитивно: если ранг T1 < ранг T2 и ранг T2 < ранг T3, то ранг T1 < ранг T3.
8) ранг знакового целочисленного типа с точной битовой шириной должен быть больше ранга любого стандартного целочисленного типа с меньшей шириной или любого целочисленного типа с точной битовой шириной с меньшей шириной.
9) ранг любого целочисленного типа с точной битовой шириной относительно расширенного целочисленного типа той же ширины определяется реализацией.
(начиная с C23)
10) любые аспекты относительного ранжирования расширенных целочисленных типов, не охваченные выше, определяются реализацией.

Примечание: целочисленные повышения применяются только

  • в рамках обычных арифметических преобразований (см. выше),
  • в рамках стандартных продвижений аргументов (см. выше),
  • к операнду унарных арифметических операторов + и - ,
  • к операнду унарного побитового оператора ~ ,
  • к обоим операндам операторов сдвига << и >> .

Преобразование в булев тип

Значение любого скалярного типа может быть неявно преобразовано в _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 округление происходит к ближайшему.
  • Если значение не может быть представлено, поведение не определено .

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

double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625
float f = d;    // f = 0.100000001490116119384765625
float x = 2 * (double)FLT_MAX; // неопределенное поведение

Преобразования комплексных типов

Значение любого комплексного типа может быть неявно преобразовано в любой другой комплексный тип. Действительная и мнимая части следуют правилам преобразования для вещественных типов с плавающей точкой.

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

Вещественно-комплексные преобразования

Значение любого вещественного типа с плавающей точкой может быть неявно преобразовано в любой комплексный тип.

  • Действительная часть результата определяется правилами преобразования для вещественных типов с плавающей точкой.
  • Мнимая часть результата является положительным нулем (или беззнаковым нулем в системах без IEEE).

Значение любого комплексного типа может быть неявно преобразовано в любой вещественный тип с плавающей точкой.

  • Действительная часть преобразуется по правилам для вещественных типов с плавающей точкой.
  • Мнимая часть отбрасывается.

Примечание: при преобразовании из комплексного в вещественный тип, 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++ документация для Неявные преобразования