Стивен Барретт - Встраиваемые системы. Проектирование приложений на микроконтроллерах семейства 68HC12/HCS12 с применением языка С
3.9.4. Оператор SWITCH
Данный оператор применяется, когда требуется передавать управление одному из нескольких операторов, в зависимости от значения выражения. Синтаксис оператора switch:
1 switch(<выражение>)
2 {
3 case <константное_выражение_1>:
4 <оператор_1>;
5 break;
6 case <константное_выражение_2>:
7 <оператор_2>;
8 break;
9 :
10 :
11 case <константное_выражение_n>:
12 <оператор_n>;
13 break;
14 default :
15 <оператор_n+1>;
16 }
Выполнение оператора switch начинается с вычисления выражения в скобках. Результат вычисления должен быть целочисленным в одно или двухбайтовом формате. Затем последовательно просматриваются префиксы case, указанные после них константные выражения вычисляются и их результаты сравниваются с результатом вычисления выражения в скобках после слова switch. Если результаты совпадают, то управление передаётся оператору, следующему за соответствующим префиксом case. Если ни одного совпадения не произошло и при этом указано служебное слово default (его указание необязательно), то управление передаётся оператору, следующему за этим служебным словом. Если совпадения не обнаружилось, а служебное слово default не указано, то ни один из операторов блока не выполняется. Заметим, что в константных выражениях после префиксов case не допускается применения переменных, выражение должно состоять из константных величин.
Приведем пример использования конструкции с оператором switch:
1 switch (a) {
2 case 1:
3 printf("Correct value%d was chosenn", a);
4 break;
5 case 2:
6 printf("Close but try againn");
7 break;
8 case 3:
9 printf("Value %d is two away from the answern", a);
10 break;
11 default:
12 printf("Your chosen value is way offn");
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, одиночных переменных или массивов из элементов в формате с плавающей запятой, а также для структур, определенных пользователем. Спецификация типа указателя информирует компилятор о размере и типе объекта, на который показывает указатель. Таким образом, при обращении к любой области памяти через указатель, содержимое этой области будет трактоваться в соответствие с заданным типом указателя. В микроконтроллерах, поддерживающих различные способы формирования адресов объектов, спецификация типа может влиять на размер области памяти, отводимой для хранения указателя.
Применение указателей в реальных программах требует особого внимания программистов, поскольку именно с указателями связано основное количество ошибок в исходном тексте программы на Си. При манипулировании указателями, а также при выполнении операций присваивания следует убедиться в корректном использовании различных типов переменных. Приведем пример применения указателя: