Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Разработка ПО на языке Си для микроконтроллера AT91SAM7S.pdf
Скачиваний:
120
Добавлен:
18.05.2014
Размер:
838.69 Кб
Скачать

11Оптимизация и её запрет, ключевое слово volatile

Оптимизация – это деятельность компилятора, направленная на изменение

исполняемого кода программы для того, чтобы увеличить его быстродействие или уменьшить размер занимаемой программой памяти, но при этом сохранить его поведение.

Оптимизация выполняется при помощи автоматического анализа исходного кода программы: поиска в ней избыточных или бесполезных инструкций, заменой медленных конструкций на более быстрые и т. д. Например, если в теле цикла выполняется разыменование указателя, но при этом ни сам указатель, ни переменная, на которую он ссылается, не изменяется внутри цикла, то вполне возможно, что повторяющееся разыменование будет вынесено компилятором за пределы тела цикла (т. е. заменено на однократное). Это произойдёт из-за того, что компилятор посчитает, что значение переменной по указателю не изменяется при выполнении цикла. Выполняя оптимизацию компилятор «исходит из предположения» о том, что любое изменение состояния ЭВМ вызывается только анализируемой им программой. Однако, если этот указатель является указателем на регистр ввода-вывода, отражённый в память, то содержимое ячеек памяти, на которое ссылается указатель может меняться «без ведома» компилятора (например, при нажатии кнопки на отладочной плате). Таким образом, поведение программы после оптимизации скорее всего будет не таким, как ожидалось.

Для выборочного отключения оптимизации для конкретной переменной (обычной или указателя) используется ключевое слово volatile. Оно указывается перед типом данных при описании (определении) переменной.

Как правило volatile применяется:

для указателей на регистры ввода-вывода, находящиеся в общем адресном пространстве (а также для элементов структур, группирующих данные регистры);

для глобальных переменных, изменения которых возможны из прерываний.

35

12Препроцессор Препроцессором называется программное средство, обрабатывающее

исходный текст программы непосредственно перед тем, как он будет передан для обработки компилятору. Часто препроцессор является составной частью компилятора, а препроцессинг рассматривается как фаза компиляции. Основное назначение препроцессора Си заключается в выполнении текстовых подстановок и реализация механизма условной компиляции.

Препроцессор управляется специальными командами, называемыми директивами препроцессора, которые размещаются прямо внутри исходных файлов. Набор этих директив определяет содержание исходного кода, который будет передан на обработку непосредственно компилятору.

12.1 Текстовые подстановки 12.1.1 Макросы

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

Макросы бывают двух видов:

-макроопределения – это простые макросы-выражения;

-макрофункции – это макросы, принимающие аргументы; исходный код, который будет развёрнут компилятором из макрофункций зависит от переданных аргументов;

Для создания макросов используется директива #define (пример см. в листинге 20). Препроцессор, находя в исходном тесте программы макросы, определённые ранее с использованием директивы #define выполняет замену имён макросов на соответствующие значения. Обычно макросы записывают большими буквами.

Листинг 20. Синтаксис директив препроцессора для определения макросов

//Синтаксис директивы #define для макроопределения:

//#define NAME VALUE

//NAME – имя макроопределения.

36

//VALUE – текст, который подставляется вместо имени макроопределения

//(может отсутствовать).

//Пример – число ПИ.

#define PI_VALUE 3,141592653589793 float circleLength (float radius)

{

//Препроцессор раскрывает строку в следующий вид:

//float length = 2.0 * 3,141592653589793 * radius; float length = 2.0 * PI_VALUE * radius;

}

//Синтаксис директивы #define для макрофункций:

//#define FUNCTION(ARG1, ARG2, ...) VALUE

//FUNCTION – имя макрофункции, ARGn – аргументы.

//VALUE – выражение использующее переданные аргументы.

//Пример: макрос для кодирования версии программы с помощью двух чисел.

//Например, версия 1.12 будет закодирована как число 0x010C

#define COMBINE_VERSION(x,y) ((x << 16) | y)

void embeddProtocolVersion (char * packet)

{

//Препроцессор трансформирует строку в следующий вид:

//(unsigned short*)(packet + 3) = ((1 << 16) | 12); (unsigned short*)(packet + 3) = COMBINE_VERSION(1, 12);

}

Макросы являются удобным инструментом и широко применяются в программах на языке Си, однако использовать их стоит с осторожностью, т. к. их неаккуратное применение может привести к трудно обнаруживаемым ошибкам.

Макросы существуют только на этапе обработки программы препроцессором, таким образом, невозможно изменить значение какого-либо макроса во время выполнения программы (он уже не существует).

12.1.2 Директива #include

Директива #include, которая часто встречаются в начале исходных файлов служит для встраивания содержимого указанного в директиве файла внутрь обрабатываемого препроцессором файла. В большинстве случаев это используется для того, чтобы избавиться от необходимости объявления используемых типов данных, функций и макросов в каждом исходном файле.

37