Продолжая использовать наш сайт, вы даете согласие на обработку файлов cookie, которые обеспечивают правильную работу сайта. Благодаря им мы улучшаем сайт!
Принять и закрыть

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.

Читать: Встраиваемые системы. Проектирование приложений на микроконтроллерах семейства 68HC12/HCS12 с применением языка С - Стивен Ф. Барретт на бесплатной онлайн библиотеке Э-Лит


Помоги проекту - поделись книгой:

15  <оператор_n+1>;

16 }

Выполнение оператора switch начинается с вычисления выражения в скобках. Результат вычисления должен быть целочисленным в одно или двухбайтовом формате. Затем последовательно просматриваются префиксы case, указанные после них константные выражения вычисляются и их результаты сравниваются с результатом вычисления выражения в скобках после слова switch. Если результаты совпадают, то управление передаётся оператору, следующему за соответствующим префиксом case. Если ни одного совпадения не произошло и при этом указано служебное слово default (его указание необязательно), то управление передаётся оператору, следующему за этим служебным словом. Если совпадения не обнаружилось, а служебное слово default не указано, то ни один из операторов блока не выполняется. Заметим, что в константных выражениях после префиксов case не допускается применения переменных, выражение должно состоять из константных величин.

Приведем пример использования конструкции с оператором switch:

1  switch (a) {

2  case 1:

3   printf("Correct value%d was chosen\n", a);

4   break;

5  case 2:

6   printf("Close but try again\n");

7   break;

8  case 3:

9   printf("Value %d is two away from the answer\n", a);

10  break;

11 default:

12  printf("Your chosen value is way off\n");

13 }

В примере текущее значение переменной a сравнивается со значениями 1, 2 и 3. Желаемое сообщение о правильном выборе появится на экране монитора в том случае, если переменная равна 1. Если же переменная равна 2 или 3, то будут выведены соответствующие сообщения об ошибках. Если ни одно из трех возможных значений не выбрано, то отобразится сообщение, записанное в строке 12. Обратите внимание, в примере мы не должны записывать слово break в строке 13, т.к. после выполнения оператора строки 12 программа автоматически продолжит исполнение следующих за конструкцией switch операторов.

А как будет вести себя программа, если мы пропустим слово break где то в середине конструкции switch, например, в строке 4? После отображения сообщения строки 3 программа перейдет к исполнению следующих операторов, пока не встретит следующее слово break или не дойдет до конца конструкции switch. В нашем случае программа отобразит сообщение строки 6, а затем предаст управление строке 13. Зная подобную особенность оператора switch, Вы можете пропустить несколько break в своей программе.

3.10. Массивы

Массив определяет непрерывный набор однотипных объектов данных. Признаком массива служит использование квадратных скобок после идентификатора переменной. При определении в квадратных скобках указывается количество элементов массива (его размер), а при использовании в выражениях — индекс требуемого элемента. Массивы могут содержать элементы в любом из ранее рассмотренных типов представления данных: char, int, float double. Пример определения массива из 10 двухбайтовых целочисленных элементов:

int list[10];

Данное определение зарезервирует в памяти МК 20 однобайтовых ячеек памяти. В выражениях программы доступ к каждому элементу массива возможен с использованием индекса. Индекс в Си автоматически отсчитывается с 0. Поэтому в нашем примере индекс может принимать значения от 0 до 9 включительно. Как и любая другая переменная, массив может быть объявлен с одновременной инициализацией его значений. Для этого в правой части операции присваивания «=» в фигурных скобках, через запятую перечисляются значения всех элементов массива:

int list[10] = {1,2,3,4,5,6,7,8,9,10};

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

int list[] = {1,2,3,4,5,6,7,8,9,10}; // Размер массива будет равен

 //числу проинициализированных переменных т.е. 10

Также можно инициализировать произвольное число первых элементов массива с указанным размером:

int list[10]= {1,2,3,4,5,}; // Инициализация первых пяти элементов

 //массива, состоящего из 10 элементов

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

char message[6]= "Smile";

char message[]= "Smile";

Первый элемент массива message[0] содержит код символа «S», последний элемент message[5] — код конца строки 0x00.

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

int matrix[2],[3];

По аналогии с одномерными массивами, для многомерных массивов возможна инициализация в процессе его определения:

int matrix[2],[3]= {1,2,3}{4,5,6};

Многомерный массив будет располагаться в памяти в следующем порядке:

m[0][0] m[0][1] m[0][2] m[1][0] m[1][1] m[1][2]

Обращение к элементам многомерного массива осуществляется посредством указания сразу нескольких индексов. Так после выполнения следующего выражения переменная m примет значение 6:

m = matrix[1][2]

Объединение некоторой группы данных в массив позволяет оперировать с этой группой, используя всего лишь одно имя переменной. Программы становятся более логичными и читабельными, что уменьшает вероятность появления ошибок программирования. Допустим по ходу исполнения алгоритма необходимо каждый элемент массива odd из 100 элементов, увеличить на 1. Следующий программный фрагмент демонстрирует реализацию этой задачи:

for (i=0, i<100, i++) odd[i] = odd[i] + 1;

Внимательный читатель должен был бы заметить, что в примерах этого параграфа мы использовали только массивы целочисленных элементов. Далее в примерах программирования МК семейства 68HC12 будут использоваться именно целочисленные исчисления, поскольку вычисления в формате с плавающей запятой предполагают достаточно сложный алгоритм управления, который вряд ли уместен при начальном обучении. Однако мы рассмотрим более сложный вариант массива — массив структур в параграфе 3.12.

3.11. Указатели

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

Для определения переменной указателя используется оператор *:

int *ptr

Приведенная запись означает, что переменная ptr содержит в себе адрес старшего байта двухбайтовой переменной типа int. Переменная указатель может быть определена для адресации различных типов переменных: целочисленных однобайтовых типа char и двухбайтовых типа int, одномерных и многомерных массивов из переменных char или int, одиночных переменных или массивов из элементов в формате с плавающей запятой, а также для структур, определенных пользователем. Спецификация типа указателя информирует компилятор о размере и типе объекта, на который показывает указатель. Таким образом, при обращении к любой области памяти через указатель, содержимое этой области будет трактоваться в соответствие с заданным типом указателя. В микроконтроллерах, поддерживающих различные способы формирования адресов объектов, спецификация типа может влиять на размер области памяти, отводимой для хранения указателя.

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

1 void main(void)

2 {

3  char *ptr;

4  static char message[] = "What a wonderful day!";

5  ptr = message;

6  printf("%s\n", ptr);

7 }

В этом примере переменная ptr была определена как указатель для однобайтовой переменной типа char. Поэтому переменная ptr должна содержать 16 разрядный адрес однобайтовой переменной. В строке 4 примера был определен и инициализирован массив однобайтовых переменных message. Этот массив содержит 22 элемента: 21 символ в кодах ASCII и один байт признака конца строки. Выражение в строке 5 имеет своей целью запись в переменную ptr начального адреса массива message, т.е. адреса символа «W». Выражение, записанное в строке 6, должно начать вывод на экран монитора символа, на который указывает содержимое переменной указателя ptr. В строке 5 данного примера может быть записано другое, более понятное выражение:

ptr = &message[0];

В этом выражении символ «&» выполняет операцию взятия адреса первого элемента массива message. И именно эта функция была реализована нами в строке 5 исходного примера.

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

void main(void) {

 static char message[] = "What a wonderful day!";

 int i;

 for (i=0; i<21; i++) putchar(message[i]);

}

Обратите внимание, что в данном примере внутренняя переменная цикла i принимает значения от 0 до 20 включительно, выводя при этом на экран 21 символ. Приведенный способ реализации задачи вывода на экран строки символов обладает одним существенным недостатком. При смене числа символов в записи, выводимой на экран, придется написать новый фрагмент кода. Указанный недостаток исправлен в следующем программном фрагменте:

void main(void) {

 char *ptr;

 static char message[] = "What a wonderful day!";

 ptr = message;

 while(*ptr != '\0') {

  putchar(*ptr);

  ptr++;

 }

}

В этом примере указатель ptr используется для передачи в функцию putchar одного кода символа, который будет выведен на экран. Далее содержимое ptr увеличивается на 1, адресуя тем самым следующий элемент массива. По условию цикла while вывод на экран закончится, если в последовательно перебираемом массиве будет найден код конца строки.

Во встраиваемых системах указатели используются для обращения к регистрам специальных функций микроконтроллера. Рассмотрим следующую запись:

#define PORTA *(volatile unsigned char *) 0x1000

Эта запись информирует компилятор о том, что однобайтовая целочисленная переменная с именем PORTA располагается в памяти по адресу 0x1000. Служебное слово volatile информирует программу (и отладчик) о том, что значение PORTA может изменяться не только под управлением программы. Любое упоминание имени PORTA далее по тексту программы будет связано с выполнением операций чтения или записи в регистр данных PORTA по его физическому адресу 0x1000. Поэтому следующий программный фрагмент позволит прочитать содержимое порта PORTA в переменную new_value:

DDRA = 0x00; //инициализировать порт PORTA на ввод

new_value = PORTA; //читать содержимое регистра данных PORTA в

                   //переменную new_value

3.12. Структуры

Возможности языка Си позволяют объединить под одним именем переменные с разным форматом представления данных. Для этого используется понятие структуры.

Структура — это объект, состоящий из данных различных типов. При использовании структур следует различать объявление (или описание) структуры, как нового типа данных, например тип данных «структура x», от фактического определения некоторой переменной с типом данных «структура x». Пример объявления структуры типа car:

struct car {

 int doors;

 char color[10];

 char *maker;

 int num_cyl;

 int year;

}

Служебное слово struct указывает начало объявления или определения структуры. За ним следует имя, которое присваивается данной структуре, оно называется идентификатором структуры. В фигурных скобках указывается список определений отдельных элементов, образующих структуру. Элементы структуры могут иметь любой тип, включая массивы, другие структуры и объединения.

Анализируя приведенную запись, отметим, что имя car — это имя нового типа данных. Не следует путать его с именем переменных типа car, которые далее будут определены в программе:

struct car your_car;

struct car my_car;

struct car his_car;

Записав приведенные выше строки, мы определили в программе три переменные с именами your_car, my_car, his_car, и каждая из этих переменных представляет собой структуру с одним и тем же набором элементов. Так первым элементом каждой из этих переменных структур будет двухбайтовое целое число. Численные значения этих первых элементов для всех переменных структур различаются, но формат представления данных для всех трех первых элементов одинаков.



Поделиться книгой:

На главную
Назад