Uploaded by WarDe Broile

Si

advertisement
Министерство транспорта Российской Федерации
Федеральное агентство железнодорожного транспорта
Федеральное государственное бюджетное
образовательное учреждение высшего образования
«Дальневосточный государственный
университет путей сообщения»
Кафедра «Системы электроснабжения»
В.К. Духовников, С.А. Власенко
ОСНОВЫ ЯЗЫКА ПРОГРАММИРОВАНИЯ СИ
ДЛЯ МИКРОКОНТРОЛЛЕРОВ PIC
Рекомендовано
методическим советом по качеству
образовательной деятельности ДВГУПС
в качестве учебного пособия
Хабаровск
Издательство ДВГУПС
2019
1
УДК 621.316.544.1.049.77 (075.8)
ББК З 844.150.2я73 + З 973.22я73
Д 85
Рецензенты:
Кафедра технических дисциплин
Педагогического гуманитарного университета им. Шолом-Алейхема
(исполняющий обязанности заведующего кафедрой А.П. Афанасьев)
Главный инженер Трансэнерго – филиала ОАО «РЖД»
Е.А. Синкевич
Духовников, В.К.
Д 85
Основы языка программирования Си для микроконтроллеров PIC :
учеб. пособие / В.К. Духовников, С.А. Власенко. – Хабаровск :
Изд-во ДВГУПС, 2019. – 92 с. : ил.
Учебное пособие соответствует рабочей программе дисциплины «Основы микропроцессорной техники».
Рассматриваются теоретические аспекты программирования, основные
операторы и структура программ языка высокого уровня С. Приводится
ряд примеров решения конкретных задач с использованием многогранных
средств и методов языка высокого уровня С. Представлены вопросы для
самоконтроля.
Учебное пособие предназначено для студентов 3-го курса очной формы обучения и 5-го курса заочной формы обучения по направлению подготовки 13.03.02 «Электроэнергетика и электротехника» и по специальности 23.05.05 «Системы обеспечения движения поездов».
УДК 621.316.544.1.049.77 (075.8)
ББК З 844.150.2я73 + З 973.22я73
© ДВГУПС, 2019
2
ВВЕДЕНИЕ
С момента появления микропроцессоров разработка программного
обеспечения происходила исключительно на том или ином языке ассемблера, ориентированном на конкретное устройство и логику его работы –
это являлось и является главным недостатком языков такого типа. Язык С,
являясь языком высокого уровня, лишен подобных недостатков и может
использоваться для программирования любого микропроцессора, для которого есть компилятор с языка С.
Язык С был создан в конце 1970-х гг. в Bell Labs, Нью-Джерси, США.
В 1978 г. Брайеном Керниганом и Денисом Ритчи опубликована его первая
общедоступная документация, которая получила широкую известность под
называнием версия «K&R». В 1989 г. эта версия С была признана Американским национальным институтом стандартов (ANSI), а в 1990 г. принята
Международной ассоциацией стандартов (ISO).
В языке высокого уровня С все низкоуровневые операции, выполняемые компьютерами, представлены в виде абстрактных конструкций, позволяющих разработчикам сосредоточиться на программировании одной
лишь логики, не заботясь о машинном коде. Изучив язык С, можно легко
переходить от одного семейства микроконтроллеров к другому, при этом
затрачивается гораздо меньше времени на разработку программных кодов.
В последние 10–15 лет язык С приобрел огромную популярность во
всем мире и оказал значительное влияние на многие другие языки программирования. Именно язык С является предшественником таких языков, как C++, Objective-C, C#, Java. В ходе программной эволюции за
языком С закрепился статус безусловного лидера в двух направлениях:
API-программирование и перенацеливаемые компиляторы.
В микроконтроллерах (МК) PIC разработка любой программы может
быть осуществлена с помощью языков низкого уровня – ассемблера или высокого уровня – С (читается как Си). Для МК старшего семейства – PIC18,
имеющих сложную архитектуру и большое количество различных интерфейсов (SPI, I2C, USART, PSP, ADC, CAN 2.0, USB 2.0, TCP/IP и др.) и банков оперативной памяти (ОЗУ), язык С – самое лучшее решение, поскольку
он создан для поддержки программной сложности и облегчения отладки.
Основные достоинства языка С:
• широкий алфавит языка, что делает его выразительным и существенно повышает наглядность и понятность текста программы;
• конструкции команд (операторов), которые отражают содержательные виды обработки данных и задаются в удобном для человека виде;
3
• низкий объем основного текста программы, так как на языке высокого
уровня одна строка эквивалентна нескольким строкам ассемблерного текста;
• высокая переносимость (портативность) программ на другие аппаратные платформы, при этом программы становятся относительно независимыми от аппаратной части;
• многочисленные библиотеки стандартных функций (математические
библиотеки, библиотеки периферийных интерфейсов и др.), которые
можно повторно использовать во многих проектах;
• автоматическое переключение между банками памяти, что упрощает
написание программы, поскольку не требуется программно прописывать
выбор банка во время написания программного кода (в микроконтроллере
PIC18F452 16 банков объемом по 256 байт).
Основные недостатки языка С:
• необходимость использования компилятора, который применяется
совместно с языком С, при этом их общая стоимость намного выше ассемблера;
• больший, в 1,25–2,5 раза, программный код, полученный после компиляции;
• при различных вариантах использования операторов и команд программного кода возникают значительные расхождения затраченного времени на выполнение одних и тех же операторов и команд, что делает невозможным написание программ со строгими временными интервалами [1–4].
Важно отметить, что последний недостаток может быть исключен посредством ассемблерных вставок, которые могут включаться в любую
часть программы, написанной на языке С. Для этого необходимо ассемблерный код расположить между специальными идентификаторами _asm
(начало ассемблерной вставки) и _endasm (конец ассемблерной вставки).
Использование ассемблерных вставок актуально в тех случаях, когда
имеются особенные требования к временным интервалам выполнения
программы, поскольку в ассемблере на исполнение команд отводится
строго установленное время, которое от различных вариантов применения
команд не изменяется [5].
Компилятор – это программа, которая преобразует операции, операторы и другие конструкции языка высокого уровня, образующие исходный
текст программы в исполняемый бинарный код микроконтроллера, а процесс преобразования называется компиляцией. Большинство компиляторов формируют из исходного файла вначале программный код на языке
низкого уровня, а затем преобразуют его в машинный код, в результате
этого достигается большая гибкость. Пример поэтапного выполнения ком4
пиляции – исходный код, написанный на языке высокого уровня С, который преобразуется в ассемблерный код низкоуровневого языка (рис. 1, а), а
затем конвертируется в машинный (бинарный) код (рис. 1, б) [1–4].
а
while (n > 0)
{
sum = sum + n;
--n;
}
Компиляция
L28 movf
btfsc
goto
movf
addwf
btfsc
incf
decf
goto
L41
_n, f
STATUS, Z
L41
_n, f
_sum, f
STATUS, C
_sum + 1, f
_n, f
L28
б
0000100010010011
0001100100000011
0010100000001111
0000100000010011
0000100000010011
0000011110010100
0001100000000011
0000101010010101
0111100000000111
Ассемблирование
Рис. 1. Преобразование исходного кода, написанного на языке
высокого уровня, в машинный код: а – компиляция в ассемблерный код;
б – ассемблирование и компоновка в машинный код
Вниманию студентов! В настоящем издании приведены не все возможности и особенности языка высокого уровня С.
5
1. ВВОДНЫЕ ПОНЯТИЯ
1.1. Комментарии, идентификаторы и ключевые слова
Комментарий – это некоторый поясняющий текст, который не учитывается при компиляции. Комментарии бывают двух типов:
1) многострочные (начинаются с символов «/*» и заканчиваются символами «*/»);
2) однострочные (начинаются с символов «//» и продолжаются до конца текущей строки, не требуя при этом никакого завершающего символа).
Например:
• /* Многострочный комментарий часто размещают в начале файла, где
он содержит: имя автора, дату последнего редактирования кода, описание
программы и т. д. */
• #include <delays.h> // Подключаем библиотеку delays.h
Идентификатор – это последовательность букв, цифр и символов
подчеркивания «_», используемая для именования различных программных элементов: переменных, констант, функций, типов и т. д.
Следует запомнить, что идентификатор никогда не должен начинаться
с цифры.
Ключевое слово – это зарезервированное слово чётко определенного
значения, которое не может использоваться в качестве идентификаторов.
Ключевые слова: asm, auto, bit, bool, break, case, const, continue, default, defined, do, double, eeprom, else, enum, explicit, extern, false, flash, float, for,
goto, if, inline, int, long, register, return, rom, short, signed, sizeof, static, struct,
switch, true, typedef, union, unsigned, void, volatile, while.
1.2. Литералы и операторы
Литерал – постоянное значение некоторого типа, используемое в выражениях.
Примеры числовых литералов:
• 12 – число 12 в десятичной форме;
• 0хС – число 12 в шестнадцатеричной форме (префикс 0х);
• 0b1100 – число 12 в двоичной форме (префикс 0b);
• 014 – число 12 в восьмеричной форме (префикс 0);
• 12.5 – число с плавающей точкой;
• 125е-1 – число 12,5 в экспоненциальной форме.
6
Символьные литералы заключаются в одинарные кавычки, например: 'G',
'z', '<', '+' и т. д. Любой символ можно представить с помощью литерала по
его ASCII-коду, например литерал 'G' равнозначен 0х47, 'z' = 0х7А и т. д.
Оператор – это символ, указывающий компилятору, какие действия
необходимо выполнить над операндами (+, –, *, /, % и т. д.)
Операнд – это элемент данных (константа, литерал, идентификатор),
над которым выполняется операция.
Операторы, соединяющие операнды, представляют собой выражения,
которые могут быть заключены в круглые скобки, они отделяются друг от
друга символом – точки с запятой «;». Ооператоры, объединенные по некоторому признаку последовательности выражений, заключаются в фигурные
скобки – «{ }»; это могут быть границы функций, блоки выражений в циклических и условных конструкциях.
Значение выражения зависит от расположения знаков операций и
круглых скобок в выражении, а также от приоритета выполнения операций. Некоторые символы могут трактоваться по-разному в зависимости от
контекста. Например, знак «–» может использоваться для изменения знака
числа или в качестве оператора вычитания. Операции, используемые в
языке С, приведены в прил. 1.
Компилятор, выполняя инструкции, жёстко использует порядок приоритетов операций. Если в выражении содержится несколько операторов с
одинаковым приоритетом, тогда эти операторы выполняются по очереди,
слева направо или справа налево (см. прил. 1).
Например:
summa = summa + 1;
// Увеличение переменной summa на 1
Оператор сложения (+) имеет приоритет, равный 4, а оператор присваивания (=) – 14. Следовательно, в начале выполняется оператор сложения,
а затем присваивания. В результате переменная summa будет увеличена на 1
[1, 5, 6].
ВОПРОСЫ ДЛЯ САМОКОНТРОЛЯ
1. Что называют комментарием, а что идентификатором в синтаксисе
языка высокого уровня С?
2. Какое из приведенных ниже слов не зарезервировано в базе ключевых слов языка высокого уровня С: auto, void, volatile, defined, else, array,
while, struct, inline, return, rom?
3. Что называют числовым литералом? Приведите примеры.
4. Определите, используя таблицу ASCII-кодов, зашифрованное слово
в шестнадцатеричном коде – 0х57, 0х6F, 0х72, 0х6C, 0х64.
5. Что называют оператором и операндом в синтаксисе языка высокого
уровня С?
7
Рекомендуемая литература
1. Уилмсхерст, Т. Разработка встроенных систем с помощью микроконтроллеров PIC: принципы и практические примеры / Т. Уилмсхерст ;
пер. с англ. – СПб. : КОРОНА-ВЕК, 2008. – С. 392–394.
2. Шпак, Ю.А. Программирование на языке С для AVR и PIC микроконтроллеров / Ю.А. Шпак. – 2-е изд., перераб. и доп. – СПб. : КОРОНАВЕК, 2011. – С. 303–304.
2. СТРУКТУРА ПРОГРАММЫ НА ЯЗЫКЕ С
Программы на языке С начинаются с директив препроцессора (символ «#»), которые не являются конструкциями языка и обрабатываются до
фактической компиляции программы. Затем в программном коде объявляются глобальные переменные и далее функции.
Например:
// Директивы препроцессора
#include <p18f452.h>
#include <delays.h>
#define LED PORTD
#pragma config BOR = OFF
// Объявление глобальных типов, переменных и констант
...
// Функции
Function1
{
// Начало функции Function1
// Объявление локальных типов, переменных и констант
...
// Операторы
...
}
// Конец функции Function1
...
FunctionN
{
// Начало функции FunctionN
// Объявление локальных типов, переменных и констант
8
...
// Операторы
...
}
// Конец функции FunctionN
// Главная функция программы
void main (void)
{
// Начало функции main
// Объявление локальных типов, переменных и констант
...
// Операторы
...
}
// Конец функции main
Рассмотрим подробно приведенный выше пример.
Препроцессор – это особая программа, которая выполняет предварительную обработку данных и подставляет некоторый код в программу до
начала компиляции.
Директива #include объявляет стандартные библиотеки, поставляемые
вместе с компилятором. Формат директивы: #include <имя_файла>. В приведенном примере имеется две директивы #include:
1) #include <p18f452.h> – объявление библиотеки, содержащей описание и объявление всех специальных функциональных регистров микроконтроллера PIC18F452;
2) #include <delays.h> – объявление библиотеки, включающей в себя
функции временных задержек.
Для языка С написано множество различных библиотек (математических – для работы со строками и случайными числами, а также для работы
с памятью и символами и т. д.), которые могут использоваться при написании программного кода. Библиотека основных стандартных математических функций приведена в прил. 2.
Директива #define указывает препроцессору на необходимость выполнить подстановку в тексте программы определенной последовательности
символов другой последовательностью, что позволяет увеличить гибкость
и портативность программного кода. Формат директивы: #define заменяемая_последовательность фрагмент_подстановки. В рассматриваемом
примере директива #define выполняет подстановку элемента PORTD вместо LED.
9
Директива #pragma config активизируют специальные функции компилятора, позволяющие выполнить настройку программируемого микроконтроллера. В приведенном примере конструкция #pragma config BOR = OFF –
отключает сброс МК при снижении напряжения питания.
Функции на языке С представляют собой «контейнеры», в которых
выполняются некоторые фрагменты программного кода. Использование
функций облегчает написание и отладку программ, поскольку в них удобно размещать повторяющиеся группы операторов. Первая строка в функции является ее заголовком и имеет общий формат:
Тип_возвращаемого_результата Имя_функции (Аргумент(ы)_тип(ы))
В начале функции указывается тип возвращаемого результата, который
должен быть только один для данной функции. В главной функции main,
приведенной в примере, используется зарезервированное слово void, указывающее, что возвращаемый результат отсутствует. После имени функции в круглых скобках записываются один или несколько типов данных,
определяющих аргументы, которые должны передаваться в функцию.
В рассматриваемом варианте передаваемых аргументов в функцию main
нет, на это указывает зарезервированное слово void. В микроконтроллерах
конструкция главной функции void main (void) является общепринятой,
поскольку она в принципе не может ничего передавать или возвращать.
Как используются и применяются функции подробно будет рассмотрено в
разделе 6 (см. пример 6.5) [1, 5, 6].
! Внимание! Выполнение программы начинается всегда с главной
функции main, независимо от того, в каком месте программы она находится.
ВОПРОСЫ ДЛЯ САМОКОНТРОЛЯ
1. Для чего предназначен препроцессор?
2. Каково назначение директивы #include в языке высокого уровня С?
3. Каково назначение директивы #define в языке высокого уровня С?
4. Каково назначение директивы #pragma config в языке высокого
уровня С?
5. Какой формат имеет заголовок функций в синтаксисе языка высокого уровня С?
6. С какой функции начинается выполнение программного кода, написанного на языке высокого уровня С?
10
Рекомендуемая литература
1. Уилмсхерст, Т. Разработка встроенных систем с помощью микроконтроллеров PIC: принципы и практические примеры / Т. Уилмсхерст ;
пер. с англ. – СПб. : КОРОНА-ВЕК, 2008. – С. 395–396, 415–416.
2. Шпак, Ю.А. Программирование на языке С для AVR и PIC микроконтроллеров / Ю.А. Шпак. – 2-е изд., перераб. и доп. – СПб. : КОРОНАВЕК, 2011. – С. 304–306, 312–315, 329–331.
3. ТИПЫ ДАННЫХ, ПЕРЕМЕННЫЕ, КОНСТАНТЫ
Тип данных в языке С определяет диапазон допустимых значений и
пространство, отводимое в памяти данных, для переменных, констант и
результатов, возвращаемых функциями. Ключевые слова для определения
основных типов данных представлены в табл. 3.1. Типы данных, используемые в языке С, с диапазоном значений и размером области памяти, выделяемой под переменные, приведены в прил. 3.
Таблица 3.1
Ключевые слова для определения основных типов данных
Слово
Обобщенное описание
Слово
Обобщенное описание
Расширенное целое значение.
char
long Если используется отдельно, то
подразумевается целое число
Короткое целое значение. Если
Немодифицируемые
const
short используется отдельно, то подданные
разумевается целое число
Квалификатор, применяемый
Число с плавающей запясо словами char или int (кваdouble
signed
той двойной точности
лификатор по умолчанию для
этих слов)
Число с плавающей запяКвалификатор, применяемый
float
unsigned
той одинарной точности
со словами char или int
int Целое число
void Отсутствие значения или типа
Отдельный символ
(обычно 8 бит)
Переменная – это именованная величина определенного типа, которая
может изменяться в ходе выполнения программы. Для объявления переменных (т. е. выделения для них памяти) в программе на языке С используется следующий формат:
11
[Спецификатор_класса_памяти] Спецификатор_типа
Описатель [= Инициатор] [, Описатель [= Инициатор] ] ...
«Спецификатор_типа» – одно или несколько ключевых слов, определяющих тип объявляемой переменной (см. прил. 3).
«Описатель» – идентификатор простой переменной либо более сложная конструкция с квадратными скобками, круглыми скобками или звездочкой (набором звездочек).
«Инициатор» – задает начальное значение или список начальных значений, которое (которые) присваивается (присваиваются) переменной при
объявлении.
«Спецификатор_класса_памяти» – определяется одним из четырех ключевых
слов языка С: auto, extern, register, static и указывает, каким образом будет распределяться память под объявляемую переменную, а также определяет видимость этой
переменной, т. е. из каких частей программы можно к ней обратиться.
Описание ключевых слов, имеющих отношение к классу хранения данных:
• auto – переменная существует только внутри блока, в котором она объявлена;
• extern – объявляет данные, определенные где-то в другом месте;
• register – переменная должна сохраняться в регистре центрального процессора;
• static – объявляет переменную, существующую на протяжении всего выполнения программы. Размещение ее объявления влияет на то, в какой части
программы к ней можно обращаться.
! Внимание! В случае если спецификатор класса памяти не указан, то по
умолчанию используется класс auto.
Константа – это именованная величина определенного типа, имеющая
конкретное значение, заданное в момент объявления, которое не может изменяться в ходе выполнения программы. Для объявления констант в программе на
языке С к «Спецификатору_типа» необходимо добавить ключевое слово const.
Объекты с типом const представляют собой типы данных, используемые только
для чтения.
Примеры записи переменных и констант:
• char с1, с2;
// Объявление символьных переменных с1 и с2
(«Спецификатор_класса_памяти» – auto; «Спецификатор_типа» – char;
«Описатель» – с1 и с2);
• int zero = 200;
/* Объявление целочисленной переменной zero с присвоенным значением,
равным 200 */
12
(«Спецификатор_класса_памяти» – auto; «Спецификатор_типа» – int; «Описатель» – zero; «Инициатор» – 200);
• extern unsigned short number1 = 19;
/* Объявление целочисленной переменной number1 с присвоенным значением, равным 19 */
(«Спецификатор_класса_памяти» – extern; «Спецификатор_типа» – unsigned
short; «Описатель» – number1, «Инициатор» – 19);
• extern const float pi = 3.14159;
// Объявление вещественной переменной pi со значением, равным 3,14159
(«Спецификатор_класса_памяти» – extern; «Спецификатор_типа» – const
float; «Описатель» – pi; «Инициатор» – 3,14159) [1, 5, 6].
ВОПРОСЫ ДЛЯ САМОКОНТРОЛЯ
1. Что называют переменной, а также константой в синтаксисе языка высокого уровня С?
2. Какой формат объявления переменных используется в синтаксисе языка
высокого уровня С?
3. Каково назначение ключевых слов: auto, extern, register, static при объявлении переменных в программном коде языка высокого уровня С?
4. Назовите основное отличие типов данных, определённых ключевыми
словами char и float в языке высокого уровня С?
5. Назовите диапазон значений типа данных unsigned short long языка высокого уровня С?
Рекомендуемая литература
1. Уилмсхерст, Т. Разработка встроенных систем с помощью микроконтроллеров PIC: принципы и практические примеры / Т. Уилмсхерст ;
пер. с англ. – СПб. : КОРОНА-ВЕК, 2008. – С. 540.
2. Шпак, Ю.А. Программирование на языке С для AVR и PIC микроконтроллеров / Ю.А. Шпак. – 2-е изд., перераб. и доп. – СПб. : КОРОНАВЕК, 2011. – С. 306–312.
4. ОСНОВНЫЕ ОПЕРАТОРЫ ЯЗЫКА С
Все операторы языка С условно разделяются на следующие 4 категории:
1) условные операторы, к которым относятся операторы условия if и
выбора switch (if-else, switch-case);
2) операторы цикла (for, while, do-while);
3) операторы перехода (break, continue, goto, return);
13
4) другие операторы (оператор «выражение», пустой оператор, составной оператор).
Любой оператор в программе может быть помечен меткой, состоящей
из имени и следующего за ним двоеточия.
4.1. Оператор «выражение»
Выполнение оператора «выражение» заключается в вычислении выражения. Важно отметить, что вызывать функцию, невозвращающую значения, можно только при помощи оператора «выражение».
Например:
• ++k1; // оператор представляет выражение, которое увеличивает
// значение переменной k1 на единицу
• с = cos(v * 5);
// оператор представляет выражение, включающее в
// себя операции присваивания и вызова функции
4.2. Пустой оператор
Пустой оператор состоит только из точки с запятой и при его выполнении ничего не происходит. Он обычно используется в операторах: do,
for, while, if в строках, когда место оператора не требуется, но по синтаксису требуется хотя бы один оператор, а также при необходимости пометить фигурную скобку.
Например:
void main (void)
{
:
{ if (...) goto w; // Переход на скобку
{ ...
}
w:; }
}
4.3. Составной оператор
Составной оператор представляет собой несколько операторов и объявлений, заключенных в фигурные скобки. Используется следующий
формат записи:
14
{ [объявление]
:
оператор; [оператор];
:
{
Выполнение составного оператора заключается в последовательном
выполнении составляющих его операторов.
Например:
void main (void)
{
char sum, rett;
float saw, cont;
:
if (...)
{
int one, two;
short five, cont;
:
}
:
}
Переменные one, two, five, cont после выполнения составного оператора будут уничтожены. Важно отметить, что переменная short cont является
локальной в составном операторе и никак не связана с переменной float
cont, объявленной в начале главной функции main.
4.4. Оператор if-else
Оператор if-else позволяет в зависимости от некоторого логического
значения выполнять различные части программного кода. Формат записи
оператора if-else:
if (условное_выражение) блок_кода_1; [else блок_кода_2;]
Выполнение оператора if-else начинается с вычисления «условного_выражения», а далее продолжается по следующему алгоритму:
• если «условное_выражение» истинно (отлично от 0), исполняется
«блок_кода_1»;
15
• если «условное_выражение» ложно (равно 0), исполняется
«блок_кода_2»;
• если «условное_выражение» ложно и отсутствует «блок_кода_2» (в
квадратных скобках заключена необязательная конструкция), то управление передается на следующий оператор после if-else.
Например:
if (gor > ver) summa++;
else
{
gor = gor – 5;
summa = 0;
}
В случае, если переменная gor больше ver, переменная summa увеличивается на 1. В противном случае переменная gor уменьшается на 5 единиц, а переменной summa присваивается нулевое значение. Важно помнить, что в случае равенства двух переменных gor и ver условное выражение считается ложным, поскольку стоит жесткое условие больше (>).
Допускается использование вложенных операторов if-else, которые могут быть включены в конструкцию if или else другого оператора if.
Например:
void main (void)
{
char rett = 2, sum = 3, zew = 5;
if (sum > rett)
{
if (rett < zew)
sum = zew;
if (rett == zew) rett = sum;
}
else rett = zew;
}
По окончании выполнения программы переменная sum станет равна 5,
а переменные rett и zew останутся неизменными (rett = 2, zew = 5).
! Внимание! В случае, если фигурные скобки операторов if-else
«опущены», то компилятор связывает каждое ключевое слово else с
наиболее близким if, у которого отсутствует else. В приведенном примере,
если удалить фигурные скобки у оператора if (sum > rett), то ключевое
слово else станет относиться к оператору if (rett = zew).
16
Оператор if-else обладает возможностью использовать несколько
«условных_выражений», соединенных между собой логическими условиями: && – логическое «И», || – логическое «ИЛИ».
Например:
void main (void)
{
char rett = 2, sum = 3, zew = 5;
if (sum > rett && zew > 0 || zew < rett) sum++;
else rett = zew;
}
В целях упрощения анализа «условного_выражения» в приведенном
примере его следует разделить на две части: sum > rett – первая часть и
zew > 0 || zew < rett – вторая часть. При этом «условное_выражение» будет отлично от 0 только в том случае, если обе части окажутся истинными, поскольку между ними стоит логическое условие – &&. Вторая часть
«условного_выражения» примет отличное от 0 значение, если одно из
условий zew > 0 или zew < rett будет истинным. В итоге выполнения
«условного выражения» оператора переменная sum станет равна 4 (sum++),
а переменные rett и zew останутся неизменны (rett = 2, zew = 5).
Последовательность выполнения операторов программы if-else может
быть принудительно прервана одним из операторов перехода: break,
continue, return, goto [1, 5, 6].
4.5. Оператор switch-case
Оператор switch-case позволяет с помощью некоторой переменной выбирать одно из выражений, соответствующее заданной константе. Формат записи оператора switch-case следующий:
switch ( выражение )
{ [объявление]
:
[case константное_выражение_1] : [блок_кода_1]
[case константное_выражение_2] : [блок_кода_2]
:
[default : [блок_кода]]
}
Алгоритм выполнения оператора switch-case:
• вычисляется «выражение» после ключевого слова switch в круглых
скобках;
17
• полученное значение последовательно сравнивается с «константными_выражениями», следующими за ключевыми словами case;
• в случае совпадения одного из «константных_выражений» со значением полученного «выражения» управление передается на «блок_кода»,
помеченный соответствующим ключевым словом case;
• если ни одно из «константных_выражений» не равно полученному
«выражению», то управление передается на «блок_кода», помеченный
ключевым словом default, а в случае его отсутствия управление передается
на следующий оператор после switch.
Выражение, следующее за ключевым словом switch, должно иметь целое значение.
В операторе switch возможно использование локальных переменных,
объявление которых находится перед первым ключевым словом case, при
этом в объявлениях не должна использоваться инициализация.
«Константные_выражения» не должны содержать переменные или вызовы функций, поскольку они вычисляются во время трансляции. Как
правило, в качестве «константных_выражений» используются целые или
символьные константы. Все «константные_выражения» в операторе switch
должны быть уникальны.
После выполнения «блок_кода», соответствующего выбранному варианту case, выполняются последующие «блок_коды» операторов case, а
также оператор default, содержащиеся в операторе switch. Если требуется
прервать оператор switch сразу после исполнения «блок_кода», то применяется оператор выхода break.
Например:
void main (void)
{
int kok = 3;
switch (kok)
{
case 1: kok += 2;
case 3: kok *= 8;
case 0: kok /= 2;
case 4: kok –= 2; break;
case 6: kok *= 2; break;
default :
;
}
}
18
В приведенном примере, выполнение оператора switch начинается с
«блок_кода», помеченного ключевым словом case 3, по окончании переменной kok присваивается значение, равное 24. Затем выполняются последующие операторы, помеченные ключевыми словами case 0 и case 4. В итоге
переменная kok = 10 (kok = 24/2 = 12, kok = 12–2 = 10). За оператором case 4
следует оператор выхода break, завершающий выполнение оператора switch.
В конечном итоге переменная kok остается без изменения (kok = 10).
Допускается использование вложенных операторов switch-case, при
этом в ключевых словах case возможно использование одинаковых «константных_выражений».
Например:
void main (void)
{
int turt = 4, kuk = 3;
switch (turt)
{
case 0: turt = 2; break;
case 1: turt += 8; break;
case 4: switch (kuk)
{
case 2: turt *= 2; break;
case 3: turt /= 2; break;
}
case 2: turt *= 5;
case 6: turt /= 2; break;
case 5: turt += 7; break;
default :
;
}
}
В конечном итоге выполнения операторов переменная kuk остается без
изменения (kuk = 3), а переменной turn присваивается значение, равное 5
(turn = 4 /2 «case 3» = 2*5 «case 2» = 10 /2 «case 6» = 5) [1, 5, 6].
4.6. Оператор for
Оператор for применим при наиболее общем способе организации
цикла. Формат записи оператора for:
19
for (выражение_1; условное_выражение; выражение_2)
{
// Тело цикла
}
Алгоритм выполнения оператора for:
1) вычисляется «выражение_1», которое выполняется только один раз
при входе в цикл, оно обычно представляет собой оператор присваивания
некоторого начального значения счетчику цикла;
2) вычисляется «условное_выражение», определяющее момент выхода
из цикла;
3) если «условное_выражение» ложно, то управление передается на
следующий оператор после for;
4) если «условное_выражение» истинно, то выполняется тело цикла,
а затем вычисляется «выражение_2», далее процесс повторяется с пункта 2
(см. выше).
Например:
void main (void)
{
unsigned char summa = 0, x = 0;
for (x = 1; x <= 10; x++)
{
summa = summa + x;
}
}
В приведенном примере вычисляется сумма чисел от 1 до 10. В результате
выполнения цикла переменная summa принимает значение, равное 55.
Допускается возможность использования нескольких переменных,
управляющих циклом.
Например:
void main (void)
{
unsigned char summa_chet = 0, summa_no_chet = 0, x, y;
for (x = 2, y = 1; x <= 10 || y <= 9; x = x + 2, y = y + 2)
{
summa_chet = summa_chet + x;
summa_no_chet = summa_no_chet + y;
}
}
20
В приведенном примере вычисляется сумма четных и нечетных чисел
от 1 до 10, при этом в переменной summa_chet сохраняется результат суммы четных чисел, а в переменной summa_no_chet – нечетных. В качестве
«условного_выражения» используется два условия (x <= 10, y <= 9), между которыми стоит знак логического «ИЛИ». На этом основании оператор
for будет выполняться до тех пор, пока оба эти условия не станут ложными. В результате выполнения цикла переменная summa_chet станет равна 30, а переменная summa_no_chet – 25 [1, 5, 6].
С помощью оператора for может быть создан бесконечный цикл, выход
из которого осуществляется посредством дополнительного условия и оператора break.
Например:
void main (void)
{
char mut;
for ( ; ; )
{
...
// Тело бесконечного цикла
if (mut == 6) break;
...
}
}
4.7. Оператор while
Оператор while является оператором цикла с предусловием и имеет
следующий формат записи:
while ( условное_выражение )
{
// Тело цикла
}
Алгоритм выполнения оператора while:
1) вычисляется «условное_выражение»;
2) если «условное_выражение» ложно, то выполнение оператора while заканчивается и управление передается на следующий оператор после while;
3) если «условное_выражение» истинно, то выполняется тело цикла,
затем процесс повторяется (см. пункт 1–3).
21
Например:
void main (void)
{
unsigned char summa = 0, x;
x = 1;
while (x <= 10)
{
summa = summa + x;
x++;
}
}
В приведенном примере решается аналогичная задача, как и в случае с
оператором for, где вычисляются суммы чисел от 1 до 10. В конечном итоге
выполнения цикла переменная summa принимает значение, равное 55 [1, 5, 6].
С помощью оператора while может быть создан бесконечный цикл, выход из которого осуществляется посредством дополнительного условия и
оператора break.
Например:
void main (void)
{
char sam;
while ( 1 )
{
...
// Тело бесконечного цикла
if (sam == 3) break;
...
}
}
! Внимание! При выполнении операторов for и while проверка условия всегда выполняется вначале цикла, поэтому тело цикла может ни разу
не выполниться, если условие изначально будет ложным.
4.8. Оператор do-while
Оператор do-while является оператором цикла с постусловием и используется в тех случаях, когда необходимо выполнить тело цикла, как
минимум, один раз, вне зависимости от условия цикла. Формат записи
оператора do-while:
22
do
{
... // Тело цикла
}
while ( условное_выражение );
Алгоритм выполнения оператора do-while:
1) выполняется «тело цикла» (которое может быть составным оператором);
2) вычисляется «условное_выражение»;
3) если «условное_выражение» ложно, то выполнение оператора dowhile заканчивается и управление передается на следующий по порядку
оператор после do-while;
4) если «условное_выражение» истинно, то выполнение оператора
продолжается с пункта 1.
Например:
void main (void)
{
...
char ser = 0, fyt = 2;
do
{
ser = fyt + 3;
fyt++;
}
while ( fyt < 30);
}
Внимание! Тело цикла операторов for, while и do-while будет выполнено полностью, даже в том случае, если «условное_выражение» изменилось на ложное в середине цикла.
!
4.9. Операторы break и continue
Оператор break завершает выполнение самого внутреннего из объединяющих его операторов switch, do, for, while и передает управление оператору, следующему за прерванным. Во вложенных операторах выход осуществляется не на самый верхний уровень вложенности, а лишь на одну
ступень вверх.
23
При выполнении оператора continue все находящиеся после него операторы блока пропускаются, а управление передается в начало цикла для
следующей итерации. Операторы continue применяются преимущественно
в сложных циклах, требующих принятия решений на основании многих
факторов [1, 5, 6].
4.10. Оператор goto
Оператор goto служит для осуществления безусловного перехода внутри функции, но при этом он не может переходить в другую функцию.
Формат записи оператора goto:
goto идентификатор;
...
идентификатор: блок_кода;
Оператор goto передает управление на оператор, помеченный меткой
«идентификатор», который должен быть уникальным и не может быть использован для других операторов программы.
При использовании оператора goto возможна передача управления
внутрь составного оператора. Но при этом необходимо учитывать, что при
входе в составной оператор, содержащий объявления переменных с инициализацией, значения последних будут не определены, поскольку объявления располагаются перед выполненными операторами.
Важно заметить, что при программировании на языке С использование
оператора безусловного перехода goto не рекомендуется, так как он затрудняет понимание программ и возможность их модификаций [1, 5, 6].
4.11. Оператор return
Оператор return завершает выполнение функции, в которой он содержится, и возвращает управление в вызывающую функцию. Управление
передается в точку вызывающей функции, которая непосредственно следует за оператором вызова.
Формат записи оператора return:
...
return [( выражение )];
...
24
В операторе return значение «выражения», если оно задано (указывать
возвращаемое «выражение» не обязательно), возвращается в вызывающую функцию в качестве значения вызываемой функции.
Например:
void main (void)
{
unsigned char sum = 0;
sum = Summa (12, 4);
}
// Функция суммирования
unsigned char Summa (unsigned char x, unsigned char y)
{
return x + y;
}
В приведенном примере при помощи функции Summa осуществляется
суммирование двух переменных x и y, а результат передается в вызывающую функцию. В рассматриваемом случае – это main. В итоге переменная
sum принимает значение, равное 16 [1, 5, 6].
ВОПРОСЫ ДЛЯ САМОКОНТРОЛЯ
1. Приведите примеры операторов цикла и перехода.
2. Какой формат записи имеет оператор if-else в синтаксисе языка высокого уровня С?
3. Назовите алгоритм выполнения оператора if-else.
4. Какой формат записи имеет оператор switch-case в синтаксисе языка
высокого уровня С?
5. Назовите алгоритм выполнения оператора switch-case.
6. Объясните назначение оператора default в операторе switch-case в
языке высокого уровня С.
7. Назовите основные отличия условных операторов if-else и switchcase в языке высокого уровня С?
8. Какой формат записи имеет оператор for в синтаксисе языка высокого уровня С?
9. Назовите алгоритм выполнения оператора for.
10. Какой формат записи имеет оператор while в синтаксисе языка высокого уровня С?
11. Назовите алгоритм выполнения оператора while.
25
12. Какой формат записи имеет оператор do-while в синтаксисе языка
высокого уровня С?
13. Назовите алгоритм выполнения оператора do-while.
14. Объясните, для чего служат операторы break и continue в языке высокого уровня С?
15. Для чего предназначены операторы goto и return в языке высокого
уровня С?
16. Какой формат записи имеет оператор goto в синтаксисе языка высокого уровня С?
17. Назовите формат записи оператора return в синтаксисе языка высокого уровня С.
Рекомендуемая литература
1. Уилмсхерст, Т. Разработка встроенных систем с помощью микроконтроллеров PIC: принципы и практические примеры / Т. Уилмсхерст ;
пер. с англ. – СПб. : КОРОНА-ВЕК, 2008. – С. 406, 413–414, 418, 431, 435.
2. Шпак, Ю.А. Программирование на языке С для AVR и PIC микроконтроллеров / Ю.А. Шпак. – 2-е изд., перераб. и доп. – СПб. : КОРОНАВЕК, 2011. – С. 321–325.
5. УКАЗАТЕЛИ, МАССИВЫ И СТРОКИ
Указатель – это переменная, содержащая адрес некоторого элемента
данных (переменной, константы, функции, структуры). В языке С указатели тесно связаны с обработкой массивов и строк.
Для объявления переменной как указателя используется оператор (*),
например:
• int *p;
• float *kil
// «р» – указатель на целое число
// «kil» – указатель на вещественное число
Для присвоения адреса некоторой переменной указателю используется
оператор (&).
Например:
char *p;
// «р» – указатель на символ
char t; // объявлена символьная переменная «t»
t = 'Z';
// в символьную переменную «t» помещен символ «Z»
р = &t;
// «р» содержит адрес переменной «t» (указывает на 'Z')
26
Для того, чтобы извлечь значение переменной, на которую указывает
указатель, используется оператор косвенной адресации (*).
Например:
char *p;
// «р» – указатель на символ
char m, f;
// объявлены символьные переменные «m» и «f»
m = 'K'; // в символьную переменную «m» помещен символ «K»
р = &m;
// «р» содержит адрес переменной «m» (указывает на 'K')
f = *p;
// переменная «f = 'K'»
Аналогичным образом этот оператор (*) можно использовать и для
присвоения некоторого значения переменной, на которую указывает указатель.
Например:
char *p;
// «р» – указатель на символ
char m, f;
// объявлены символьные переменные «m» и «f»
f = 'G';
// символьная переменная «f» содержит символ «G»
р = &m;
// «р» содержит адрес переменной «m» (указывает на 'G')
*p = f;
// переменная «m = 'G'»
Рассмотрим приведенные примеры более детально, используя для
наглядности конкретные адреса ячеек памяти:
Пример извлечения значения переменной представлен на рис. 5.1, а:
char *p;
// «р» – указатель на символ. Символ «p» располагается по адресу 0х16
char m, f;
/* объявлены символьные переменные «m» и «f». Символ «m» располагается по адресу 0х13, а «f» – 0х15 (рис. 5.1, б) */
m = 'K'; // в символьную переменную «m» помещен символ «K»
р = &m;
// «р» содержит адрес переменной «m» (р = 0х13) (рис. 5.1, в)
f = *p;
/* в переменную «f» записывается значение (символ), находящееся в
ячейке памяти по адресу, содержащемуся в переменной «p». Переменная
«f = 'K'» (рис. 5.1, г) */
27
Адрес
0х12
0х13
0х14
Адрес
0х12
m ‒ 0х13
0х14
Память
данных
0х00
0х00
0х00
Память
данных
0х00
0х00
0х00
Адрес
0х15
0х16
0х17
Адрес
f ‒ 0х15
р ‒ 0х16
0х17
0х00
0х00
0х00
Память
данных
б
Память
данных
а
0х00
0х00
0х00
Адрес
0х12
m ‒ 0х13
0х14
Адрес
0х12
m ‒ 0х13
0х14
Память
данных
0х00
0х4B = ꞌKꞌ
0х00
Память
данных
0х00
0х4B = ꞌKꞌ
0х00
Адрес
f ‒ 0х15
р ‒ 0х16
0х17
Адрес
f ‒ 0х15
р ‒ 0х16
0х17
0х00
0х13
0х00
Память
данных
г
Память
данных
в
0х4B = ꞌKꞌ
0х13
0х00
Рис. 5.1. Чтение данных из ячеек памяти микроконтроллера: а – исходное состояние
ячеек памяти МК; б – объявлены символьные переменные «p», «m» и «f»;
в – запись данных в ячейки памяти МК «m – 0x13» и «p – 0x16»;
г – запись символа 'K' в переменную «f – 0x15»
Пример присвоения некоторого значения переменной представлен на
рис. 5.2, а:
char *p;
// «р» – указатель на символ. Символ «p» располагается по адресу 0хА4
char m, f;
/* объявлены символьные переменные «m» и «f». Символ «m» располагается по адресу 0хА1, а «f» – 0хА2 (рис. 5.2, б) */
f = 'G';
// в символьную переменную «f» помещен символ «G»
р = &m;
// «р» содержит адрес переменной «m» (р = 0хА1) (рис. 5.2, в)
*p = f;
/* значение (символ) переменной «f» записывается в ячейку памяти,
находящуюся по адресу, содержащемуся в переменной «p». Переменная
«m = 'G'» (рис. 5.2, г) */
28
Адрес
0хA0
0хA1
0хA2
Адрес
0хA0
m ‒ 0хA1
f ‒ 0хA2
Память
данных
0х00
0х00
0х00
Память
данных
0х00
0х00
0х00
Адрес
0хA3
0хA4
0хA5
Адрес
0хA3
р ‒ 0хA4
0хA5
0х00
0х00
0х00
Память
данных
б
Память
данных
а
0х00
0х00
0х00
Адрес
0хA0
m ‒ 0хA1
f ‒ 0хA2
Адрес
0хA0
m ‒ 0хA1
f ‒ 0хA2
Память
данных
0х00
0х00
0х47 = ꞌGꞌ
Память
данных
0х00
0х47 = ꞌGꞌ
0х47 = ꞌGꞌ
Адрес
0хA3
р ‒ 0хA4
0хA5
Адрес
0хA3
р ‒ 0хA4
0хA5
0х00
0хA1
0х00
Память
данных
г
Память
данных
в
0х00
0хA1
0х00
Рис. 5.2. Запись данных в ячейки памяти микроконтроллера: а – исходное состояние
ячеек памяти МК; б – объявлены символьные переменные «p», «m» и «f»;
в – запись данных в ячейки памяти МК «p – 0xA4» и «f – 0xA2»;
г – запись символа 'G' в переменную «m – 0xA1»
Массив – это тип данных, который используется для представления
последовательности однотипных значений. Массивы объявляются подобно обычным переменным, с указанием в квадратных скобках размерности
(количества элементов в массиве):
• int dig_number[10];
// Массив из десяти целочисленных переменных
// типа int
• char stroka[8];
// Массив из восьми символов
В подавляющем большинстве случаев рационально инициализировать
массив непосредственно при его объявлении:
• int dig_number[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
• char stroka[8] = {'T', 'h', 'e', ' ', 'L', 'i', 'n', 'e'};
Строка – это особый вид массива, состоящего из символов (тип char),
которые заканчиваются пустым символом «\0». Поэтому размер массива
строки должен быть на одну единицу (байт) больше. Представим вышеприведенный массив (stroka[8]) в виде строкового литерала:
• char stroka[9] = "The Line".
29
При инициализации массивов и строк размерность можно не указывать:
• int dig_number[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
• char stroka[] = "The Line".
Доступ к элементам массива и строк можно получить по индексу, начинающемуся с нуля. Наряду с этим важно отметить, что имя массива или строки устанавливается равным адресу первого элемента, поэтому при передаче
имени массива или строки в функцию фактически передается и этот адрес.
Например:
int dig_number[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
char stroka[] = "The Line";
int number;
char str;
number = dig_number[4];
// number = 14
str = stroka[4];
// str = L
/* в переменные number и str записываются пятые по счету элементы
массива dig_number[] и строки stroka[], поскольку нумерация в массивах и
строках начинается с нуля */
number = *dig_number + 6;
/* в переменную number записывается седьмой по счету элемент массива dig_number[], number = 16 */
str = *stroka;
/* str = T, потому что имя строки (массива) равно адресу первого элемента этой строки (массива) */
Массивы и строки по умолчанию размещаются в памяти микроконтроллера SRAM (Static RAM – оперативная память ОЗУ МК), размер которой в
зависимости от модели и семейства МК различен, но имеет в десятки и
сотни раз меньше объем по сравнению с основной памятью (памятью программ) – FLASH. Поэтому в программах массивы и строки больших объемов целесообразно сохранять во FLASH-памяти микроконтроллера, для
этого такие конструкции необходимо объявлять с использованием ключевого слова const [1, 5, 6].
Например:
• const int dig_number[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
• const char stroka[] = "The Line".
30
ВОПРОСЫ ДЛЯ САМОКОНТРОЛЯ
1. Дайте определения следующим понятиям: «указатель», «массив» и
«строка», применяемые в языке высокого уровня С.
2. Назовите основные отличия массива от строки в языке высокого
уровня С.
3. Для чего предназначены операторы (*) и (&) в синтаксисе языка высокого уровня С?
Рекомендуемая литература
1. Уилмсхерст, Т. Разработка встроенных систем с помощью микроконтроллеров PIC: принципы и практические примеры / Т. Уилмсхерст ;
пер. с англ. – СПб. : КОРОНА-ВЕК, 2008. – С. 432–435.
2. Шпак, Ю.А. Программирование на языке С для AVR и PIC микроконтроллеров / Ю.А. Шпак. – 2-е изд., перераб. и доп. – СПб. : КОРОНАВЕК, 2011. – С. 318–321.
6. ПРИМЕРЫ ПРОГРАММНЫХ КОДОВ
Важно заметить, что в приведенных примерах некоторые части программного кода написаны не самым рациональным способом. Это сделано
намеренно, с целью продемонстрировать различные и многогранные возможности языка С.
Пример 6.1
Задача. Требуется из цифрового ряда от 0 до 10 определить количество
нечетных чисел кратных трем и вычислить их общую сумму.
Для решения задачи в первую очередь необходимо составить алгоритм. Для этого воспользуемся графической формой записи алгоритма,
поскольку он обладает наибольшей наглядностью и определенностью. На
рис. 4 приведена блок-схема алгоритма, решающего поставленную задачу.
Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет следующие переменные: balance, counter_num, summa, score.
2. Блок модификации. Задает цикл со счетчиком, при этом начальное
значение счетчика (переменная score) равно 1, конечное 10, а шаг изменения
эквивалентен 2. По достижении счетчиком конечного значения 10 цикл и
алгоритм завершается.
31
Начало
balance, counter_num
summa, score
1
2
3
score = 1, 10, 2
balance = score % 3
Нет
4 balance = 0
Да
counter_num++
5 summa = summa + score
Конец
Рис. 6.1. Блок-схема алгоритма
1
3. Блок операций над данными.
Осуществляет присвоение переменной
balance остатка от деления переменной
score на 3.
4. Блок проверки условия. Выполняет
проверку на равенство нулю переменной
balance. В случае положительного результата проверки управление передается на блок операций над данными 5;
если исход проверки отрицателен, то
управление незамедлительно переходит
к блоку модификации 2 (рис. 6.1).
5. Блок операций над данными.
Производит увеличение переменной
counter_num на единицу, а переменной
summa на величину, содержащуюся в
переменной score. Затем управление передается на блок модификации 2.
На основании приведенного алгоритма (рис. 6.1) составляется программный
код на языке программирования С, где
части кода, выполняющие аналогичные
функции блоков графического алгоритма, отмечены подобными цифрами:
void main (void)
{
char balance;
char score;
char counter_num = 0;
char summa = 0;
2 – for (score = 1; score <= 10; score = score + 2)
{
3 –
4 –
balance = score % 3;
if (balance == 0)
// Остаток от деления
{
counter_num++;
summa = summa + score;
5
}
}
}
32
Ход выполнения программного кода со срезом значений переменных в
ключевых местах программы приведен в табл. 6.1.
Таблица 6.1
Детализация выполнения программного кода
Программный
1
код
Объявление переменных. Блок 1
char balance;
char score;
char counter_num = 0;
char summa = 0;
Полученный
Объявлены символьные переменные: balance, score,
результат
counter_num и summa, при этом двум последним присвоепосле выполны нулевые значения
нения кода
Цикл решения основной задачи. Блоки: 2, 3, 4, 5
2 – for (score = 1; score <= 10; score = score + 2)
{
3 – balance = score % 3;
4 – if (balance == 0)
Программный
{
код
counter_num++;
5
summa = summa + score;
}
}
Цикл 1
Блок 2 score = 1; 1 <= 10 (истина, выполняется цикл)
Блок 3 balance = 1%3 = 1;
Блок 4 1 == 0 (ложь, блок 5-го кода не выполняется)
Цикл 2
Выполняемые Блок 2 score = 3; 3 <= 10 (истина, выполняется цикл)
циклы прог- Блок 3 balance = 3%3 = 0;
раммного кода Блок 4 0 == 0 (истина, выполняется блок 5-го кода)
Блок 5 counter_num=1; summa=3;
Цикл 3
Блок 2 score = 5; 5 <= 10 (истина, выполняется цикл)
Блок 3 balance = 5%3 = 1;
Блок 4 1 == 0 (ложь, блок 5-го кода не выполняется)
33
Окончание табл. 6.1
Блок 2
Блок 3
Блок 4
Выполняемые
Блок 2
циклы программного кода Блок 3
Блок 4
Блок 5
Блок 2
Цикл 4
score = 7; 7 <= 10 (истина, выполняется цикл)
balance = 7%3 = 1;
1 == 0 (ложь, блок 5-го кода не выполняется)
Цикл 5
score = 9; 9 <= 10 (истина, выполняется цикл)
balance = 9%3 = 0;
0 == 0 (истина, выполняется блок 5-го кода)
counter_num = 2; summa = 3 + 9 = 12;
Цикл 6
score = 11; 11 <= 10 (ложь, завершение цикла и
программного кода в целом)
Ответ. В цифровом ряду от 0 до 10 имеется два нечетных числа кратных трем (counter_num=2), сумма которых равна 12 (summa=12).
Пример 6.2
Задача. Необходимо, используя операторы присвоения (&) и указания (*),
выполнить следующее:
• переместить символы из строки stroka[m] в строку stroka_new[m];
• копировать значения из массива dig_number[n] в массив
dig_number_new[n], расположив их в обратном порядке.
Блок-схема алгоритма, решающего поставленные задачи, приведена на
рис. 6.2. Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет переменные: counter_num, counter_str, строки: stroka[m], stroka_new[m] и массивы:
dig_number[n], dig_number_new[n].
2. Блок модификации (цикл 1). Задает цикл со счетчиком, где начальное
значение счетчика (переменная counter_str) равно 1, конечное m, а шаг изменения эквивалентен 1. В случае достижения счетчиком конечного значения (m)
управление передается на следующий блок модификации 5 (рис. 6.2).
3–4. Блоки операций над данными. Осуществляют присвоение переменной массива stroka_new[counter_str] значения, равного элементу массива
stroka[counter_str]. После этого stroka[counter_str] обнуляется, тем самым
решается первый пункт поставленной задачи.
5. Блок модификации (цикл 2). Задает цикл со счетчиком, при этом начальное значение счетчика (переменная counter_num) равно 1, конечное n, а шаг
изменения эквивалентен 1. В момент достижения счетчиком конечного значения (n) цикл, а также и алгоритм в целом свою работу заканчивают.
34
Начало
A
counter_num, counter_str
stroka[m], stroka_new[m]
dig_number[n]
dig_number_new[n]
1
2
5 counter_num = 1, n, 1
Цикл 2
dig_number_new[counter_num] =
6 = dig_number[n+1-counter_num]
counter_str = 1, m, 1
Цикл 1
stroka_new[counter_str] =
3 = stroka[counter_str]
Конец
stroka[counter_str] = 0
4
A
Рис. 6.2. Блок-схема алгоритма
6. Блок операции над данными. Производит присвоение переменной
массива dig_number_new[counter_num] значения, равного элементу массива dig_number[n-counter_num], выполняя тем самым второй пункт поставленной задачи.
На основании приведенного алгоритма составляется программный код на
языке программирования С. Части кода, выполняющие аналогичные функции
блоков графического алгоритма (рис. 6.2), отмечены подобными цифрами:
void main (void)
{
int counter_num = 0;
char counter_str = 0;
char stroka[6] = "Apple";
char stroka_new[6];
1
int dig_number[5] = {3, 5, 9, 13, 22};
int dig_number_new[5];
char *kuk = &stroka[0];
int *ret = &counter_num;
2 – for (counter_str = 0; stroka[counter_str] != 0; counter_str++)
{
35
3–
4–
stroka_new[counter_str] = *(kuk + counter_str);
stroka[counter_str] = 0;
}
5 – while (counter_num <= 4)
{
6–
dig_number_new[counter_num] = *(dig_number + 4 - counter_num);
5–
counter_num = *ret + 1;
}
}
Ход выполнения программного кода со срезом значений переменных в
ключевых местах программы приведен в табл. 6.2.
Таблица 6.2
Детализация выполнения программного кода
Объявление переменных, строк и массивов. Блок 1
int counter_num = 0;
char counter_str = 0;
char stroka[6] = "Apple";
char stroka_new[6];
Программный
1
код
int dig_number[5] = {3, 5, 9, 13, 22};
int dig_number_new[5];
char *kuk = &stroka[0];
int *ret = &counter_num;
Объявлены: целочисленные переменная counter_num и массивы dig_number[5], dig_number_new[5], а также символьная переменная counter_str и строки stroka[6], stroka_new[6].
Переменным counter_num и counter_str присвоены нулевые
Полученный значения, массив dig_number[5] инициализирован из чисел
результат по- {3, 5, 9, 13, 22}, а строка stroka[6] содержит слово "Apple".
сле выполне- Также объявлены переменные kuk и ret. При этом переменния кода
ная kuk указывает на принадлежность к символьному типу
данных и содержит адрес нулевого элемента строки stroka,
а переменная ret указывает на принадлежность к целочисленному типу данных и содержит адрес целочисленной переменной counter_num
36
Продолжение табл. 6.2
Цикл 1 – решения первой задачи. Блоки: 2, 3, 4
2
for (counter_str = 0; stroka[counter_str] != 0; counter_str++)
{
Программный
код
3 – stroka_new[counter_str] = *(kuk + counter_str);
4 – stroka[counter_str] = 0;
}
Цикл 1–1
Блок 2 counter_str = 0; 'A' ≠ 0 (истина, выполняется
цикл)
Блок 3 stroka_new[0] = 'A'; (stroka_new[6] = "A0000 \0")
Блок 4 stroka[0] = 0; (stroka[6] = "0pple \0")
Цикл 1–2
Блок 2 counter_str = 1; 'р' ≠ 0 (истина, выполняется
цикл)
Блок 3 stroka_new[1] = 'р'; (stroka_new[6] = "Ap000 \0")
Блок 4 stroka[1] = 0; (stroka[6] = "00ple \0")
Цикл 1–3
Блок 2 counter_str = 2; 'р' ≠ 0 (истина, выполняется
цикл)
Выполняемые
циклы прог- Блок 3 stroka_new[2] = 'р'; (stroka_new[6] = "App00 \0")
Блок 4 stroka[2] = 0; (stroka[6] = "000le \0")
раммного
кода
Цикл 1–4
Блок 2 counter_str = 3; 'l' ≠ 0 (истина, выполняется
цикл)
Блок 3 stroka_new[3] = 'l'; (stroka_new[6] = "Appl0 \0")
Блок 4 stroka[3] = 0; (stroka[6] = "0000e \0")
Цикл 1–5
Блок 2 counter_str = 4; 'е' ≠ 0 (истина, выполняется цикл)
Блок 3 stroka_new[4] = 'е'; (stroka_new[6] = "Apple \0")
Блок 4 stroka[4] = 0; (stroka[6] = "00000 \0")
Цикл 1-6
counter_str = 5; '\0' ≠ 0 (ложь, выполнение операБлок 2 тора прерывается, и управление передается на
следующий оператор за for «Блок 5»)
37
Окончание табл. 6.2
Цикл 2 – решение второй задачи. Блоки: 5, 6
5 – while (counter_num <= 4)
{
Программный
dig_number_new[counter_num] =
6
код
= *(dig_number + 4 - counter_num);
5 – counter_num = *ret + 1;
}
Цикл 2–1
Блок 5 counter_num = 0; 0 <= 4 (истина, выполняется цикл)
dig_number_new[0] = 22;
Блок 6
(dig_number_new[5] = {22,0,0,0,0})
Блок 5 counter_num = counter_num + 1 = 1;
Цикл 2–2
Блок 5 counter_num = 1; 1 <= 4 (истина, выполняется цикл)
dig_number_new[1] = 13;
Блок 6
(dig_number_new[5] = {22,13,0,0,0})
Блок 5 counter_num = counter_num + 1 = 2;
Цикл 2-3
Блок 5 counter_num = 2; 2 <= 4 (истина, выполняется цикл)
dig_number_new[2] = 9;
Выполняемые
Блок 6
(dig_number_new [5] = {22,13,9,0,0})
циклы программного Блок 5 counter_num = counter_num + 1 = 3;
кода
Цикл 2–4
Блок 5 counter_num = 3; 3 <= 4 (истина, выполняется цикл)
dig_number_new[3] = 5;
Блок 6
(dig_number_new [5] = {22,13,9,5,0})
Блок 5 counter_num = counter_num + 1 = 4;
Цикл 2-5
Блок 5 counter_num = 4; 4 <= 4 (истина, выполняется цикл)
dig_number_new[4] = 3;
Блок 6
(dig_number_new [5] = {22,13,9,5,3})
Блок 5 counter_num = counter_num + 1 = 5;
Цикл 2–6
counter_num = 5; 5 <= 4 (ложь, завершение цикла
Блок 5
и программного кода в целом)
38
После исполнения программного кода объявленные строки и массивы будут иметь следующие значения: stroka[6] = "00000", stroka_new[6] = "Apple",
dig_number[5] = {3, 5, 9, 13, 22}, dig_number_new [5] = {22, 13, 9, 5, 3}.
Из приведенных строк и массивов можно сделать вывод, что оба пункта
поставленной задачи выполнены.
Пример 6.3
Задача. Требуется расположить элементы массива dig_number[n] в порядке возрастания.
Блок-схема алгоритма, решающего поставленную задачу, приведена на
рис. 6. Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет переменные: array,
mas_number, i, j, bol и массив dig_number[n].
2. Блок модификации (цикл 1). Задает цикл со счетчиком, где начальное
значение счетчика (переменная i) равно 1, конечное n-1, а шаг изменения
эквивалентен 1. При достижении счетчиком конечного значения (n-1)
цикл, а также алгоритм в целом заканчивают выполняться.
3. Блок операций над данными. Присваивает переменной mas_number
значение элемента массива dig_number[n] (dig_number[i]), расположенного
по адресу, равному переменной i. Переменная bol приравнивается к i.
4. Блок модификации (цикл 2). Задает цикл со счетчиком, где начальное
значение счетчика (переменная j) равно i+1, конечное n, а шаг изменения
эквивалентен 1. В случае достижения счетчиком конечного значения (n)
управление передается на блок проверки условия 7 (см. рис. 6.2).
5. Блок проверки условия. Выполняет проверку на то, что величина, содержащаяся в переменной mas_number, больше по отношению к значению
элемента массива dig_number[n] (dig_number[j]), расположенного по адресу,
равному переменной j. При положительном результате проверки управление
передается на блок операций над данными 6; если исход проверки отрицателен, то управление вновь переходит к блоку модификации 4 (см. рис. 6.2).
6. Блок операций над данными. Присваивает переменной mas_number
значение элемента массива dig_number[n] (dig_number[j]), расположенного
по адресу, равному переменной j. Переменная bol приравнивается к j.
7. Блок проверки условия. Производит проверку на равенство переменной bol значению, содержащемуся в переменной i. В случае положительного результата проверки управление передается на блок модификации 2,
если исход проверки отрицателен, то управление переходит к блоку операций над данными 8.
39
Начало
dig_number[n]
1 mas_number, i, j, bol
B
C
Да
mas_number =
= dig_number[j]
bol = j
A
2
3
i = 1, n-1, 1
6
Цикл 1
Нет
5
7
j = i + 1, n, 1
Цикл 2
dig_number[j]
< mas_number
8
Да
B
A
E
mas_number =
= dig_number[i]
bol = i
Да
4
D
C
bol = i
Нет
dig_number[bol] =
= dig_number[i]
dig_number[i] =
= mas_number
D
Конец
E
Рис. 6.3. Блок-схема алгоритма
8. Блок операций над данными. Присваивает элементу массива
dig_number[n] (dig_number[bol]), расположенного по адресу, равному переменной bol, значение, содержащееся в массиве dig_number[n]
(dig_number[i]) по адресу, равному переменной i. А также приравнивает
элемент массива dig_number[n] (dig_number[i]), находящийся на позиции,
равной i, к переменной mas_number.
На основании приведенного алгоритма составляется программный код на
языке программирования С. Части кода, выполняющие аналогичные функции
блоков графического алгоритма (рис. 6.3), отмечены подобными цифрами:
void main (void)
{
int dig_number[5] = {32,51,11,7,3};
char array = 5;
1
int mas_number;
char i, j, bol;
40
2 – for (i = 0; i < array - 1; i++)
{
mas_number = *(dig_number + i);
3
bol = i;
4–
for (j = i + 1; j < array; j++)
{
5–
if (*(dig_number + j) < mas_number)
{
mas_number = *(dig_number + j);
6
bol = j;
}
}
7–
if (bol != i)
{
dig_number[bol] = *(dig_number + i);
8
*(dig_number + i) = mas_number;
}
}
}
Ход выполнения программного кода со срезом значений переменных в
ключевых местах программы приведен в табл. 6.3.
Таблица 6.3
Детализация выполнения программного кода
Объявление переменных и массива. Блок 1
int dig_number[5] = {32,51,11,7,3};
char array = 5;
Программ1
ный код
int mas_number;
char i, j, bol;
Объявлены: целочисленные переменная mas_number и
Полученный массив dig_number[5], а также символьные переменные
результат array, i, j и bol. Символьной переменной array присвоено
после выпол- значение, равное 5, так как массив dig_number[5] состоит
нения кода из пяти элементов. Массив dig_number[5] инициализирован из чисел {32,51,11,7,3}
41
Продолжение табл. 6.3
Циклы решения поставленной задачи. Блоки: 2, 3, 4, 5, 6, 7, 8
2 – for (i = 0; i < array - 1; i++)
{
mas_number = *(dig_number + i);
3
bol = i;
4 – for (j = i + 1; j < array; j++)
{
Програм- 5 – if (*(dig_number + j) < mas_number)
мный код
{ mas_number = *(dig_number + j);
6
bol = j; }
}
7 – if (bol != i)
{ dig_number[bol] = *(dig_number + i);
8
*(dig_number + i) = mas_number; }
}
Цикл 1–1
Блок 2 i = 0; 0 < 4 (истина, выполняется цикл)
Блок 3 mas_number = 32; bol = 0
Цикл 2–1
Блок 4 j = 1; 1 < 5 (истина, выполняется цикл)
51 < 32 (условие ложно, управление передается
Блок 5
вновь на оператор if «Блок 4»)
Цикл 2–2
Блок 4 j = 2; 2 < 5 (истина, выполняется цикл)
Выполняемые
11 < 32 (условие истинно, выполняется блок коциклы прог- Блок 5 да оператора if «Блок 6»)
раммного Блок 6 mas_number = 11; bol = 2
кода
Цикл 2–3
Блок 4 j = 3; 3 < 5 (истина, выполняется цикл)
7 < 11 (условие истинно, выполняется блок кода
Блок 5
оператора if «Блок 6»)
Блок 6 mas_number = 7; bol = 3
Цикл 2–4
Блок 4 j = 4; 4 < 5 (истина, выполняется цикл)
3 < 7 (условие истинно, выполняется блок кода
Блок 5
оператора if «Блок 6»)
Блок 6 mas_number = 3; bol = 4
42
Продолжение табл. 6.3
Блок 4
Блок 7
Блок 8
Блок 2
Блок 3
Блок 4
Выполняемые
циклы прог- Блок 5
раммного Блок 6
кода
Блок 4
Блок 5
Блок 6
Блок 4
Блок 5
Блок 4
Блок 7
Цикл 2–5
j = 5; 5 < 5 (ложь, завершение цикла, управление
передается на следующий оператор за for «Блок 7»)
Блок 7
4 ≠ 0 (условие истинно, выполняется блок кода
оператора if «Блок 8»)
Блок 8
dig_number[4] = 32; dig_number[0] = 3
(dig_number[5] = {3,51,11,7,32})
Цикл 1–2
i = 1; 1 < 4 (истина, выполняется цикл)
mas_number = 51; bol = 1
Цикл 2–1
j = 2; 2 < 5 (истина, выполняется цикл)
11 < 51 (условие истинно, выполняется блок кода оператора if «Блок 6»)
mas_number = 11; bol = 2
Цикл 2–2
j = 3; 3 < 5 (истина, выполняется цикл)
7 < 11 (условие истинно, выполняется блок кода
оператора if «Блок 6»)
mas_number = 7; bol = 3
Цикл 2–3
j = 4; 4 < 5 (истина, выполняется цикл)
32 < 7 (условие ложно, управление передается
вновь на оператора if «Блок 4»)
Цикл 2–4
j = 5; 5 < 5 (ложь, завершение цикла, управление
передается на следующий оператор за for «Блок 7»)
Блок 7
3 ≠ 1 (условие истинно, выполняется блок кода
оператора if «Блок 8»)
43
Продолжение табл. 6.3
Блок 8
Блок 2
Блок 3
Блок 4
Блок 5
Блок 4
Блок 5
Выполняемые
циклы прогБлок 4
раммного
кода
Блок 7
Блок 2
Блок 3
Блок 4
Блок 5
Блок 6
Блок 4
Блок 8
dig_number[3] = 51; dig_number[1] = 7
(dig_number[5] = {3,7,11,51,32})
Цикл 1–3
i = 2; 2 < 4 (истина, выполняется цикл)
mas_number = 11; bol = 2
Цикл 2–1
j = 3; 3 < 5 (истина, выполняется цикл)
51 < 11 (условие ложно, управление передается
вновь на оператор if «Блок 4»)
Цикл 2–2
j = 4; 4 < 5 (истина, выполняется цикл)
32 < 11 (условие ложно, управление передается
вновь на оператор if «Блок 4»)
Цикл 2–3
j = 5; 5 < 5 (ложь, завершение цикла, управление
передается на следующий оператор за for «Блок 7»)
Блок 7
2 ≠ 2 (условие ложно, управление передается
вновь на оператор for «Блок 2»)
(dig_number[5] = {3,7,11,51,32})
Цикл 1–4
i = 3; 3 < 4 (истина, выполняется цикл)
mas_number = 51; bol = 3
Цикл 2–1
j = 4; 4 < 5 (истина, выполняется цикл)
32 < 51 (условие истинно, выполняется блок кода оператора if «Блок 6»)
mas_number = 32; bol = 4
Цикл 2–2
j = 5; 5 < 5 (ложь, завершение цикла, управление
передается на следующий оператор за for «Блок 7»)
44
Окончание табл. 6.3
Блок 7
Выполняемые
циклы прогБлок 8
раммного
кода
Блок 2
Блок 7
4 ≠ 3 (условие истинно, выполняется блок кода
оператора if «Блок 8»)
Блок 8
dig_number[4] = 51; dig_number[3] = 32
(dig_number[5] = {3,7,11,32,51})
Цикл 1-5
i = 4; 4 < 4 (ложь, завершение цикла и программного кода в целом)
По окончании выполнения программного кода элементы массива
dig_number[5] будут располагаться следующим образом: dig_number[5] =
= {3,7,11,32,51}. Следовательно, решение поставленной задачи выполнено.
Пример 6.4
Задача. Требуется найти минимальное и максимальное число в массиве dig_number[n] и определить занимаемую позицию числом 24, если таковое присутствует (если такое содержится в массиве). Также необходимо
подсчитать в строке stroka[m] количество прописных «G», «E» и строчных
«g», «e» символов.
Блок-схема алгоритма, решающего поставленные задачи приведена на
рис. 7. Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет следующие переменные: counter, position, number_min, number_max, symbol_g, symbol_e,
строку stroka[m] и массив dig_number[n].
2. Блок модификации (цикл 1). Задаёт цикл со счетчиком, где начальное
значение счетчика (переменная position) равно 1, конечное n, а шаг изменения эквивалентен 1. В случае достижения счетчиком конечного значения (n), управление передается на блок операций над данными 4 (рис. 6.4).
3. Блок проверки условия. Производит проверку на равенство элемента
массива dig_number[n], расположенного по адресу, заданному переменной
position, с числом 24. При положительном результате проверки управление передается на блок операций над данными 4, в противном случае
незамедлительно переходит к блоку модификации 2 (рис. 6.4).
45
A
Начало
dig_number[n], stroka[m]
counter, position
number_min, number_max
symbol_g, symbol_e
1
2
Нет
position = 1, n, 1
Цикл 1
10
Нет
B
Цикл 2
12
counter = 1, m, 1
Цикл 3
Да
symbol_g++
13stroka[counter]=
= 'E' = 'e'
6
C
Нет
11stroka[counter]=
= 'G' = 'g'
counter = 1, n, 1
number_min >
dig_number[co
unter]
Да
number_min =
7 dig_number[counter]
A
D
number_max <
dig_number[co
unter]
Да
number_max =
9 dig_number[counter]
Да
number_min = dig_number[0]
number_max
= dig_number[0]
4
Нет
C
8
3 dig_number[po
sition] = 24
5
B
14
D
Нет
Да
symbol_e++
Конец
Рис. 6.4. Блок-схема алгоритма
4. Блок операций над данными. Загружает значение нулевого элемента
массива dig_number[n] (dig_number[0]) переменным number_min и
number_max.
5. Блок модификации (цикл 2). Задает цикл со счетчиком, где начальное
значение счетчика (переменная counter) равно 1, конечное n, а шаг изменения эквивалентен 1. В случае достижения счетчиком конечного значения (n), управление передается на блок модификации 10 (рис. 6.4).
6. Блок проверки условия. Выполняет проверку на то, что величина, содержащаяся в переменной number_min, больше по отношению к значению
элемента массива dig_number[n] (dig_number[counter]), расположенного по
46
адресу, равному переменной counter. В случае положительного результата
проверки управление передается на блок операций над данными 7; если исход
проверки отрицателен, то управление переходит к блоку проверки условия 8.
7. Блок операций над данными. Присваивает переменной number_min
значение элемента массива dig_number[n] (dig_number[counter]), расположенного по адресу, равному переменной counter.
8. Блок проверки условия. Выполняет проверку на подтверждение того,
что величина, содержащаяся в переменной number_max, меньше по отношению к значению элемента массива dig_number[n] (dig_number[counter]),
расположенного по адресу, равному переменной counter. В случае положительного результата проверки управление передается на блок операций
над данными 9; если исход проверки отрицателен, то управление переходит к блоку модификации 5 (рис. 6.4).
9. Блок операций над данными. Присваивает переменной number_max
значение элемента массива dig_number[n] (dig_number[counter]), расположенного по адресу, равному переменной counter. Затем управление передается на блок модификации 5.
10. Блок модификации (цикл 3). Задает цикл со счетчиком, при этом
начальное значение счетчика (переменная counter) равно 1, конечное m, а шаг
изменения эквивалентен 1. В результате достижения счетчиком конечного
значения (m) цикл, а также алгоритм в целом заканчивают выполнение.
11. Блок проверки условия. Осуществляет проверку на равенство элемента строки stroka[m] (stroka[counter]), расположенного по адресу, равному переменной counter символам «G», «g». В случае положительного
результата проверки управление передается на блок операций над данными 12; если исход проверки отрицателен, то управление переходит к блоку
проверки условия 13.
12. Блок операций над данными. Производит увеличение переменной
symbol_g на единицу.
13. Блок проверки условия. Выполняет проверку на равенство элемента
строки stroka[m] (stroka[counter]), расположенного по адресу, равному переменной counter символам «E», «e». При положительном результате проверки управление передается на блок операций над данными 14; если исход проверки будет отрицательным, то управление переходит к блоку модификации 10 (рис. 6.4).
14. Блок операций над данными. Производит увеличение переменной
symbol_e на единицу.
На основании приведенного алгоритма составлен программный код на
языке программирования С. Части кода, выполняющие аналогичные функции
блоков графического алгоритма (рис. 6.4), отмечены подобными цифрами:
47
1
2, 3
void main (void)
{
int dig_number[7] = {23,41,53,24,4,9,48};
char counter = 0;
char position;
int number_min, number_max;
char stroka[11] = "Golden age";
char symbol_g = 0, symbol_e = 0;
for (position = 0; dig_number[position] != 24 && position != 6;
position++);
4 – number_min = number_max = dig_number[0];
5 – for (counter = 1; counter <= 6; counter++)
{
if (number_min > dig_number[counter]) number_min =
6, 7
= *(dig_number + counter);
if (number_max < dig_number[counter]) number_max =
8, 9
= *(dig_number + counter);
}
counter = 0;
10
while (stroka[counter] != 0)
{
11, 12 – if (stroka[counter] == 'G' || stroka[counter] == 'g') symbol_g++;
13, 14 – if (stroka[counter] == 'E' || stroka[counter] == 'e') symbol_e++;
10 – counter++;
}
}
Ход выполнения программного кода со срезом значений переменных
в ключевых местах программы приведен в табл. 6.4.
Таблица 6.4
Детализация выполнения программного кода
Объявление переменных, строк и массивов. Блок 1
int dig_number[7] = {23,41,53,24,4,9,48};
char counter = 0;
char position;
Программный
1
код
int number_min, number_max;
char stroka[11] = "Golden age";
char symbol_g = 0, symbol_e = 0;
48
Продолжение табл. 6.4
Объявлены: целочисленные переменные number_min,
number_max и массив dig_number[7], а также символьные
Полученный
переменные counter, position, symbol_e, symbol_c и строка
результат
stroka[11]. Символьным переменным присвоены нулевые
после выползначения, массив dig_number[7] инициализирован из чинения кода
сел {23, 41, 53, 24, 4, 9, 48}, а строка stroka[11] содержит
словосочетание "Golden age"
Циклы 1 и 2 решения первой задачи. Блоки: 2, 3, 4, 5, 6, 7, 8, 9
for (position = 0; dig_number[position] != 24 &&
2,3
position != 6; position++);
– number_min = number_max = dig_number[0];
– for (counter = 1; counter <= 6; counter++)
Программный
{
код
if (number_min > dig_number[counter]) num6,7
ber_min = = *(dig_number + + counter);
if (number_max < dig_number[counter]) num8,9
ber_max = = *(dig_number + + counter);
}
Цикл 1–1
position = 0; 23 ≠ 24 и 0 ≠ 6
Блок 2, 3 (оба условия истины, оператор продолжает
выполняться)
Цикл 1–2
position = 1; 41 ≠ 24 и 1 ≠ 6
Блок 2, 3 (оба условия истины, оператор продолжает
Выполняемые
выполняться)
циклы прогЦикл 1–3
раммного
position = 2; 53 ≠ 24 и 2 ≠ 6
кода
Блок 2, 3 (оба условия истины, оператор продолжает
выполняться)
Цикл 1–4
position = 3; 24 ≠ 24 и 3 ≠ 6
(первое условие ложно, выполнение оператора
Блок 2, 3
прерывается, и управление передается на следующий оператор за for «Блок 4»)
4
5
49
Продолжение табл. 6.4
Блок 4
Блок 5
Блок 6
Блок 8
Блок 9
Блок 5
Блок 6
Выполняемые
Блок 8
циклы программного Блок 9
кода
Блок 5
Блок 6
Блок 8
Блок 5
Блок 6
Блок 7
Блок 8
Блок 4
number_min = number_max = 23
Цикл 2–1
counter = 1; 1 <= 6 (истина, выполняется цикл)
23 > 41 (условие ложно, управление передается
на следующий оператор условия if «Блок 8»)
(number_min = 23)
23 < 41 (условие истинно, выполняется блок
кода оператора if «Блок 9»)
number_max = 41
Цикл 2–2
counter = 2; 2 <= 6 (истина, выполняется цикл)
23 > 53 (условие ложно, управление передается
на следующий оператор условия if «Блок 8»)
(number_min = 23)
41 < 53 (условие истинно, выполняется блок
кода оператора if «Блок 9»)
number_max = 53
Цикл 2–3
counter = 3; 3 <= 6 (истина, выполняется цикл)
23 > 24 (условие ложно, управление передается
на следующий оператор условия if «Блок 8»)
(number_min = 23)
53 < 24 (условие ложно, управление передается на оператор for «Блок 5»)
(number_max = 53)
Цикл 2–4
counter = 4; 4 <= 6 (истина, выполняется цикл)
23 > 4 (условие истинно, выполняется блок
кода оператора if «Блок 7»)
number_min = 4
53 < 4 (условие ложно, управление передается
на оператора for «Блок 5»)
(number_max = 53)
50
Продолжение табл. 6.4
Цикл 2–5
Блок 5
counter = 5; 5 <= 6 (истина, выполняется цикл)
4 > 9 (условие ложно, управление передается
Блок 6
на следующий оператор условия if «Блок 8»)
(number_min = 4)
53 < 9 (условие ложно, управление передаетБлок 8
ся на оператор for «Блок 5»)
(number_max = 53)
Выполняемые
Цикл 2–6
циклы прог- Блок 5
counter = 6; 6 <= 6 (истина, выполняется цикл)
раммного
4 > 48 (условие ложно, управление передается
кода
Блок 6
на следующий оператор условия if «Блок 8»)
(number_min = 4)
53 < 48 (условие ложно, управление передаБлок 8
ется на оператор for «Блок 5»)
(number_max = 53)
Цикл 2–7
counter = 7; 7 <= 6 (условие ложно, выполнение
Блок 5
оператора прерывается, и управление передается на следующий оператор за for «Блок 10»)
Цикл 3 решения второй задачи. Блоки: 10, 11, 12, 13, 14
counter = 0;
10
while (stroka[counter] != 0)
{
if (stroka[counter] == 'G' || stroka[counter] == 'g')
Программный 11,12
symbol_g++;
код
if (stroka[counter] == 'E' || stroka[counter] == 'e')
13,14
symbol_e++;
10 – counter++;
}
Блок 10
Выполняемые Блок 10 counter = 0
циклы прогЦикл 3–1
раммного Блок 10
'G' ≠ 0 (истина, выполняется цикл)
кода
'G' = 'G' или 'G' = 'g' (одно из условий истинно,
Блок 11
выполняется блок кода оператора if «Блок 12»)
51
Продолжение табл. 6.4
Блок 12
Блок 13
Блок 10
Блок 10
Блок 11
Блок 13
Выполняемые
циклы прог- Блок 10
раммного
кода
Блок 10
Блок 11
Блок 13
Блок 10
Блок 10
Блок 11
symbol_g = 1
'G' = 'E' или 'G' = 'e' (оба условия ложны,
управление передается на следующий оператор за if «Блок 10»)
(symbol_e = 0)
counter = 1
Цикл 3–2
'о' ≠ 0 (истина, выполняется цикл)
'о' = 'G' или 'о' = 'g' (оба условия ложны, управление передается на следующий оператор if
«Блок 13»)
(symbol_g = 1)
'о' = 'E' или 'о' = 'e' (оба условия ложны, управление передается на следующий оператор за if
«Блок 10»)
(symbol_e = 0)
counter = 2
Цикл 3–3
'l' ≠ 0 (истина, выполняется цикл)
'l' = 'G' или 'l' = 'g' (оба условия ложны, управление передается на следующий оператор if
«Блок 13»)
(symbol_g = 1)
'l' = 'E' или 'l' = 'e' (оба условия ложны, управление передается на следующий оператор за if
«Блок 10»)
(symbol_e = 0)
counter = 3
Цикл 3–4
'd' ≠ 0 (истина, выполняется цикл)
'd' = 'G' или 'd' = 'g' (оба условия ложны, управление передается на следующий оператор if
«Блок 13»)
(symbol_g = 1)
52
Продолжение табл. 6.4
Блок 13
Блок 10
Блок 10
Блок 11
Блок 13
Блок 14
Блок 10
Блок 10
Выполняемые
циклы программного Блок 11
кода
Блок 13
Блок 10
Блок 10
Блок 11
Блок 13
Блок 10
'd' = 'E' или 'd' = 'e' (оба условия ложны, управление передается на следующий оператор за if
«Блок 10»)
(symbol_e = 0)
counter = 4
Цикл 3–5
'e' ≠ 0 (истина, выполняется цикл)
'e' = 'G' или 'e' = 'g' (оба условия ложны, управление передается на следующий оператор if
«Блок 13»)
(symbol_g = 1)
'e' = 'E' или 'e' = 'e' (одно из условий истинно,
выполняется блок кода оператора if «Блок 14»)
symbol_e = 1
counter = 5
Цикл 3–6
'n' ≠ 0 (истина, выполняется цикл)
'n' = 'G' или 'n' = 'g' (оба условия ложны, управление передается на следующий оператор if
«Блок 13»)
(symbol_g = 1)
'n' = 'E' или 'n' = 'e' (оба условия ложны, управление передается на следующий оператор за if
«Блок 10»)
(symbol_e = 1)
counter = 6
Цикл 3–7
'0x20' ≠ 0 (истина, выполняется цикл, так как в
кодовой таблице ASCII пробелу присвоено значение 0x20)
'0x20' = 'G' или '0x20' = 'g' (оба условия ложны,
управление передается на следующий оператор
if «Блок 13»), (symbol_g = 1)
'0x20' = 'E' или '0x20' = 'e' (оба условия ложны,
управление передается на следующий оператор
за if «Блок 10»), (symbol_e = 1)
counter = 7
53
Окончание табл. 6.4
Блок 10
Блок 11
Блок 13
Блок 10
Блок 10
Блок 11
Выполняемые Блок 12
циклы программного
Блок 13
кода
Блок 10
Блок 10
Блок 11
Блок 13
Блок 14
Блок 10
Блок 10
Цикл 3–8
'а' ≠ 0 (истина, выполняется цикл)
'а' = 'G' или 'а' = 'g' (оба условия ложны, управление передается на следующий оператор if
«Блок 13»)
(symbol_g = 1)
'а' = 'E' или 'а' = 'e' (оба условия ложны, управление передается на следующий оператор за if
«Блок 10»)
(symbol_e = 1)
counter = 8
Цикл 3–9
'g' ≠ 0 (истина, выполняется цикл)
'g' = 'G' или 'g' = 'g' (одно из условий истинно,
выполняется блок кода оператора if «Блок 12»)
symbol_g = 2
'g' = 'E' или 'g' = 'e' (оба условия ложны, управление передается на следующий оператор за if
«Блок 10»)
(symbol_e = 1)
counter = 9
Цикл 3–10
'e' ≠ 0 (истина, выполняется цикл)
'e' = 'G' или 'e' = 'g' (оба условия ложны, управление передается на следующий оператор if
«Блок 13»)
(symbol_g = 2)
'e' = 'E' или 'e' = 'e' (одно из условий истинно,
выполняется блок кода оператора if «Блок 14»)
symbol_e = 2
counter = 10
Цикл 3–11
\0 ≠ 0 (ложь, завершение цикла и программного
кода в целом)
54
Ответ. В массиве dig_number[7] = {23,41,53,24,4,9,48} имеются минимальное число 4 (number_min = 4) и максимальное 53 (number_max = 53),
а число 24 располагается на третьей позиции (position = 3). Строка
stroka[11] = "Golden age" содержит по два прописных «G», «E» и строчных
«g», «e» символов (symbol_g = 2, symbol_e = 2).
Пример 6.5
Задача. Пассажирский поезд следует от станции А до станции Д с остановками на промежуточных станциях Б, В и Г, при этом стоянка на станциях
Б и Г составляет 10 минут, а на ст. В – 15. Расстояния между станциями равно: А–Б – 62 км, Б–В – 87 км, В–Г – 107 км, Г–Д – 26 км. Время, затраченное
на прохождение от станций составляет: от станции А до станции Б – 45 мин;
от ст. Б до ст. В – 55 мин; от ст. Б до ст. В – 55 мин; от ст. В до ст. Г –
80 мин; от ст. Г до ст. Д – 15 мин. Требуется определить: 1) среднюю скорость пассажирского поезда на каждом участке (А–Б, Б–В, В–Г, Г–Д); 2) вычислить время его нахождения в пути. (Среднюю скорость представить,
км/ч, а полное время в пути в виде Х часов ХХ минут).
Блок-схема алгоритма, решающего поставленные задачи, приведена на
рис. 6.3. Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет переменные: way,
hour, min, time_way и массивы: distance[n], time[n], parking[n], speed[n].
2. Блок модификации. Задает цикл со счетчиком, где начальное значение счетчика (переменная way) равно 1, конечное n, а шаг изменения эквивалентен 1. В случае достижения счетчиком конечного значения (n)
управление передается на блок проверки условия 5 (рис. 6.3).
3. Блок операций над данными. Присваивает элементу массива speed[n]
(speed[way]), расположенного по адресу, равному переменной way, значение,
содержащееся в массиве distance[n] (distance [way]), деленное на частное от
значения, находящегося в массиве time[n] (time[way]), деленное на 60.
4. Блок операций над данными. Увеличивает переменную time_way на
величины значений элементов массивов time[n] (time[way]) и parking[n]
(parking[way]), расположенных по адресу, равному переменной way.
5. Блок проверки условия. Выполняет проверку на то, что величина, содержащаяся в переменной time_way, больше либо равна 60. В случае положительного результата проверки управление передается на блок операций над данными 6; если исход проверки отрицателен, то блок-схема алгоритма заканчивает решение поставленной задачи.
6. Блок операций над данными. Уменьшает переменную time_way на 60.
Переменная min приравнивается к time_way, а значение переменной hour
увеличивается на единицу.
55
Начало
A
time[n], distance[n]
speed[n], parking[n]
way, hour, min
time_way
1
2
5 time_way ≥ 60
Нет
Да
time_way = time_way ‒ 60
min = time_way
6
hour ++
way = 1, n, 1
speed[way] = distance[way] /
/ (time[way]/60)
3
Конец
time_way = time_way +
4 + time[way] + parking[way]
A
Рис. 6.3. Блок-схема алгоритма
На основании приведенного алгоритма составляется программный код на
языке программирования С. Части кода, выполняющие аналогичные функции
блоков графического алгоритма (рис. 6.3), отмечены подобными цифрами:
void Speed_calc (char data_s);
0
float Time_min_hour (float data);
void Time_calc (char data_t);
1
char distance[4] = {62,87,107,26};
char time[4] = {45,55,80,15};
char parking[4] = {10,15,10,0};
float speed[4];
unsigned char time_way, min;
char way, hour;
56
3.1
3.2
4.1
void Speed_calc (char data_s)
{
*(speed + data_s) = *(distance + data_s) /
/ Time_min_hour (*(time + data_s));
}
float Time_min_hour (float data)
{
return data / 60;
}
void Time_calc (char data_t)
{
time_way = time_way + *(time + data_t);
time_way = time_way + *(parking + data_t);
}
void main (void)
{
2 – for (way = 0; way <= 3; way++)
{
3 – Speed_calc (way);
4 – Time_calc (way);
}
– for (hour = 0; time_way >= 60; hour++)
5,6
min = time_way = time_way - 60;
}
Ход выполнения программного кода со срезом значений переменных в
ключевых местах программы приведен в табл. 6.5.
Таблица 6.5
Детализация выполнения программного кода
Объявление прототипов функций. Блок 0
void Speed_calc (char data_s);
Программный
0
float Time_min_hour (float data);
код
void Time_calc (char data_t);
57
Продолжение табл. 6.5
Объявлены прототипы функций Speed_calc, Time_min_hour,
Полученный Time_calc, содержащие возможность получения символьных
результат (char) и вещественных (float) аргументов, при этом функция
после выпол- Time_min_hour возвращает результат в виде вещественного
нения кода значения (float), а в двух других функциях, возвращаемый
результат отсутствует (void)
Объявление переменных и массивов. Блок 1
char distance[4] = {62,87,107,26};
char time[4] = {45,55,80,15};
char parking[4] = {10,15,10,0};
Программный
1
код
float speed[4];
unsigned char time_way, min;
char way, hour;
Объявлены: символьные переменные way, hour, min,
time_way и массивы distance[4]*, time[4], parking[4], а также
массив вещественных переменных speed[4]. Символьные
массивы данных содержат: distance[4] – расстояния между
станциями в км, time[4] – время, затраченное на преодоление расстояния от одной станции до другой в мин,
parking[4] – время стоянки на станциях в мин. В символьном массиве данных parking[4] намеренно добавлен 0 (на
Полученный
четвертую позицию), хотя по условию задачи он должен
результат
состоять из трех элементов. Это сделано для того, чтобы
после выполуравнять размерность массива parking[4] и массивов
нения кода
distance[4] и time[4], что в свою очередь приведет к значительному упрощению программного кода. Такое добавление элемента в массив parking[4] на величину итогового результата никак не повлияет, поскольку при действии сложения ноль не учитывается. В массиве вещественных данных speed[4] будет сохраняться результат вычисления
средней скорости движения пассажирского поезда на каждом участке пути, км/ч (А–Б, Б–В, В–Г, Г–Д)
* Здесь и далее указанная цифра в квадратных скобках не является адресной
ссылкой на использованную литературу.
58
Продолжение табл. 6.5
Запись функций подпрограмм. Блоки: 3.1, 3.2, 4.1
void Speed_calc (char data_s)
{
3.1
*(speed + data_s) = *(distance + data_s) /
/ Time_min_hour (*(time + data_s));
}
float Time_min_hour (float data)
{
Программный
3.2
код
return data / 60;
}
void Time_calc (char data_t)
{
4.1
time_way = time_way + *(time + data_t);
time_way = time_way + *(parking + data_t);
}
Полученный
Записаны функции подпрограмм Speed_calc, Time_min_hour,
результат
Time_calc, текст которых будет выполняться при обращении
после выполк ним из основного кода программы
нения кода
Цикл решения поставленной задачи. Блоки: 2, 3, 4, 5, 6
2 – for (way = 0; way <= 3; way++)
{
3 – Speed_calc (way);
Программный
4 – Time_calc (way);
код
}
for (hour = 0; time_way >= 60; hour++) min = time_way =
5,6
= time_way - 60;
Цикл 1
Выполняемые Блок 2 way = 0; way ≤ 3 (истина, выполняется цикл)
циклы прогБлок 3
раммного Блок 3.1 speed[0] = distance[0] / Time_min_hour (time[0])
кода
Блок 3.2 Time_min_hour (time[0]) = 45 / 60 = 0,75
Блок 3.1 speed[0] = 62 / 0,75 = 82,6
59
Продолжение табл. 6.5
Блок 4.1
Блок 2
Блок 3.1
Блок 3.2
Блок 3.1
Блок 4.1
Выполняемые Блок 2
циклы программного Блок 3.1
кода
Блок 3.2
Блок 3.1
Блок 4.1
Блок 2
Блок 3.1
Блок 3.2
Блок 3.1
Блок 4.1
Блок 4
time_way = 0 + 45 = 45;
time_way = 45 + 10 = 55
(speed[4] = {82,6; 0,0; 0,0; 0,0}; time_way = 55)
Цикл 2
way = 1; way ≤ 3 (истина, выполняется цикл)
Блок 3
speed[1] = distance[1] / Time_min_hour (time[1])
Time_min_hour (time[1]) = 55 / 60 = 0,916
speed[1] = 87 / 0,916 = 94,9
Блок 4
time_way = 55 + 55 = 110;
time_way = 110 + 15 = 125
(speed[4] = {82,6; 94,9; 0,0; 0,0}; time_way = 125)
Цикл 3
way = 2; way ≤ 3 (истина, выполняется цикл)
Блок 3
speed[2] = distance[2] / Time_min_hour (time[2])
Time_min_hour (time[2]) = 80 / 60 = 1,33
speed[2] = 107 / 1,33 = 80,2
Блок 4
time_way = 125 + 80 = 205;
time_way = 205 + 10 = 215
(speed[4] = {82,6; 94,9; 80,2; 0,0}; time_way = 215)
Цикл 4
way = 3; way ≤ 3 (истина, выполняется цикл)
Блок 3
speed[3] = distance[3] / Time_min_hour (time[3])
Time_min_hour (time[3]) = 15 / 60 = 0,25
speed[3] = 26 / 0,25 = 104
Блок 4
time_way = 215 + 15 = 230;
time_way = 230 + 0 = 230
(speed[4] = {82,6; 94,9; 80,2; 104,0}; time_way = 230)
60
Продолжение табл. 6.5
Блок 2
Блок 5,6
Выполняемые
циклы программного
Блок 5,6
кода
Блок 5,6
Блок 5,6
Цикл 5
way = 4; way ≤ 3 (ложь, выполнение оператора
прерывается, и управление передается на следующий оператор за for «Блок 5»)
(speed[4] = {82,6; 94,9; 80,2; 104,0}; time_way = 230)
Блок 5, 6
hour = 0; 230 ≥ 60 (истина, выполняется цикл)
min = time_way = 230 – 60 = 170
(hour = 0; min = 170; time_way = 170)
Блок 5, 6
hour = 1; 170 ≥ 60 (истина, выполняется цикл)
min = time_way = 170 – 60 = 110
(hour = 1; min = 110; time_way = 110)
Блок 5, 6
hour = 2; 110 ≥ 60 (истина, выполняется цикл)
min = time_way = 110 – 60 = 50
(hour = 2; min = 50; time_way = 50)
Блок 5, 6
hour = 3; 50 ≥ 60 (ложь, завершение оператора for
«Блок 5» и программного кода в целом)
(hour = 3; min = 50; time_way = 50)
Ответ. Средняя скорость пассажирского поезда на участках составляет:
А–Б – 82,6 км/ч, Б–В – 94,9 км/ч, В–Г – 80,2 км/ч, Г–Д – 104,0 км/ч. Общее
время нахождения пассажирского поезда в пути составило 3 часа 50 минут.
7. ПРИМЕРЫ ВЫПОЛНЕНИЯ ПРОГРАММНЫХ КОДОВ
С ПРИМЕНЕНИЕМ БЛОК-СХЕМ
Решение приведенных задач должно содержать блок-схемы алгоритмов и программные коды на языке программирования С.
Задание 1
Задача. Требуется установить числа, на которые делится число 60 без
остатка и определить их количество. Числа необходимо сохранить в массив dig_number[], а их количество в переменную – counter_num.
61
Решение. Число 60 делится без
остатка только на числа, которые
меньше 60, и на само себя, поэтому
balance, counter_num
числа больше 60 рассматривать нет
необходимости.
1 dig_number[20], score
Блок-схема алгоритма, решающего поставленную задачу, приведена
2 score = 1, 60, 1
на рис. 7.1.
Рассмотрим назначение каждого
блока.
balance = 60 % score
1. Блок операций ввода и вывода
3
данных. Объявляет переменные: balance,
counter_num, score и массив –
Нет
dig_number[20]. Размерность массива
4 balance = 0
составляет 20 единиц, поскольку маловероятно, что количество чисел, на
Да
которые делится число 60 без остатка,
dig_number[counter_num] =
будет больше 20.
= score
2. Блок модификации. Задает цикл
counter_num++
5
со счетчиком, при этом начальное
значение счетчика (переменная score)
равно 1, конечное 60, а шаг изменения эквивалентен 1. При достижении
счетчиком конечного значения (60)
Конец
цикл и алгоритм в целом заканчивают
своё выполнение.
Рис. 7.1. Блок-схема алгоритма
3. Блок операций над данными.
Осуществляет присвоение переменной balance остатка от деления числа 60 на переменную score.
4. Блок проверки условия. Выполняет проверку на равенство нулю переменной balance. В случае положительного результата проверки управление передается на блок операций над данными 5; если исход проверки
отрицателен, то управление незамедлительно переходит к блоку модификации 2 (см. рис. 7.1).
5. Блок операций над данными. Присваивает элементу массива
dig_number (dig_number[counter_num]), расположенного по адресу, равному переменной counter_num, значение переменной score, а также производит увеличение переменной counter_num на единицу. Затем управление
передается на блок модификации 2.
На основании приведенного алгоритма составляется программный код
на языке программирования С. Части кода, выполняющие аналогичные
функции блоков графического алгоритма (рис. 7.1), отмечаются подобными цифрами:
Начало
62
1
void main (void)
{
char balance;
char score;
char counter_num = 0;
char dig_number[20];
2 – for (score = 1; score <= 60; score++)
{
3 – balance = 60 % score; // Остаток от деления
4 – if (balance == 0)
{
dig_number[counter_num] = score;
5
counter_num++;
}
}
}
После выполнения программного кода массив dig_number[20] будет
содержать следующие значения: dig_number[20] = {1, 2, 3, 4, 5, 6, 10, 12,
15, 20, 30, 60}, количество которых
равно 12 (counter_num = 12).
Начало
Задание 2
Задача. Требуется разработать программу – программу генератора последовательности чисел Фибоначчи, до
пятнадцатого порядкового номера
включительно. Ряд чисел необходимо
сохранить в массив dig_number[15].
Последовательность чисел Фибоначчи представляет собой числовой ряд,
где каждое число является суммой двух
предыдущих чисел: 0, 1, 1, 2, 3, 5 и т.д.
Решение. Поставленная задача решается при помощи блок-схемы алгоритма (рис. 7.2).
63
fib_w = 1, counter
1 dig_number[15] = {0}
2 counter = 2, 15, 1
dig_number[counter] = fib_w
fib_w = dig_number[counter –
– 1] + fib_w
3
Конец
Рис. 7.2. Блок-схема алгоритма
Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет переменные:
fib_w = 1, counter и массив – dig_number[15].
2. Блок модификации. Задает цикл со счетчиком, при этом начальное
значение счетчика (переменная counter) равно 2, конечное 15, а шаг изменения эквивалентен 1. При достижении счетчиком конечного значения 15
цикл и алгоритм в целом своё выполнение заканчивают.
3. Блок операций над данными. Присваивает элементу массива
dig_number (dig_number[counter]), расположенного по адресу, равному переменной counter, значение переменной fib_w. Далее переменная fib_w
увеличивается на число, содержащееся в массиве dig_number по адресу,
равному переменной counter – 1. После этого управление передается на
блок модификации 2.
На основании приведенного алгоритма составляется программный код
на языке программирования С. Части кода, выполняющие аналогичные
функции блоков графического алгоритма (см. рис. 7.2), отмечены подобными цифрами:
void main (void)
{
int fib_w = 1;
1
char counter = 0;
int dig_number[15] = {0};
2 – for (counter = 1; counter <= 14; counter++)
{
dig_number[counter] = fib_w;
3
fib_w = *(dig_number + counter - 1) + fib_w;
}
}
По окончании выполнения программного кода массив dig_number[15]
будет содержать следующую последовательность чисел Фибоначчи –
dig_number[15] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377}.
Задание 3
Задача. На метеорологической станции ежедневно регистратор считывает значения температуры и относительной влажности воздуха каждый
час, которые сохраняет в массиве данных data[48]. Показания регистратора размещаются в массиве data[48] в следующем формате:
64
В период с 00:00 до 01:00 часов
«0» адрес массива – температура воздуха;
«1» адрес массива – относительная влажность воздуха.
В период с 01:00 до 02:00 часов
«2» адрес массива – температура воздуха;
«3» адрес массива – относительная влажность воздуха.
и т. д.
В конце очередного дня сформировался следующий массив данных:
data[48] = {6, 91, 6, 91, 5, 90, 5, 90, 7, 86, 9, 78, 12, 72, 14, 64, 16, 52, 17, 49,
17, 48, 18, 47, 19, 52, 18, 56, 17, 59, 17, 65, 16, 70, 15, 77, 14, 72, 14, 66, 13,
64, 13, 66, 12, 68, 12, 69}.
Требуется разработать программу, в которой на основании суточных показаний, содержащихся в массиве data[48], будет определена максимальная/минимальная температура и относительная влажность воздуха за сутки, а также вычислены их среднесуточные показания.
Решение. Поставленная задача решается при помощи блок-схемы алгоритма (рис. 7.3).
Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет переменные: counter = 0, buffer, average_temp = 0, max_temp, min_temp, average_air_hum = 0,
max_average_air_hum, min_average_air_hum и массив – data[48]. Переменные:
– counter и buffer используются как вспомогательные;
– max_temp, min_temp и average_temp применяются для хранения значений максимальной/минимальной и среднесуточной температуры воздуха за сутки соответственно;
– max_average_air_hum, min_average_air_hum и average_air_hum используются для хранения значений максимальной/минимальной и среднесуточной влажности воздуха за сутки соответственно.
2. Блок операций над данными. Осуществляет присвоение переменным
max_temp и min_temp значения элемента массива data (data[0]), расположенного в нулевом адресе массива.
3. Блок модификации (цикл 1). Задает цикл со счетчиком, при этом начальное значение счетчика (переменная counter) равно 1, конечное 47, а шаг изменения эквивалентен 2. В случае достижения счетчиком конечного значения (47) управление передается на блок операций над данными 9 (рис. 7.3).
4. Блок операций над данными. Присваивает переменной buffer значение элемента массива data (data[counter]), расположенного по адресу, равному переменной counter, а также производит увеличение переменной
average_temp на величину переменной buffer.
65
5. Блок проверки условия. Выполняет проверку на то, что величина, содержащаяся в переменной max_temp, меньше по отношению к значению,
находящемуся в переменной buffer. В случае положительного результата
проверки управление передается на блок операций над данными 6; если
исход проверки отрицателен, то управление незамедлительно переходит к
блоку модификации 3 (рис. 7.3).
6. Блок операций над данными. Приравнивает переменную max_temp к
buffer.
7. Блок проверки условия. Выполняет проверку, которая должна подтвердить, что величина, содержащаяся в переменной min_temp, действительно
больше по отношению к значению, находящемуся в переменной buffer.
В случае положительного результата проверки управление передается на
блок операций над данными 8; если исход проверки отрицателен, то управление незамедлительно переходит к блоку модификации 3 (рис. 7.3).
8. Блок операций над данными. Приравнивает переменную min_temp к
buffer.
9. Блок операций над данными. Производит деление переменной
average_temp на число 24, а также осуществляет присвоение переменным
max_average_air_hum и min_average_air_hum значения элемента массива
data (data[1]), расположенного в первом адресе массива.
10. Блок модификации (цикл 2). Задает цикл со счетчиком, при этом
начальное значение счетчика (переменная counter) равно 2, конечное 48,
шаг изменения эквивалентен 2. В случае достижения счетчиком конечного
значения (48) управление передается на блок операций над данными 16
(рис. 7.3).
11. Блок операций над данными. Присваивает переменной buffer значение элемента массива data (data[counter]), расположенного по адресу, равному переменной counter, а также производит увеличение переменной
average_air_hum на величину переменной buffer.
12. Блок проверки условия. Выполняет проверку на то, что величина,
содержащаяся в переменной max_average_air_hum, меньше по отношению
к значению, находящемуся в переменной buffer. В случае положительного
результата проверки управление передается на блок операций над данными 13; если исход проверки отрицателен, то управление незамедлительно
переходит к блоку модификации 10 (рис. 7.3).
13. Блок операций над данными. Приравнивает переменную
max_average_air_hum к buffer.
14. Блок проверки условия. Выполняет проверку на то, что величина,
содержащаяся в переменной min_average_air_hum, больше по отношению
к значению, находящемуся в переменной buffer. В случае положительного
66
результата проверки управление передается на блок операций над данными 15; если исход проверки отрицателен, то управление незамедлительно
переходит к блоку модификации 10 (рис. 7.3).
Начало
A
data[48], counter = 0
buffer, average_temp = 0
max_temp, min_temp
average_air_hum = 0
max_average_air_hum
1 min_average_air_hum
average_temp =
= average_temp / 24
max_average_air_hum =
= min_average_air_hum =
= data[1]
9
10 counter = 2, 48, 2
max_temp = min_temp =
= data[0]
2
Цикл 2
3 counter = 1, 47, 2
buffer = data[counter]
average_air_hum = buffer +
11 + average_air_hum
buffer = data[counter]
average_temp = buffer +
+ average_temp
4
12
Цикл 1
Нет
5
6 max_temp = buffer
7
max_average_air_hum
< buffer
Да
max_temp
< buffer
Да
Нет
Нет
13 max_average_air_hum = buffer
Нет
14
min_average_air_hum
> buffer
Да
min_temp
> buffer
15 min_average_air_hum = buffer
Да
8
min_temp = buffer
16 average_air_hum =
= average_air_hum / 24
Конец
A
Рис. 7.3. Блок-схема алгоритма
67
15. Блок операций над данными. Приравнивает переменную
min_average_air_hum к buffer. После этого управление передается на блок
модификации 10.
16. Блок операций над данными. Производит деление переменной
average_air_hum на число 24, после этого алгоритм свою работу заканчивает.
На основании приведенного алгоритма составляется программный код на
языке программирования С. Части кода, выполняющие аналогичные функции
блоков графического алгоритма (см. рис. 7.3), отмечены подобными цифрами:
void main (void)
{
char data[48] = {6,91,6,91,5,90,5,90,7,86,9,78,
12,72,14,64,16,52,17,49,17,48,
18,47,19,52,18,56,17,59,17,65,
16,70,15,77,14,72,14,66,13,64,
13,66,12,68,12,69};
1
char counter = 0;
char buffer;
int average_temp = 0;
char max_temp, min_temp;
int average_air_hum = 0;
char max_average_air_hum, min_average_air_hum;
2 – max_temp = min_temp = data[0];
3 – for (counter = 0; counter <= 46; counter = counter + 2)
{
buffer = *(data + counter);
average_temp = buffer + average_temp;
4–8
if (max_temp < buffer) max_temp = buffer;
if (min_temp > buffer) min_temp = buffer;
}
average_temp = average_temp / 24;
9
max_average_air_hum = min_average_air_hum = data[1];
10 – for (counter = 1; counter <= 47; counter = counter + 2)
{
68
buffer = *(data + counter);
average_air_hum = buffer + average_air_hum;
11–15
if (max_average_air_hum < buffer) max_average_air_hum = buffer;
if (min_average_air_hum > buffer) min_average_air_hum = buffer;
}
16 – average_air_hum = average_air_hum / 24;
}
По завершении выполнения программного кода максимальная и минимальная температура, а также относительная влажность воздуха составляют:
19°, 5°; 91 %, 47 % соответственно, а среднесуточные показания – 13° и 68 %.
Задание 4
Задача. Требуется разработать программу генератора десяти случайных чисел 4-битовой точности (максимальное число 15 = 0b00001111), которые должны быть разделены на четные/нечетные и размещены в массивах chet_num[10]/nochet_num[10] соответственно, при этом четные числа
должны располагаться в порядке убывания по величине, а нечетные –
в порядке возрастания.
Решение. С целью демонстрации гибкости языка программирования
высокого уровня С приведем только два варианта решения поставленной
задачи, так как имеется множество других вариантов решения, которые
также будут давать правильный ответ.
Блок-схема алгоритма первого варианта, решающего поставленную задачу, приведена на рис. 7.4.
Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет переменные: val,
counter, chet = 0, nochet = 0, array = 10, mas_number, i, j, bol и массивы:
chet_num[10], nochet_num[10], number[10].
Переменные:
– val, counter, array, mas_number, i, j и bol используются как вспомогательные;
– chet и nochet применяются для хранения промежуточных значений
четных и нечетных чисел соответственно.
Массивы:
– chet_num[10] и nochet_num[10] предназначены для хранения четных
и нечетных сгенерированных случайных чисел соответственно;
69
– number[10] используется как вспомогательный массив для временного хранения сгенерированных случайных чисел.
2. Блок модификации (цикл 1). Задает цикл со счетчиком, при этом начальное значение счетчика (переменная counter) равно 1, конечное 10, а шаг изменения эквивалентен 1. В случае достижения счетчиком конечного значения (10) управление передается на следующий блок модификации 6 (рис. 7.4).
3. Блок операций над данными. Осуществляет присвоение переменной
val значения случайного сгенерированного числа (val = rand()), которое затем приводится к 4-битовой точности (val & 0b00001111) и сохраняется в
переменной val.
4. Блок проверки условия. Выполняет проверку на равенство нулю переменной val. В случае положительного результата проверки управление передается на блок операций над данными 5; если исход проверки отрицателен, то
управление незамедлительно переходит к блоку модификации 2 (рис. 7.4).
5. Блок операций над данными. Увеличивает значение переменной val
на единицу и далее присваивает полученное значение элементу массива
number (number[counter]), расположенного по адресу, равному переменной
counter. После этого управление передается на блок модификации 2.
6. Блок модификации (цикл 2). Задает цикл со счетчиком, при этом начальное значение счетчика (переменная i) равно 1, конечное array-1 (9), шаг изменения эквивалентен 1. В случае достижения счетчиком конечного значения (9)
управление передается на следующий блок модификации 13 (рис. 7.4).
7. Блок операций над данными. Присваивает переменной mas_number
значение элемента массива number (number[i]), расположенного по адресу,
равному переменной i. Переменная bol приравнивается к i.
8. Блок модификации (цикл 3). Задает цикл со счетчиком, где начальное
значение счетчика (переменная j) равно i+1, конечное – array (10), а шаг изменения эквивалентен 1. В результате достижения счетчиком конечного значения (10) управление передается на блок проверки условия 11 (рис. 7.4).
9. Блок проверки условия. Выполняет проверку, заключающуюся в том,
что величина, содержащаяся в переменной mas_number, больше по отношению к значению элемента массива number (number[j]), расположенного по адресу, равному переменной j. При положительном результате проверки управление передается на блок операций над данными 10; если исход проверки отрицателен, то управление вновь переходит к блоку модификации 8 (рис. 7.4).
10. Блок операций над данными. Присваивает переменной mas_number
значение элемента массива number (number[j]), расположенного по адресу,
равному переменной j. Переменная bol приравнивается к j. Затем управление передается на блок модификации 8.
70
Начало
A
val, counter, chet = 0
nochet = 0, chet_num[10]
nochet_num[10]
number[10], array = 10
1 mas_number, i, j, bol
B
Да
11
C
bol = i
Нет
number[bol] = number[i]
12 number[i] = mas_number
2 counter = 1, 10, 1
Цикл 1
val = rand()
val
=
val
& 0b00001111
3
13 counter = 1, 10, 1
Цикл 4
14 val = number[counter]
Нет
4
val = 0
Нет
Да
val++
number[counter]
= val
5
15 val % 2 = 0
16
6
i = 1, array-1, 1
17 counter = 1, 10, 1
Цикл 2
Цикл 5
mas_number = number[i]
bol = i
7
18 val = number[counter]
Да
Нет
8
j = i + 1, array, 1
9
number[j]
< mas_number
Да
chet++
19 val % 2 = 0
Цикл 3
chet -20 chet_num[chet] = val
nochet_num[nochet] = val
21
nochet++
Да
mas_number = number[j]
bol = j
10
22 number[counter] = 0
A
Нет
B
C
Конец
Рис. 7.4. Блок-схема алгоритма первого варианта решения
71
11. Блок проверки условия. Производит проверку на правильность равенства переменной bol значению, содержащемуся в переменной i. В случае положительного результата проверки управление передается на блок
модификации 6; если исход проверки отрицателен, то управление переходит к блоку операций над данными 12.
12. Блок операций над данными. Не только присваивает элементу массива number (number[bol]), расположенного по адресу, равному переменной bol, значение, содержащееся в массиве number (number[i]) по адресу,
равному переменной i, но и приравнивает элемент массива number
(number[i]), находящийся на позиции i, к переменной mas_number. Далее
управление передается на блок модификации 6.
13. Блок модификации (цикл 4). Задает цикл со счетчиком, где начальное
значение счетчика (переменная counter) равно 1, конечное 10, шаг изменения
эквивалентен 1. В случае достижения счетчиком конечного значения (10)
управление передается на следующий блок модификации 17 (рис. 7.4).
14. Блок операций над данными. Присваивает переменной val значение
элемента массива number (number[counter]), расположенного по адресу,
равному переменной counter.
15. Блок проверки условия. Выполняет проверку на правильность равенства нулю остатка от деления значения переменной val на число 2
(определение чётности либо нечётности числа). В случае положительного
результата проверки управление передается на блок операций над данными 16; если исход проверки отрицателен, то управление незамедлительно
переходит к блоку модификации 13 (рис. 7.4).
16. Блок операций над данными. Осуществляет увеличение переменной
chet на единицу. После этого управление передается на блок модификации 13.
17. Блок модификации (цикл 5). Задает цикл со счетчиком, где начальное значение счетчика (переменная counter) равно 1, конечное 10, шаг изменения эквивалентен 1. При достижении счетчиком конечного значения
(10) цикл и алгоритм в целом свою работу заканчивают.
18. Блок операций над данными. Присваивает переменной val значение
элемента массива number (number[counter]), расположенного по адресу,
равному переменной counter.
19. Блок проверки условия. Выполняет проверку на равенство нулю
остатка от деления значения переменной val на число 2. В случае положительного результата проверки управление передается на блок операций
над данными 20; если исход проверки отрицателен, то управление переходит к блоку операций над данными 21 (рис. 7.4).
72
20. Блок операций над данными. Уменьшает значение переменной chet
на единицу и далее присваивает элементу массива chet_num
(chet_num[chet]), расположенного по адресу, равному переменной chet,
значение переменной val. Затем управление передается на блок операций
над данными 22.
21. Блок операций над данными. Присваивает элементу массива
nochet_num (nochet_num[nochet]), расположенного по адресу, равному переменной nochet, значение переменной val, затем выполняет увеличение
значения переменной nochet на единицу. Далее управление передается на
блок операций над данными 22.
22. Блок операций над данными. Элементу массива number
(number[counter]), расположенного по адресу, равному переменной
counter, присваивает нулевое значение. После этого управление передается на блок модификации 17.
На основании приведенного алгоритма составляется программный код
на языке программирования С. Части кода, выполняющие аналогичные
функции блоков графического алгоритма (см. рис. 7.4), отмечены подобными цифрами:
void main (void)
{
int val;
char counter;
1
char chet = 0, nochet = 0;
char chet_num[10];
char nochet_num[10];
char number[10];
1
char array = 10;
int mas_number;
char i, j, bol;
2 – for (counter = 0; counter <= 9; counter++)
{
val = rand();
3
val = val & 0b00001111;
4 –
if (val == 0)
{
val++;
5
number[counter] = val;
73
}
6 –
7
8 –
9 –
10
11 –
12
}
for (i = 0; i < array - 1; i++)
{
mas_number = *(number + i);
bol = i;
for (j = i + 1; j < array; j++)
{
if (*(number + j) < mas_number)
{
mas_number = *(number + j);
bol = j;
}
}
if (bol != i)
{
number[bol] = *(number + i);
*(number + i) = mas_number;
}
}
13 – for (counter = 0; counter <= 9; counter++)
{
14 –
val = *(number + counter);
15–16 –
if (val % 2 == 0) chet++;
}
17 – for (counter = 0; counter <= 9; counter++)
{
18 –
val = *(number + counter);
19 –
if (val % 2 == 0)
{
chet--;
20
chet_num[chet] = val;
}
19 –
else
{
74
nochet_num[nochet] = val;
nochet++;
}
number[counter] = 0;
}
21
22 –
}
Приведем результаты выполнения программного кода со срезом значений
переменных в ключевых местах программы (циклы 1–5). По завершении циклов 1–5 переменные и массивы будут содержать следующие значения:
– инициализация: val = 0, counter = 0, chet = 0, nochet = 0, array = 10,
mas_number = 0, i = 0, j = 0, bol = 0, chet_num[10] = {0}, nochet_num[10] = {0},
number[10] = {0};
– цикл 1: val = 7, counter = 10, chet = 0, nochet = 0, array = 10,
mas_number = = 0, i = 0, j = 0, bol = 0, chet_num[10] = {0}, nochet_num[10] = {0},
number[10] = {6,15,12,13,2,11,8,9,14,7};
– цикл 2–3: val = 7, counter = 10, chet = 0, nochet = 0, array = 10,
mas_number = 14, i = 9, j = 10, bol = 8, chet_num[10] = {0}, nochet_num[10] = {0},
number[10] = {2,6,7,8,9,11,12,13,14,15};
– цикл 4: val = 15, counter = 10, chet = 5, nochet = 0, array = 10,
mas_number = 14, i = 9, j = 10, bol = 8, chet_num[10] = {0}, nochet_num[10] =
= {0}, number[10] = {2,6,7,8,9,11,12,13,14,15};
– цикл 5: val = 15, counter = 10, chet = 0, nochet = 5, array = 10,
mas_number = 14, i = 9, j = 10, bol = 8, chet_num[10] =
= {14,12,8,6,2,0,0,0,0,0}, nochet_num[10] = {7,9,11,13,15,0,0,0,0,0},
number[10] = {0}.
Блок-схема алгоритма второго варианта, решающего поставленную задачу, приведена на рис. 7.5.
Рассмотрим назначение каждого блока.
1. Блок операций ввода и вывода данных. Объявляет переменные: val,
counter, counter_max, pozic, max, chet = 0, nochet = 0 и массивы –
chet_num[10], nochet_num[10], number[10].
Переменные:
– val, counter, counter_max, pozic и max, которые используются как
вспомогательные;
– chet и nochet применяются для хранения промежуточных значений
чётных и нечётных чисел соответственно.
75
Начало
A
val, counter, counter_max
pozic, max, chet = 0
nochet = 0, chet_num[10]
nochet_num[10]
number[10]
1
10 counter = 1, 10, 1
Цикл 3
max = number[0]
pozic = 0
11
12 counter_max = 1, 9, 1
Цикл 4
2 counter = 1, 10, 1
Цикл 1
val = number
13 [counter_max + 1]
val = rand()
3 val = val & 0b00001111
Нет
Нет
4
14 max < val
val = 0
Да
15
max = val
pozic = counter_max + 1
Да
val++
5 number[counter] = val
Да
16 max % 2 = 0
6 counter = 1, 10, 1
Нет
Цикл 2
chet_num[chet] = max
17
chet++
7 val = number[counter]
Нет
nochet-18
nochet_num[nochet] = max
8 val % 2 ≠ 0
9
Да
nochet++
19
number[pozic] = 0
Конец
A
Рис. 7.5. Блок-схема алгоритма второго варианта решения
76
Массивы:
– chet_num[10] и nochet_num[10] предназначены для хранения четных
и нечетных сгенерированных случайных чисел соответственно;
– number[10] используется как вспомогательный массив для временного хранения сгенерированных случайных чисел.
2–5. Блоки алгоритма. Выполняют аналогичные функции так же, как и
в первом варианте решения задачи (см. рис. 7.4).
6. Блок модификации (цикл 2). Задает цикл со счетчиком, где начальное
значение счетчика (переменная counter) равно 1, конечное 10, шаг изменения
эквивалентен 1. В случае достижения счетчиком конечного значения 10
управление передается на следующий блок модификации 10 (рис. 7.5).
7. Блок операций над данными. Присваивает переменной val значение
элемента массива number (number[counter]), расположенного по адресу,
равному переменной counter.
8. Блок проверки условия. Выполняет проверку правильности на неравенство нулю остатка от деления значения переменной val на число 2
(определение чётности либо нечётности числа). В случае положительного
результата проверки управление передается на блок операций над данными 9; если исход проверки отрицателен, то управление незамедлительно
переходит к блоку модификации 6 (рис. 7.5).
9. Блок операций над данными. Осуществляет увеличение переменной
nochet на единицу. После этого управление передается на блок модификации 6.
10. Блок модификации (цикл 3). Задает цикл со счетчиком, где начальное
значение счетчика (переменная counter) равно 1, конечное 10, шаг изменения
эквивалентен 1. При достижении счетчиком конечного значения 10 цикл и
алгоритм в целом будут выполнены.
11. Блок операций над данными. Приравнивает переменную pozic к нулю и присваивает переменной max значение элемента массива number
(number[0]), расположенного по нулевому адресу.
12. Блок модификации (цикл 4). Задает цикл со счетчиком, где начальное
значение счетчика (переменная counter_max) равно 1, конечное 9, шаг изменения эквивалентен 1. В случае достижения счетчиком конечного значения 9
управление передается на блок проверки условия 16 (рис. 7.5).
13. Блок операций над данными. Присваивает переменной val значение
элемента массива number (number[counter_max + 1]), расположенного по
адресу, равному переменной counter_max + 1.
14. Блок проверки условия. Выполняет проверку, заключающуюся в том,
что величина, содержащаяся в переменной max, меньше по отношению к значению переменной val. При положительном результате проверки управление
77
передается на блок операций над данными 15; если исход проверки отрицателен, то управление вновь переходит к блоку модификации 12 (см. рис. 7.5).
15. Блок операций над данными. Приравнивает переменную max к val.
Переменной pozic присваивается значение counter_max, увеличенное на
единицу.
16. Блок проверки условия. Выполняет проверку на равенство нулю
остатка от деления значения переменной max на число 2. В случае положительного результата проверки управление передается на блок операций
над данными 17; если исход проверки отрицателен, то управление переходит к блоку операций над данными 18 (см. рис. 7.5).
17. Блок операций над данными. Присваивается элементу массива
chet_num (chet_num[chet]), расположенного по адресу, равному переменной chet, значение переменной max, затем выполняется увеличение значения переменной chet на единицу. Далее управление передается на блок
операций над данными 19.
18. Блок операций над данными. Уменьшает значение переменной
nochet на единицу и далее присваивает элементу массива nochet_num
(nochet_num[nochet]), расположенного по адресу, равному переменной
nochet, значение переменной max. Затем управление передается на блок
операций над данными 19.
19. Блок операций над данными. Элементу массива number
(number[pozic]), расположенного по адресу, равному переменной pozic,
присваивается нулевое значение. Затем управление передается на блок
модификации 10.
На основании приведенного алгоритма составляется программный код
на языке программирования С. Части кода, выполняющие аналогичные
функции блоков графического алгоритма (см. рис. 7.5), отмечены подобными цифрами:
void main (void)
{
int val;
char counter;
char counter_max;
char pozic;
char max;
1
char chet = 0, nochet = 0;
char chet_num[10];
char nochet_num[10];
char number[10];
78
2 – for (counter = 0; counter <= 9; counter++)
{
val = rand();
3
val = val & 0b00001111;
4 –
if (val == 0)
{
val++;
5
number[counter] = val;
}
}
6 – for (counter = 0; counter <= 9; counter++)
{
7 –
val = *(number + counter);
8–9 –
if (val % 2 != 0) nochet++;
}
10 – for (counter = 0; counter <= 9; counter++)
{
max = number[0];
11
pozic = 0;
12 –
for (counter_max = 0; counter_max <= 8; counter_max++)
{
13 –
val = *(number + counter_max + 1);
14 –
if (max < val)
{
max = val;
15
pozic = counter_max + 1;
}
}
16 –
if (max % 2 == 0)
{
chet_num[chet] = max;
17
chet++;
}
16 –
else
{
79
nochet--;
nochet_num[nochet] = max;
}
number[pozic] = 0;
}
18
19 –
}
Далее приведем результаты выполнения программного кода со срезом
значений переменных в ключевых местах программы (циклы 1–4). По завершении циклов 1–4 переменные и массивы будут содержать следующие
значения:
– инициализация: val = 0, counter = 0, counter_max = 0, pozic = 0, max = 0,
chet = 0, nochet = 0, chet_num[10] = {0}, nochet_num[10] = {0}, number[10] = {0};
– цикл 1: val = 7, counter = 10, counter_max = 0, pozic = 0, max = 0, chet = 0,
nochet = 0, chet_num[10] = {0}, nochet_num[10] = {0}, number[10] =
= {6,15,12,13,2,11,8,9,14,7};
– цикл 2: val = 7, counter = 10, counter_max = 0, pozic = 0, max = 0, chet = 0,
nochet = 5, chet_num[10] = {0}, nochet_num[10] = {0}, number[10] =
= {6,15,12,13,2,11,8,9,14,7};
– цикл 3–4: val = 0, counter = 10, counter_max = 9, pozic = 4, max = 2,
chet = 5, nochet = 0, chet_num[10] = {14,12,8,6,2,0,0,0,0,0}, nochet_num[10] =
= {7,9,11,13,15,0,0,0,0,0}, number[10] = {0}.
Итак, выполнение обоих вариантов программного кода позволило получить одинаковые ответы: chet_num[10] = {14,12,8,6,2,0,0,0,0,0},
nochet_num[10] = {7,9,11,13,15,0,0,0,0,0}, что подтверждает правильность
обоих решений.
Приведем основные параметры обоих вариантов решения поставленной задачи.
Вариант1:
– используется 9 переменных;
– блок-схема алгоритма программы состоит из 22 блоков;
– программный код занимает 54 строки;
– время выполнения программного кода составляет 2,32 мс (микроконтроллер PIC18F452 при тактовой частоте кварцевого резонатора 20 МГц).
Вариант 2:
– используется 7 переменных;
– блок-схема алгоритма программы состоит из 19 блоков;
– программный код занимает 46 строк;
80
– время выполнения программного кода составляет 2,44 мс (микроконтроллер PIC18F452 при тактовой частоте кварцевого резонатора 20 МГц).
На основании приведенных данных можно заключить, что, несмотря
на бо́льший размер программного кода первого варианта решения задачи,
время его выполнения меньше на 0,12 мс, более того, из двадцати двух блоков схемы алгоритма семь из них (с шестого по двенадцатый) (см. рис. 7.4)
были заимствованы с алгоритма решения задачи третьего примера
(см. рис. 6.2), что значительно упростило составление алгоритма и написание программного кода в целом.
Таким образом, когда величина программного кода не имеет значения,
а время его выполнения ограничено, то наиболее приемлемым является
первый вариант решения. Второй вариант решения целесообразен в том
случае, если время исполнения программы не лимитировано, а ее объем
задан определенными рамками.
81
ЗАКЛЮЧЕНИЕ
Современная микропроцессорная техника непрерывно развивается.
Разрабатываются новые типы микропроцессоров, совершенствуются методы их программирования.
Язык высокого уровня С существует и активно используется уже более
45 лет, при этом он постоянно дорабатываясь и совершенствуясь, а продолжающаяся стандартизация доказывает его популярность среди разработчиков программного обеспечения.
Согласно данным (на май 2018 г.), индекс TIOBE, который измеряет
рост популярности языков программирования, показал, что язык высокого
уровня C занимает 2-е место среди двадцатки самых распространенных
языков, уступая первенство лишь языку Java.
В настоящем учебном пособии рассматриваемые теоретическое описание языка высокого уровня С и ряд примеров решения конкретных задач с
помощью различных средств и методов С окажут существенную помощь
студенту в понимании, освоении и закреплении знаний и навыков программирования микропроцессорных систем.
Знание теории дисциплины «Основы микропроцессорной техники»
обязательно при выполнении лабораторных работ, суть которых заключается в написании программ на языке высокого уровня С для микроконтроллеров PIC18. Следовательно, язык С является основным инструментом без которого невозможно изучить и освить не только указанную
дисциплину, но в том числе и «Микропроцессорные информационноуправляющие системы».
82
БИБЛИОГРАФИЧЕСКИЙ СПИСОК
1. Уэит, М. Язык Си: руководство для начинающих / М. Уэит, С. Прата,
Д. Мартин; пер. с англ. Л.Н. Горинович, В.С. Явнилович. – М. : Мир, 1988.
– 512 с.
2. Катцен, Сид. PIC-микроконтроллеры. Всё, что необходимо знать
/ С. Катцен; пер. с англ. А.В. Евстафьева – М. : Додэка-XXI, 2008. – 656 с.
3. Тавернье, К. PIC-микроконтроллеры: практика применения / К. Тавернье ; пер. с фр. – М. : ДМК Пресс, 2002. – 272 с.
4. Брей, Б. Применение микроконтроллеров PIC18: архитектура, программирование и построение интерфейсов с применением С и ассемблера
/ Б. Брей ; пер. с англ. – СПб. : КОРОНА-ВЕК, 2008. – 576 с.
5. Уилмсхерст, Т. Разработка встроенных систем с помощью микроконтроллеров PIC: принципы и практические примеры / Т. Уилмсхерст ; пер.
с англ. – СПб. : КОРОНА-ВЕК, 2008. – 544 с.
6. Шпак, Ю.А. Программирование на языке С для AVR и PIC микроконтроллеров. – 2-е изд., перераб. и доп. / сост. Ю.А. Шпак – СПб. :
КОРОНА-ВЕК, 2011. – 544 с.
83
ПРИЛОЖЕНИЕ 1
ОПЕРАТОРЫ ЯЗЫКА С [5]
Приоритетность
и порядок выполнения
Операция
Символ
Пример
Скобки и оператор доступа к элементам массива
1, слева направо Вызов функции
( )
sqr( )
1, слева направо Индексы
[ ]
array[ ]
1, слева направо Указатель на поле
–>
X –> Y
Арифметические
4, слева направо Сложение
+
Z=X+Y
4, слева направо Вычитание
–
Z=X–Y
3, слева направо Умножение
*
Z=X*Y
3, слева направо Деление
/
Z=X/Y
Z=X%Y
Остаток от деления (деле3, слева направо
%
(только целые
ние по модулю)
типы)
Сравнения
6, слева направо Больше
>
while (X > Y)
6, слева направо Больше или равно
>=
while (X >= Y)
6, слева направо Меньше
<
while (X < Y)
6, слева направо Меньше или равно
<=
while (X <= Y)
7, слева направо Равно
==
while (X == Y)
7, слева направо Не равно
!=
while (X != Y)
Логические
«И» (1, если X и Y не рав11, слева направо
&&
X && Y
ны 0)
«ИЛИ» (1, если X или Y
12, слева направо
||
X || Y
не равны 0)
2, справа налево «НЕ» (1, если X = 0)
!
!X
Поразрядные
8, слева направо Побитовое «И»
&
X&Y
10, слева направо Побитовое «ИЛИ»
|
X|Y
Побитовое исключающее
9, слева направо
^
X^Y
«ИЛИ»
Дополнение до единицы
~X
2, слева направо
~
(побитовое «НЕ»)
84
Продолжение прил. 1
Приоритетность
и порядок выполнения
Операция
Символ
Пример
Сдвиг вправо, X сдвигаетZ = X >> Y
>>
ся вправо Y раз
Сдвиг влево, X сдвигается
Z = X << Y
5, слева направо
<<
влево Y раз
Z = 0b00000000; Z = 120 >> 2; → Z =
0b00011110;
Z = 0b00000000; Z = 240 >> 4; → Z =
Пример сдвига вправо:
0b00001111;
Z = 0b00000000; Z = 84 >> 6; → Z =
0b00000001;
Z = 0b00000000; Z = 1 << 5; → Z = 0b00100000;
Пример сдвига влево:
Z = 0b00000000; Z = 5 << 3; → Z = 0b00101000;
Z = 0b00000000; Z = 7 << 4; → Z = 0b01110000;
Присваивание (ассоциативность)
14, справа налево Присваивание
=
X=Y
Сложение с присваиваниX += Y
14, справа налево
+=
ем
(X = X + Y)
Вычитание
X –= Y
14, справа налево
–=
с присваиванием
(X = X – Y)
Умножение
X *= Y
14, справа налево
*=
с присваиванием
(X = X *Y)
Деление с присваиванием
X /=Y
14, справа налево
/=
(X = X / Y)
Вычисление остатка
X %= Y
14, справа налево
%=
с присваиванием
(X = X % Y)
Побитовое «И»
X &= Y
14, справа налево
&=
с присваиванием
(X = X & Y)
Побитовое «ИЛИ»
X |= Y
14, справа налево
|=
с присваиванием
(X = X | Y)
Побитовое исключающее
X ~= Y
14, справа налево
~=
«ИЛИ» с присваиванием
(X = X ~ Y)
Сдвиг вправо
X >>= Y
14, справа налево
>>=
с присваиванием
(X = X >> Y)
Сдвиг влево
X <<= Y
14, справа налево
<<=
с присваиванием
(X = X << Y)
5, слева направо
85
Окончание прил. 1
Приоритетность
и порядок выполнения
Операция
Символ
Пример
Операторы инкрементирования и декрементирования
2, справа налево Преикремент
++
++ X
2, справа налево Предекремент
––
–– X
2, справа налево Пост-икремент
++
X ++
2, справа налево Пост-декремент
––
X ––
Операторы «интерпретации данных»
Объект или функция,
2, справа налево
*
*X
на которые указывает X
2, справа налево Адрес X
&
& адрес
Приведение типа для зна2, справа налево
(тип) (long) X
чения X скалярного типа
2, справа налево Размер Х в байтах
sizeof sizeof X
86
ПРИЛОЖЕНИЕ 2
БИБЛИОТЕКА ОСНОВНЫХ СТАНДАРТНЫХ
МАТЕМАТИЧЕСКИХ ФУНКЦИЙ [6]
Функция acos
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
math.h
float acos(float X)
Возвращает арккосинус вещественного числа Х (Х – в диапазоне от –1 до 1). Результат – в диапазоне от 0 до π
float val;
val = acos(0.762); //Результат: val = 0.7044
Функция asin
math.h
float asin(float X)
Возвращает арксинус вещественного числа Х (Х – в диапазоне от –1 до 1). Результат – в диапазоне от –π/2 до π/2
float val1, val2;
val1 = asin(-1); //Результат: val1 = -1.57079
val2 = asin(1); //Результат: val2 = 1.57079
Функция atan
math.h
float atan(float X)
Возвращает арктангенс вещественного числа Х (Х – в диапазоне от –1 до 1). Результат – в диапазоне от –π/2 до π/2
float val;
val = atan(1); //Результат: val = 0.785398
Функция atan2
math.h
float atan2(float Y, float X)
Возвращает арктангенс вещественного числа Y/Х.
Результат лежит в диапазоне от –π до π
float val;
val = atan2(3.3, 4.5); //Результат: val = 0.632748
Функция ceil
math.h
float ceil(float X)
87
Продолжение прил. 2
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Возвращает значение Х, округленное до ближайшего целого
float val;
val = ceil(3.57); //Результат: val = 4.00
Функция cosh
math.h
float cosh(float X)
Возвращает гиперболический косинус угла Х, представленного в радианах
float val;
val = cosh(5.231); //Результат: val = 93.49251
Функция exp
math.h
float exp(float X)
Возвращает значение еХ
float val;
val = exp(2.3); //Результат: val = 9.97418
Функция fabs
math.h
float fabs(float X)
Возвращает модуль вещественного числа Х
float val1 = 2.4, val2 = 3.6, res;
res = fabs(val1 - val2); //Результат: res =1.2
Функция floor
math.h
float floor(float X)
Возвращает целую часть вещественного числа Х
float val;
val = floor(4.567); //Результат: val = 4.000
Функция fmod
math.h
float fmod(float X, float Y)
Возвращает остаток от деления вещественного числа Х
на вещественное число Y
float val;
val = fmod(25.6, 8); //Результат: val = 1.600
88
Продолжение прил. 2
Функция log
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
math.h
float log(float X)
Возвращает натуральный логарифм числа Х. Если Х <= 0,
возникает ошибка
float val;
val = log(5); //Результат: val = 1.609438
Функция log10
math.h
float log10(float X)
Возвращает логарифм числа Х по основанию 10. Если
Х <= 0, то возникает ошибка
float val;
val = log10(5); //Результат: val = 0.69897
Функция modf
math.h
float modf(float X, float *IPART)
Разбивает число Х на целую и дробную части. Дробная
часть возвращается как число со знаком, а целая сохраняется в параметре-переменной IPART
float int_part, fract_part;
fract_part = modf(-45.7, &int_part);
//Результат: fract_part = -0.7; int_part = -45.00
Функция pow
math.h
float pow(float X, float Y)
Возвращает число Х, возведенное в степень Y
float val;
val = pow(2, 3); //Результат: val = 8
Функция sin
math.h
float sin(float X)
Возвращает синус угла Х, представленного в радианах
float val;
val = sin(5.121); //Результат: val = -0.917673
89
Продолжение прил. 2
Функция sinh
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
Библиотека
Заголовок
Описание
Пример использования
math.h
float sinh(float X)
Возвращает гиперболический синус угла Х, представленного в радианах
float val;
val = sinh(5.12); //Результат: val = 83.66468
Функция sqrt
math.h
float sqrt(float X)
Возвращает квадратный корень положительного числа Х.
Для отрицательного Х результат не определен
float val;
val = sqrt(6.4); //Результат: val = 2.529822
Функция tan
math.h
float tan(float X)
Возвращает тангенс угла Х, представленного в радианах
float val;
val = tan(5.121); //Результат: val = -2.309572
Функция tanh
math.h
float tanh(float X)
Возвращает гиперболический тангенс угла Х, представленного в радианах
float val;
val = tanh(5.121); //Результат: val = 0.999928
Функция rand
stdlib.h
int rand(void)
Возвращает псевдослучайное число в диапазоне от 0 до
значения RAND_MAX. Начальное число для генерации
псевдослучайной последовательности задается с помощью функции srand(). По умолчанию это число равно 1
int val;
val = rand(); //Результат: val = 17718
90
Окончание прил. 2
Функция srand
Библиотека
Заголовок
Описание
Пример использования
stdlib.h
void srand(unsigned int seed)
Устанавливает начальное число для генерации псевдослучайной последовательности
int val = 5;
srand(val);
val = rand(); //Результат: val = 23050
ПРИЛОЖЕНИЕ 3
ТИПЫ ДАННЫХ ЯЗЫКА С [5]
Описание
Длина,
байт
char
signed char
unsigned char
int
unsigned int
short
unsigned short
short long
unsigned short
long
Символ
Символ
Символ
Целое число
Целое число
Целое число
Целое число
Целое число
1
1
1
2
2
2
2
3
–128...+127
–128...+127
0...255
–32768...+32767
0...65535
–32768...+32767
0...65535
–8 388 608...+8 388 607
Целое число
3
0...16 777 215
long
Целое число
4
Целое число
Число с плавающей
запятой
Число с плавающей
запятой, двойная
точность
4
–2 147 483 648...
...+2 147 483 647
0...4 294 967 295
4
3,402×10–38...3,402×1038
8
1,7×10–308...1,7×10308
Тип данных
unsigned long
float
double
91
Диапазон значений
ОГЛАВЛЕНИЕ
ВВЕДЕНИЕ ......................................................................................................... 3
1. ВВОДНЫЕ ПОНЯТИЯ .................................................................................. 6
1.1. Комментарии, идентификаторы и ключевые слова ............................ 6
1.2. Литералы и операторы ........................................................................... 6
Вопросы для самоконтроля ............................................................................... 7
2. СТРУКТУРА ПРОГРАММЫ НА ЯЗЫКЕ С ............................................... 8
Вопросы для самоконтроля ............................................................................. 10
3. ТИПЫ ДАННЫХ, ПЕРЕМЕННЫЕ, КОНСТАНТЫ ................................ 11
Вопросы для самоконтроля ............................................................................. 13
4. ОСНОВНЫЕ ОПЕРАТОРЫ ЯЗЫКА С ..................................................... 13
4.1. Оператор «выражение» ........................................................................ 14
4.2. Пустой оператор ................................................................................... 14
4.3. Составной оператор .............................................................................. 14
4.4. Оператор if-else ..................................................................................... 15
4.5. Оператор switch-case ............................................................................ 17
4.6. Оператор for .......................................................................................... 19
4.7. Оператор while ...................................................................................... 21
4.8. Оператор do-while ................................................................................. 22
4.9. Операторы break и continue ................................................................. 23
4.10. Оператор goto ...................................................................................... 24
4.11. Оператор return.................................................................................... 24
Вопросы для самоконтроля ............................................................................. 25
5. УКАЗАТЕЛИ, МАССИВЫ И СТРОКИ .................................................... 26
Вопросы для самоконтроля ............................................................................. 31
6. ПРИМЕРЫ ПРОГРАММНЫХ КОДОВ .................................................... 31
7. ПРИМЕРЫ ВЫПОЛНЕНИЯ ПРОГРАММНЫХ КОДОВ
С ПРИМЕНЕНИЕМ БЛОК-СХЕМ............................................................. 61
ЗАКЛЮЧЕНИЕ ................................................................................................ 82
БИБЛИОГРАФИЧЕСКИЙ СПИСОК ............................................................ 83
ПРИЛОЖЕНИЕ 1. ОПЕРАТОРЫ ЯЗЫКА С ................................................ 84
ПРИЛОЖЕНИЕ 2. БИБЛИОТЕКА ОСНОВНЫХ СТАНДАРТНЫХ
МАТЕМАТИЧЕСКИХ ФУНКЦИЙ ............................................................... 87
ПРИЛОЖЕНИЕ 3. ТИПЫ ДАННЫХ ЯЗЫКА С .......................................... 91
92
Download