Преобразование типов
Ввиду статической типизации, записать число одного типа в переменную другого типа напрямую невозможно, необходимо приведение. В некоторых случаях приведение происходит автоматически: если операнды некоторой операции имеют разные типы, то они преобразуются из «узких» (которые занимают меньше места в памяти) к более «широким» без потери данных.
int a = 1;
float b = 2.0f;
float c = b + a; // a converts to float
Символьная переменная char
представляет собой число.
char letter = 'A'; // 'A' is 65 in ASCII
Стандарт при этом ничего не говорит о том, является ли char
беззнаковым, и в зависимости от компилятора реализация может быть разной. Для стандартной части ASCII (первые 127 символов) гарантируется, что при преобразовании из char
в int
результатом будет положительное число, а вот расширение ASCII на разных системах может дать как положительное, так и отрицательное. Если требуется хранить информацию не символьного характера, то имеет смысл явно указать знаковость при помощи модификатора.
Преобразования от более «широких» к более «узким» в общем случае происходит с потерей данных.
4 июля 1996 года из Французской Гваины была запущена ракета Arian V, разработка которой заняла 10 лет и стоила Европейскому Союзу порядка 7 млрд. долларов, вместе с полезной нагрузкой стоимостью 500 млн. долларов. На 34-й секунде полёта ракета взорвалась по причине ошибки в программном обеспечении: при попытке сконвертировать 64-битную переменную с плавающей запятой, отвечающую за горизонтальную скорость, в 16-битное число со знаком произошло переполнение регистра (число оказалось больше 32767, т.е. максимально допустимого для 16-битной переменной), что привело к исключительной ситуации и краху всей системы, так как проверка переполнения была отключена. ПО было написано на языке Ада.
uint32_t a = 1023;
uint8_t b = a; // b == 255
Большинство компиляторов при подобных преобразованиях выводят предупреждения. И несмотря на это, ошибки, связанные с преобразованием типов, одни из самых распространенных: программист самостоятельно должен отслеживать диапазоны. При преобразовании более «широких» к более «узким» типов лучше всего указывать преобразование явным способом, например так:
#define MM_PER_INCH 25.4f
uint8_t a = (uint8_t)(MM_PER_INCH);