mirror of
https://github.com/Nekrolm/ubbook.git
synced 2025-12-18 05:14:34 +03:00
76 lines
4.4 KiB
Markdown
76 lines
4.4 KiB
Markdown
# Integer promotion
|
||
|
||
C++ от C досталось тяжелое наследство. Одна его часть была исправлена и беспощадно зарезана для большей надежности — так, например, поступили с неявными `const` преобразованиями. Другая же часть, доставляющая не меньше проблем, перешла в первозданном виде.
|
||
|
||
В C и C++ много различных типов целых чисел разных размеров. И над ними определены операции. Правда, операции определены не для каждого типа чисел.
|
||
|
||
Например, здесь нет +, -, *, / для `uint16_t`. Но применить мы их можем.
|
||
|
||
```C++
|
||
uint16_t x = 1;
|
||
uint16_t y = 2;
|
||
auto a = x - y;
|
||
auto b = x + y;
|
||
auto c = x * y;
|
||
auto d = x / y;
|
||
```
|
||
и [результатом](https://godbolt.org/z/bdKjsor9T) операций над беззнаковыми числами станет число со знаком.
|
||
Но стоит тип хотя бы одного аргумента поменять на `uint32_t`, как результат сразу же [теряет знак](https://godbolt.org/z/aY1nhdr37).
|
||
|
||
## Что происходит?
|
||
|
||
Происходят две неявные операции:
|
||
|
||
1. Типы, меньшие `int`, приводятся к `int` (integer promotion). Знаковому! Независимо от знаковости исходного типа!
|
||
2. Когда в операции участвуют аргументы разных типов целых чисел, они приводятся к общему типу (usual arithmetic conversion):
|
||
- Меньший тип приводится к большему
|
||
- Если размеры одинаковы, то знаковый приводится к беззнаковому
|
||
|
||
Аналогичные операции проводятся и над числами с плавающей точкой.
|
||
За полной таблицей и цепочкой, что и в кого неявно превращается, стоит обратиться к тексту [стандарта](https://eel.is/c++draft/conv.rank).
|
||
|
||
## К чему это приводит?
|
||
|
||
1. К ошибкам в логике:
|
||
Неявные преобразования вовлекаются в любую операцию. Вы выполняете сравнение знакового и беззнакового числа и забыли явно привести типы? Готовьтесь к тому, что `-1 < 1` может [вернуть](https://godbolt.org/z/sqvrasjE4) `false`:
|
||
```C++
|
||
std::vector<int> v = {1};
|
||
auto idx = -1;
|
||
if (idx < v.size()) {
|
||
std::cout << "less!\n";
|
||
} else {
|
||
std::cout << "oops!\n";
|
||
}
|
||
```
|
||
2. К [неопределенному поведению](https://godbolt.org/z/M3Kx3e3q6):
|
||
```C++
|
||
unsigned short x=0xFFFF;
|
||
unsigned short y=0xFFFF;
|
||
auto z=x*y;
|
||
```
|
||
Integer promotion неявно приводит `x` и `y` к `int`, в котором происходит переполнение. Переполнение `int` — неопределенное поведение.
|
||
3. К трудностями в переносе программ с одной платформы на другую. Если меняется размер `int`/`long` применение правил неявных конверсий к вашему коду также [меняется](https://godbolt.org/z/hs59o3zca):
|
||
```C++
|
||
std::cout << (-1L < 1U);
|
||
```
|
||
Выводит разные значения в зависимости от размера типа `long`.
|
||
|
||
## Что делать?
|
||
|
||
1. Не смешивать в одном выражении знаковые и беззнаковые типы
|
||
2. Уделять особое внимание коду, работающему с типами, меньшими `int`.
|
||
3. Включать предупреждения от компилятора (`-Wconversion`, не всегда работает)
|
||
|
||
|
||
## Полезные ссылки
|
||
1. https://eel.is/c++draft/conv.prom
|
||
2. https://eel.is/c++draft/expr.arith.conv
|
||
3. https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules
|
||
4. https://shafik.github.io/c++/2021/12/30/usual_arithmetic_confusions.html
|
||
|
||
|
||
|
||
|
||
|
||
|