Си для микроконтроллеров и чайников. Часть 3. Преобразование типов - типичный источник ошибок. Раздел: Начинающим.

Eugene's MCU

Вторник, 29.10.2013, 22:43

Главная | | Мой профиль | Выход | RSS
Меню сайта
Категории каталога
Рекламный блок



схемы новости электроники

Рекомендовать этот сайт:

Главная » Статьи » » Начинающим


Си для микроконтроллеров и чайников. Часть 3. Преобразование типов - типичный источник ошибок
Краткое предисловие для начинающих: прочитать
Преобразование типов переменных – это часть внутренней автоматической работы компилятора, происходящая в строгом соответствии с правилами языка программирования. Сам разработчик при написании программы в явном виде этим, как правило, не занимается. Однако, неаккуратное объявление типов переменных, или присвоение переменной значения превышающего допустимый диапазон, и даже неправильный формат записи константы, могут привести к потере данных и некорректной работе программы, при полном молчании компилятора.
Когда происходит и в чём заключается приведение типов? Таких ситуаций достаточно много. Рассмотрим наиболее опасные из них.
§ > Преобразование типа выражения перед присвоением переменной.

В первом разделе мы обращали своё внимание на необходимость явного указания типа объявляемой переменной. Это позволяет компилятору зарезервировать за ней нужное количество адресного пространства и определить диапазон значений, которые она способна хранить. Тем не менее, мы не застрахованы он того, что в процессе выполнения программы произойдёт попытка записать в переменную значение свыше предельно допустимого. В самых грубых случаях компилятор выдаст нам сообщение о возможной ошибке. Например, при желании записать в переменную типа unsigned char (диапазон от 0 до 255) число 400:
unsigned char a=400; // выдаст сообщение типа "integer conversion resulted in truncation”


компилятор предупреждает нас о том, что произошла попытка записать числовое значение, требующее для хранения два байта (400 это 1 в старшем байте и 144 в младшем) в однобайтовую переменную. Однако тех случаях, когда присваиваемое выражение содержит переменные, и компилятор мог бы заметить возможную потерю данных, он освобождает себя от этой обязанности, например:
unsigned char x=200, y=200 ;
x=x+y;

при таком варианте, не смотря на то, что значение выражение (x+y) так же равно 400, никаких предупреждений со стороны компилятора уже не последует. А в переменную x запишется только младший байт числа 400, то есть 144. И здесь компилятор трудно в чём-то упрекнуть, ведь вместо явно проинициализированной переменной в выражении может быть использован, например, приёмный регистр шины UART, в котором может оказаться любое значение, принятое от внешнего устройства.
Другой пример в этом же духе – присвоение дробного значения переменной целого типа:
float a=1.5; // Объявлена переменная с плавающей точкой.
char b=3; // Объявлена целочисленная переменная.
b=a*b; // Ожидается, что в переменную b будет записано значение 4,5.

В результате в переменной b сохранится только целая часть результата a*b – число 4.
§ > Преобразование результата выражения к типу наиболее точной переменной в выражении.
При таком преобразовании компилятор руководствуется следующим правилом: прежде чем начнется вычисление выражения, операторы с "низшим” типом повышаются до "высших” при этом результат также приводится к ”высшему” типу. Какой тип нужно считать ”высшим”? Тот, который без потери точности может сохранить любое допустимое значение другого типа. Так, в предыдущем примере:
float a =1.5; // Объявлена переменная a с плавающей точкой.
char b=3; // Объявлена целочисленная переменная.


В выражении (a*b) переменная float a имеет более высокий тип, потому что может сохранять любое целое значение из диапазона 0…255 типа char. Результат выражения (a*b) будет иметь тип float.
Типичный пример неожиданности для этого случая – попытка получить дробное число делением двух целочисленных:
char a=3; // Объявлена целочисленная переменная.
char b=4; // Объявлена целочисленная переменная.
float c; // Объявлена переменная "c" с плавающей точкой для сохранения результата.
c=a/b; // Ожидается, что "c" будет равно 0,75 ( ¾ ) .


В отличие от предыдущего примера, результат записывается в переменную способную хранить числа с плавающей точкой, однако компилятор в соответствии с правилом приведения, получив в результате деления число 0,75 приводит его к типу целочисленных операндов, отбросив дробную часть. В результате в переменную "c” будет записан ноль.
Более реалистичный пример из жизни – расчёт измеряемого напряжения из выходного кода АЦП:
int ADC; // Двухбайтовая целочисленная переменная для хранения кода АЦП.
float U; // Переменная с плавающей точкой для сохранения значения напряжения.
U= ADC*(5/1024); // Расчёт напряжения.


Здесь упущено из виду то, что константа в Си, как и любая переменная, тоже имеет свой тип. Его желательно указывать явно или, используя соответствующую форму записи. Константы 5 и 1024 записаны без десятичной точки и будут восприняты языком Си как целочисленные. Как следствие, результат выражения (5/1024) тоже будет приведён к целому – 0 вместо ожидаемого 0,00489. Это не случилось бы при записи выражения в формате (5.0/1024).
Приведённых ошибок также можно избежать, используя оператор явного приведения типов выражений языка Си, который записывается в виде названия типа, заключённого в круглые скобки и воздействует на выражение стоящее после него. Этот оператор приводит результат выражения к явно указанному типу, не взирая на типы его операндов:
c= (float) a/b; // Ожидается, что "c" будет равно 0,75 (¾);
U= ADC * ( (float)5/1024 ); // Расчёт напряжения.


Читать далее >> Часть 4. Функции.


При использовании материалов сайта ссылка на данный источник обязательна.

Категория: Начинающим | Добавил: eugenemcu (14.11.2009) | Автор:

Просмотров: 19389 | Комментарии: 2 | Рейтинг: 5.0 / 12 |
Всего комментариев: 2
* 1 (27.06.2010 21:00) [ Материал ]
Очень удачное описание материала. Добавьте, пожалуйста в конце ссылку на Часть 4.

* 2 (31.01.2012 04:00) [ Материал ]
safdasspfslddfsp

Статистика

Наш магазин




Какую среду разработки Вы используете?

[ Результаты · Архив опросов ]
Всего ответов: 1352



Им нужна Ваша помощь: