Тема 1. Алгоритмы и программы
Тема 1. Алгоритмы и программы
Цели и задачи изучения темы
В данной теме рассматриваются понятие алгоритма, программы, способы записи алгоритмов, критерии качества программ, типы языков программирования и принципы структурного программирования.
Понятие алгоритма. Понятие программы. Способы записи алгоритмов.
Понятие алгоритма - одно из основных понятий программирования и математики. Алгоритм - это последовательность команд, предназначенная исполнителю, в результате выполнения которой он должен решить поставленную задачу.
Алгоритм записывается на формальном языке, исключающем неоднозначность толкования. Исполнитель - это человек, компьютер, автоматическое устройство и т.п. Он должен уметь выполнять все команды, составляющие алгоритм.
Алгоритмы можно описывать на естественном языке. Но при использовании некоторого формального языка исчезает неоднозначность, появляются краткость и ясность изложения.
К формальным языкам относятся все языки программирования. Язык программирования это формальная знаковая система, предназначенная для записи компьютерных программ. Язык программирования определяет набор лексических, синтаксических и семантических правил, задающих внешний вид программы и действия, которые выполнит вычислительное устройство (исполнитель) под ее управлением.
Различие между компьютерной программой и алгоритмом заключается в том, что при упоминании алгоритма, как правило, имеют в виду основную идею его построения, общую для всех языков программирования. Программа же всегда связана с записью алгоритма на конкретном языке программирования.
Другими словами алгоритм - это метод или схема решения задачи, а программа - это конкретная реализация алгоритма, которая может быть выполнена на компьютере. Алгоритм, в свою очередь, является реализацией идеи решения. Это можно проиллюстрировать следующей схемой:
Идея решения → Алгоритм → Программа.
Стрелка означает переход к следующему этапу решения задачи с повышением уровня детализации.
Большинство языков программирования имеют общие черты. Поэтому не всегда целесообразно пользоваться каким-либо конкретным языком программирования и загромождать изложение несущественными деталями. Здесь мы будем использовать язык блок-схем.
Критерии качества программ
Можно выделить целый ряд критериев качества программы. Вот некоторые из наиболее существенных критериев:
· Корректность – программа должна работать правильно при любых исходных данных.
· Эффективность – программа должна использовать, по возможности, минимальное количество ресурсов (память, время и др.).
· Эргономичность – программа должна быть удобной для пользователя.
· Читабельность – текст программы должен быть понятен для программиста.
· Портируемость - программа после незначительных изменений способна работать на компьютерной платформе отличной от исходной платформы.
Вопросы для повторения
- Понятие алгоритма
- Понятие языка программирования
- Понятие программы
- Критерии качества программ (корректность, эффективность, эргономичность, читабельность, портируемость)
- Понятие низкоуровневого языка программирования
- Понятие высокоуровневого языка программирования
- Принципы структурного программирования.
Резюме по теме
В данной теме рассмотрены некоторые понятия программирования и принципы структурного программирования.
Тема 2. Характеристика языка Си
Цели и задачи изучения темы
В данной теме рассматриваются история развития и основные особенности языка Си.
История языка Си
Язык программирования Си был разработан и реализован в 1972 году сотрудником фирмы AT&T Bell Laboratories Денисом Ритчи. Основным отличием языка Си от его прототипов является введение в Си типов данных. Язык Си был разработан во время создания операционной системы UNIX.
В 1983 году Американский национальный институт стандартов (ANSI) приступил к созданию стандарта Cи. В 1989 году стандарт был завершён и утверждён как ANSI X3.159-1989 «Язык программирования C». Именно эту версию часто называют «ANSI C», или «C89».
В 1990 году, стандарт ANSI C (с небольшими изменениями) был принят Международной организацией по стандартизации (ISO) как ISO/IEC 9899:1990. Эту версию иногда называют C90. Однако термины C89 и C90 относятся, в сущности, к одному языку.
В 2000 году ANSI принял стандарт ISO/IEC 9899:1999. Этот стандарт обычно называют C99. Это и есть современный стандарт языка программирования C.
ANSI C на сегодняшний день поддерживается практически всеми распространёнными компиляторами языка Си. Любая программа, написанная только с использованием стандарта и не допускающая специфических аппаратных допущений, должна работать на любой платформе.
Достоинства языка Си
Язык Си компактен, является относительно маленьким языком программирования. Ввод-вывод не считается частью языка Си, а определяется стандартной библиотекой. Язык Си удалось сделать относительно маленьким языком программирования за счет того, что в его состав не были включены ввод-вывод и средства для работы со строками. Язык Си был задуман настолько гибким, что эти возможности могли быть реализованы в каждом конкретном случае наиболее удачным образом. Практический опыт использования языка Си показал правильность такого подхода. Большая часть операционной системы UNIX и все утилиты этой операционной системы реализованы на языке Си.
Си - удобный язык. Он достаточно структурирован, чтобы поддерживать хороший стиль программирования, и вместе с тем не связывает ограничениями. Си включает в себя те управляющие конструкции, которые рекомендуются теоретическим и практическим программированием. Его структура побуждает программиста использовать в своей работе нисходящее проектирование, структурное программирование и пошаговую разработку модулей. Результатом такого подхода является надежная и читаемая программа.
Си - эффективный язык. Его структура позволяет наилучшим образом использовать возможности современных ЭВМ. Написанные на языке Си программы обычно отличаются компактностью и быстротой исполнения.
Си - переносимый (или мобильный) язык. Это означает, что программа, написанная на Си для одной вычислительной системы, может быть перенесена с небольшими изменениями или вообще без них, на другую.
Многие фирмы, производящие программное обеспечение, используют Си для реализации своих проектов. Языки программирования как С++, Java, С# и др. имеют семантику схожую с семантикой Си. Также широко Си используется для обучения программированию. Язык Си быстро становится одним из наиболее важных и популярных языков программирования.
Сильная типизация
Язык Си можно отнести к языкам программирования, поддерживающих сильную типизацию. Язык программирования является языком программирования с сильной типизацией, если:
· каждый объект в этом языке программирования принадлежит точно одному из существующих в этом языке программирования типу данных;
· преобразование типов осуществляется только путем преобразования значения из одного типа в другой;
· преобразование типов не производится путем трактовки представления значения как данных различных типов.
Практика программирования показала, что языки программирования с сильной типизацией способствуют увеличению ясности и надежности программ.
В языке Си допускается неявное преобразование типов. Однако транслятор с языка Си выводит предупреждение о каждом встретившимся в программе случае неявного преобразования типов.
Структура простой программы
Программа на языке Си состоит из одной или более функций, причем какая-нибудь из них (главная) обязательно должна называться main(). Описание функции состоит из заголовка и тела. Заголовок состоит из директив препроцессора типа #include и имени функции. Отличительным признаком имени функции служат круглые скобки, при этом аргумент может отсутствовать. Тело функции заключено в фигурные скобки и представляет собой набор операторов, каждый из которых оканчивается символом "точка с запятой".
Пример простой программы осуществляющий перевод расстояния в метрах в расстояние в морских саженях и футах представлен ниже.
/*
Пример 1
Осуществляет перевод метров в морские сажени и футы.
1 морская сажень = 1.83 м;
1 фут = 30.5 см.
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
float m,ft,fm;
setbuf(stdout, NULL);
printf("Введите расстояние в метрах: ");
scanf("%f",&m);
ft=m/0.305;
fm=m/1.83;
printf("В %f метров = %f морских саженей или %f футов.",m,fm,ft);
return EXIT_SUCCESS;
}
Данная программа состоит из единственной функции main. Перевод метров в морские сажени и футы имеет смысл оформить в виде отдельных функций. В этом случае текст программы будет следующий:
/*
Пример 2
Осуществляет перевод метров в морские сажени и футы.
*/
#include <stdio.h>
#include <stdlib.h>
float m_to_fm(float m){ // Переводит метры в морские сажени
return m/1.83; // 1 морская сажень = 1.83 м;
}
float m_to_ft(float m){ // Переводит метры в футы
return m/0.305; // 1 фут = 30.5 см.
}
int main(void) {
float m;
setbuf(stdout, NULL);
printf("Введите расстояние в метрах: ");
scanf("%f",&m);
printf("В %f метров = %f морских саженей или %f футов.",m,m_to_fm(m),m_to_ft(m));
return EXIT_SUCCESS;
}
Если программа (проект) большая, то она может состоять из нескольких файлов. Файлы, содержащие тексты Си-программы, называются исходными.
В языке Си исходные файлы бывают двух типов:
· заголовочные, или h-файлы;
· файлы реализации, или Cи-файлы.
Имена заголовочных файлов имеют расширение ".h". Имена файлов реализации имеют расширения ".c".
Заголовочные файлы содержат только описания (прототипы функций, имена и типы внешних переменных, константы, новые типы и т.п.). Другими словами h-файлы содержат лишь информацию о программах. Файлы реализации содержат сами программы.
Файлы реализации могут подключать описания, содержащиеся в заголовочных файлах. Сами заголовочные файлы также могут использовать другие заголовочные файлы.
Заголовочный файл подключается с помощью директивы препроцессора #include.
Препроцессор - это программа предварительной обработки текста непосредственно перед трансляцией. Препроцессор, как правило, является частью компилятора.
В рассмотренных примерах директивы #include <stdio.h> и #include <stdlib.h> были подключены заголовочные файлы стандартных библиотек. Если h-файл является частью стандартной Си-библиотеки и расположен в одном из системных каталогов, то его имя записывается в угловых скобках. Имена h-файлов, созданных самим программистом в рамках разрабатываемого проекта и расположенных в текущем каталоге, указываются в двойных кавычках, например, #include "abcd.h".
Вопросы для повторения
1. Стандарты языка Си (С89 или ANSI C, С90, С99)
2. Понятие интерпретатора
3. Достоинства и недостатки интерпретаторов
4. Понятие компилятора
5. Достоинства и недостатки компиляторов
6. Понятие интерпретатора компилирующего типа.
7. Достоинства и недостатки интерпретаторов компилирующего типа.
8. К какому классу относится язык Си?
9. Понятие интегрированной среды разработки программ
10. Состав типовой интегрированной среды разработки программ
11. Понятие языка программирования с сильной типизацией
12. Преимущества языков программирования с сильной типизацией
13. Структура Си программы
14. Назначение заголовочных файлов и файлов реализации.
15. Понятие препроцессора. Назначение препроцессора.
Резюме по теме
В данной теме рассмотрены история развития и основные особенности языка Си.
Тема 3. Основы языка Си
Цели и задачи изучения темы
В данной теме рассматриваются основы языка Си.
Константы и переменные
Некоторые данные устанавливаются равными определенным значениям еще до того, как программа начинает выполняться, а после ее запуска такие значения сохраняются неизменными на всем протяжении работы программ. Эти данные называются константами.
Для задания символических имен констант в Си очень часто используется директива препроцессора #define. Так, строка #define PI 3.14159265 задает символическое имя PI для константы 3.14159265. После этого имя PI можно использовать вместо числового значения. Препроцессор находит все вхождения слова PI в текст и заменяет их на константу.
Данные, которые могут изменяться, или же им могут быть присвоены значения во время выполнения программы, называются переменными.
Основные типы данных
Кроме различия между переменными и константами существует еще различие между типами данных. Некоторые данные в программе являются числами, некоторые - символами. Компилятор должен уметь идентифицировать и обрабатывать данные любого типа.
Термины тип данных (или просто тип) переменной обозначает множество значений, которые может принимать эта переменная. Набор базовых типов данных отличается в различных языках.
В языке Си предусмотрено использование нескольких основных типов данных. Если величина есть константа, то компилятор может распознать ее тип только по тому виду, в котором она присутствует в программе. В случае переменной необходимо, чтобы ее тип был объявлен в операторе описания. В стандарте языка Си используется семь ключевых слов, указывающих на различные типы данных:
· int
· long
· short
· unsigned
· char
· float
· double
Первые четыре ключевых слова используются для представления целых чисел. Если требуются только целые неотрицательные значения, то использовать ключевое слово unsigned перед соответствующим ключевым словом, например, unsigned int.
float, double используются для представления чисел с десятичной точкой.
При описании переменной сначала указывается тип, затем - имя переменной или список имен, разделенных запятыми, например,
int x;
int y, z, t;
При описании переменных можно присваивать им начальные значения:
int maxind = 1000;
int a = 5, b = 7;
Тип также определяет, сколько памяти занимает переменная этого типа. Для различных ЭВМ число байт памяти отводимое под переменные одного типа может различаться. Для определения того числа служит функция sizeof(). Пример использования данной функции:
/*
Пример 3
Определяет сколько байт памяти занимают переменные
различных типов
*/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("int - %d ,байт \n",sizeof(int));
printf("long - %d ,байт \n",sizeof(long));
printf("short - %d ,байт \n",sizeof(short));
printf("unsigned - %d ,байт \n",sizeof(unsigned));
printf("char - %d ,байт \n",sizeof(char));
printf("float - %d ,байт \n",sizeof(float));
printf("double - %d ,байт \n",sizeof(double));
return EXIT_SUCCESS;
}
Резулитат выполнения программы:
int - 4 ,байт
long - 4 ,байт
short - 2 ,байт
unsigned - 4 ,байт
char - 1 ,байт
float - 4 ,байт
double - 8 ,байт
Размер памяти, занимаемой переменной некоторого типа определить диапазон значений этой переменной.
В таблице 3.1 перечислены диапазоны значений простейших типов данных.
Таблица 3.1
Структуры данных
Базовым строительным блоком структуры данных является ячейка, которая предназначена для хранения значения определенного базового или составного типа данных. Структуры данных создаются путем задания имен совокупностям (агрегатам) ячеек и (необязательно) интерпретации значения некоторых ячеек как представителей (указателей) других ячеек.
В качестве простейшего механизма агрегирования ячеек в большинстве языков программирования можно применять (одномерный) массив, т.е. последовательность ячеек определенного типа. Массив также можно рассматривать как отображение множества индексов в множество ячеек.
Другим общим механизмом агрегирования ячеек в языках программирования является структура (запись). Запись можно рассматривать как ячейку, состоящую из нескольких других ячеек (называемых полями), значения в которых могут быть разных типов. Записи часто группируются в массивы; тип данных определяется совокупностью типов полей записи.
Массивы и записи являются структурами с произвольным доступом, подразумевая под этим, что время доступа к компонентам массива или записи не зависит от значения индекса массива или указателя поля записи.
Третий метод агрегирования ячеек, который можно найти в языках программирования это файл. Файл, как и одномерный массив, является последовательностью значений определенного типа. Различают файлы прямого и последовательного доступа. В файле прямого доступа имеет индекс, т.е. все ячейки (компоненты) файла пронумерованы. Доступ может быть осуществлен к любой ячейки. Файл последовательного доступа не имеет индексов: его элементы доступны только в том порядке, в каком они были записаны в файл.
Достоинство агрегирования с помощью файла заключается в том, что файл не имеет ограничения на количество составляющих его элементов и это количество может изменяться во время выполнения программы.
В дополнение к средствам агрегирования ячеек в языках программирования можно использовать указатели и курсоры. Указатель — это ячейка, чье значение указывает на другую ячейку. При графическом представлении структуры данных в виде схемы тот факт, что ячейка А является указателем на ячейку В, показывается с помощью стрелки от ячейки А к ячейке В.
Курсор — это ячейка с целочисленным значением, используемая для указания на массив. В качестве способа указания курсор работает так же, как и указатель, но курсор можно использовать и в языках, которые не имеют явного типа указателя.
В схемах структур данных принято рисовать стрелку из ячейки курсора к ячейке, на которую указывает курсор. Иногда также указывают целое число в ячейке курсора, напоминая тем самым, что это не настоящий указатель.
Пример структуры данных представлен на рис.3.1. В данной структуре ячейка header содержит указатель на запись, состоящую из двух полей: cur и next. Поле cur содержит курсор, содержащий индекс логически первого элемента массива reclist (в данном случае четвертый элемент массива является логически первым, см. ниже). Поле next содержит указатель на аналогичную запись, в которой поле cur содержит курсор на логически последнюю запись массива reclist (в примере это вторая запись), а поле next содержит пустой указатель. Массив reclist состоит из четырех записей с индексами от 1 до 4. Каждая запись состоит из двух полей: data и next. Поле data содержит данные (в примере это символы a,b,c,d). Логический порядок элементов массива определяется значением курсора в поле next - курсор хранит индекс логически следующего элемента массива. Так за четвертым элементом следует первый, за первым - третий, за третьим - второй. Второй элемент массива логически последний, т.к. значение поля next второго элемента равно 0, т.е. отличается от любого значения индекса массива - 1,2,3 или 4.
Некоторые способы описания новых типов на языке Си, создания различных структур данных (массивов, файлов, структур) и испоьзования указателей будут рассмотрены в дальнейшем. Также будут рассмотрены различные способы предятавления данных в памяти ЭВМ.
3.1.4.Оператор определения имени типа typedef
Новые имена типов данных в Си можно определять, используя ключевое слово typedef. На самом деле таким способом новый тип данных не создается, а всего лишь определяется новое имя для уже существующего типа. Этот процесс может помочь сделать машинно-зависимые программы более переносимыми. Если вы для каждого машинно-зависимого типа данных, используемого в вашей программе, определяете данное вами имя, то при компиляции для новой среды придется менять только операторы typedef.
Общий вид декларации typedef (оператора typedef) такой:
typedef тип новое_имя;
где тип — это любой тип данных языка Си, а новое_имя — новое имя этого типа. Новое имя является дополнением к уже существующему, а не его заменой.
Например, для float можно создать новое имя с помощью
typedef float balance;
Это выражение дает компилятору указание считать balance еще одним именем float. Затем, используя balance, можно создать переменную типа float:
balance over_due;
Теперь, когда имя balance определено, его можно использовать и в другом операторе typedef. Например, выражение
typedef balance overdraft;
дает компилятору указание признавать overdraft в качестве еще одного имени balance, которое в свою очередь является еще одним именем float.
Использование операторов typedef может облегчить чтение кода и его перенос на новую машину. Однако новый физический тип данных таким способом не создается.
Для создания новых типов в Си можно использовать массивы, указатели и структуры. Более детально структуры, вопросы конструирования новых типов и абстрактные типы данных будут рассмотрены позднее. Сейчас рассмотрим массивы и указатели.
Массивы
Массив представляет собой последовательность переменных одного типа. Доступ к элементу массива осуществляется по его номеру. Описание массива в Си состоит из имени базового типа, названия массива и его размера, который указывается в квадратных скобках. Размер массива обязательно должен быть целочисленной константой или константным выражением. Примеры:
int a[10];
char c[256];
double d[1000];
В первой строке описан массив целых чисел из 10 элементов. Подчеркнем, что нумерация в Си всегда начинается с нуля, так что индексы элементов массива изменяются в пределах от 0 до 9. Во второй строке описан массив символов из 256 элементов (индексы в пределах 0...255), в третьей - массив вещественных чисел из 1000 элементов (индексы в пределах 0...999). Для доступа к элементу массива указывается имя массива и индекс (номер) элемента в квадратных скобках, например,
a[9], c[255], d[123].
Функция sizeof() возвращает размер всего массива в байтах, а не в элементах массива. В данном примере
sizeof(a) = 10*sizeof(int) = 40,
sizeof(c) = 256*sizeof(char) = 256,
sizeof(d) = 1000*sizeof(double) = 8000.
При описании массива можно выполнить инициализацию его элементов:
double a[3]={5.2, 6.7, 3.1}};
У рассмотренной реализации массивов, есть один существенный недостаток: так можно реализовать только массивы, размер которых известен заранее, т.е. до начала работы программы на стадии компиляции.
Реализация массивов, размер которых становится известным на стадии выполнения программы, будет рассмотрен в теме 5.
Указатели
Указатели - это переменные, которые хранят адреса объектов. При описании указателя надо задать тип объектов, адреса которых будут содержаться в нем. Перед именем указателя при описании ставится звездочка, чтобы отличить его от обычной переменной. Примеры описаний указателей:
int *a, *b, c, d;
char *e;
В первой строке описаны указатели a и b на тип int и простые переменныe c и d типа int.
Для указателей определены две унарные операции:
1. присвоить указателю адрес некоторой переменной. Для этого используется операция взятия адреса, которая обозначается амперсендом &. Например, строка a = &c; указателю a присваивает значение адреса переменной c;
2. получить объект, адрес которого содержится в указателе; для этого используется операция звездочка '*', которая записывается перед указателем. (Заметим, что звездочкой обозначается также операция умножения.) Например, строка d = *a; присваивает переменной d значение целочисленной переменной, адрес которой содержится в a. Так как ранее указателю a был присвоен адрес переменной c, то в результате переменной d присваивается значение c, т.е. данная строка эквивалентна следующей: d = c;
Указатели и массивы
Между указателями и массивами существует тесная связь. Имя массива a является указателем на его первый элемент, т.е. выражения a и &(a[0]) эквивалентны. Подробнее связь между массивами и указателями будет рассмотрена в дальнейшем.
Оператор присваивания
В Си оператор присваивания записывается с помощью символа равенства, например, строка
x = 100;
означает присвоение переменной x значения 100.
Оператор присваивания представляет собой бинарную операцию. Значением операции присваивания является значение, которое присваивается переменной, стоящей в левой части. Это позволяет использовать знак присваивания внутри выражения, например,
x = (y = sin(z)) + 1.0;
Здесь в скобках стоит выражение y = sin(z), в результате вычисления которого переменной y присваивается значение sin(z). Значением этого выражения является значение, присвоенное переменной y, т.е. sin(z). К этому значению затем прибавляется единица, т.е. в результате переменной x присваивается значение sin(z)+1.
Выражения, подобные выражению, приведенному в этом примере, иногда используются, когда необходимо запомнить значение подвыражения (в данном случае sin (z)) в некоторой переменной (в данном случае y), чтобы затем не вычислять его повторно.
Арифметические операции
К четырем обычным арифметическим операциям сложения +, вычитания -, умножения * и деления / в Си добавлена операция нахождения остатка от деления первого целого числа на второе, которая обозначается символом процента %. Приоритет у операции вычисления остатка % такой же, как и у деления или умножения.
Операции сравнения
Операция сравнения сравнивает два выражения. В результате вырабатывается логическое значение - 1 или 0 (истина или ложь) в зависимости от значений выражений.
Операции сравнения в Си обозначаются следующим образом:
== равно, != не равно,
> больше, >= больше или равно,
< меньше, <= меньше или равно.
Примеры:
int res;
int x, y;
res = (x == y); // истинна, если x равно y, иначе ложь
res = (x == x); // всегда истинна
res = (2 < 1); // всегда ложь
Логические операции
В Си используются следующие логические операции:
· || логическое "или" (логическое сложение)
· && логическое "и" (логическое умножение)
· ! логическое "не" (логическое отрицание)
Примеры логических выражений:
int a, b, c, d ,x, y;
a = b || c; // логическое "или"
d = b && c; // логическое "и"
a = !b; // логическое "не"
a = (x == y); // сравнение в правой части
c = (x > 0) && (y != 1); // c истинно, когда оба сравнения истинны
Самый высокий приоритет у операции логического отрицания, затем следует логическое умножение, самый низкий приоритет у логического сложения.
Операции сдвига
Операции сдвига применяются к целочисленным переменным: двоичный код числа сдвигается вправо или влево на указанное количество позиций. Сдвиг вправо обозначается двумя символами "больше" >>, сдвиг влево - двумя символами "меньше" <<.
При сдвиге влево на k позиций младшие k разрядов результата устанавливаются в ноль. Сдвиг влево на k позиций эквивалентен умножению на число 2k.
Сдвиг вправо по-разному определяется для беззнаковых и знаковых чисел. При сдвиге вправо беззнакового числа на k позиций освободившиеся k старших разрядов устанавливаются в ноль.
При сдвиге вправо чисел со знаком происходит так называемое "расширение знакового разряда". Если число неотрицательно, т.е. старший, или знаковый, разряд числа равен нулю, то происходит обычный сдвиг, как и в случае беззнаковых чисел. Если же число отрицательное, т.е. его старший разряд равен единице, то освободившиеся в результате сдвига k старших разрядов устанавливаются в единицу. Число, таким образом, остается отрицательным.
Сдвиг вправо на k позиций соответствует целочисленному делению на число 2k. При k = 1 это соответствует делению на 2 только для отрицательных чисел, не равных -1. Для числа -1, все биты двоичного кода которого равны единице, сдвиг вправо не приводит к его изменению.
3.3.8.Операции "увеличить на", "домножить на" и т.п.
В большинстве алгоритмов при выполнении операции сложения чаще всего переменная-результат операции совпадает с первым аргументом:
x = x + y;
Здесь складываются значения двух переменных x и y, результат помещается в первую переменную x. Таким образом, значение переменной x увеличивается на значение y.
В Си существует сокращенная запись операции увеличения:
s += y;
Оператор += читается как "увеличить на".
Подобные операторы существуют для любых операций, допустимой в Си. Например, для арифметических операций +, -, *, /, % можно использовать операции
+= увеличить на
-= уменьшить на
*= домножить на
/= поделить на
%= поделить с остатком на
Для логических операций можно использовать операторы &&= и ||=.
Операция приведения типа
Операция приведения типа используется, когда значение одного типа преобразуется к другому типу. Операция обозначается именем типа, заключенным в круглые скобки; она записывается перед ее единственным аргументом.
Рассмотрим два примера. Пусть требуется преобразовать целое число к вещественному типу. В примере значение целой переменной n приводится к вещественному типу и присваивается вещественной переменной x:
double x;
int n;
. . .
x = (double) n; // Операция приведения к типу double
В данном случае никакой потери информации не происходит, поэтому такое приведение допустимо и по умолчанию:
x = n; // Эквивалентно x = (double) n;
Во следующем примере вещественное значение преобразуется к целому типу. При этом дробная часть вещественного числа отбрасывается, а знак числа сохраняется:
double x, y;
int n, k;
. . .
x = 3.7;
y = -1.5;
n = (int) x; // n присваивается значение 3
k = (int) y; // k присваивается значение -1
В результате выполнения операции приведения вещественного числа к целому типу происходит отбрасывание дробной части числа, т.е. потеря информации. Поэтому, если использовать операцию приведения типа неявно (т.е. в результате простого присваивания целой переменной вещественного значения), например,
double x; int n;
. . .
n = x; // неявное приведение вещественного типа к целому
то компилятор обязательно выдаст предупреждение или ошибку. Когда используется явное приведение типа, компилятору сообщается, что это не случайная ошибка, а намеренное приведение вещественного значения к целому типу, при котором дробная часть отбрасывается. При этом компилятор никаких предупреждений не выдает.
Операция приведения типа чаще всего используется для преобразования указателей (см. разд. 1.8).
Управляющие конструкции
Управляющие конструкции позволяют организовывать циклы и ветвления в программах. В Си всего несколько конструкций, причем многие из них можно не использовать т.к. они реализуются через остальные.
Фигурные скобки
Фигурные скобки позволяют объединить несколько простых (элементарных) операторов в один составной оператор, или блок. Во всех синтаксических конструкциях составной оператор можно использовать вместо простого.
Оператор цикла while
Цикл while является циклом с предусловием, т.е. условие проверяется перед выполнением тела цикла. В Си для определения цикла while используется следующаий оператор:
while (условие)
действие;
Сначала проверяется условие. Если оно истинно, то выполняется действие. Затем снова проверяется условие; если оно истинно, то снова повторяется действие, и т.д. Цикл завершается, когда условие становится ложным. Пример:
int n, p;
. . .
p = 1;
while (2*p <= n)
p *= 2;
В результате выполнения этого фрагмента в переменной p будет вычислена максимальная степень двойки, не превосходящая целого положительного числа n.
Если условие ложно с самого начала, то действие не выполняется ни разу.
Тело цикла может состоять из одного или нескольких операторов. В последнем случае их надо заключить в фигурные скобки.
Оператор цикла for
Цикл for как цикл while является циклом с предусловием. В Си для определения цикла for используется следующаий оператор:
for (инициализация; условие продолжения; итератор)
действие;
Инициализация выполняется один раз перед первой проверкой условия продолжения и первым выполнением тела цикла. Условие продолжения проверяется перед каждым выполнением тела цикла. Если условие истинно, то выполняется действие, иначе цикл завершается. Итератор выполняется после каждого выполнения тела цикла (перед следующей проверкой условия продолжения).
Рассмотрим пример суммирования массива с использованием цикла for:
double a[100]; // Массив a содержит не более 100 эл-тов
int n; // Реальная длина массива a (n <= 100)
double sum; // Переменная для суммы эл-тов массива
int i; // Переменная цикла
. . .
sum = 0.0;
for (i = 0; i < n; ++i)
sum += a[i]; // Увеличиваем сумму на a[i]
Здесь целочисленная переменная i используется в качестве переменной цикла. В операторе инициализации переменной i присваивается значение 0. Условием продолжения цикла является условие i<n. Итератор ++i увеличивает переменную i на единицу. Таким образом, переменная i последовательно принимает значения 0, 1, 2,..., n-1. Для каждого значения i выполняется оператор sum += a[i];.
В большинстве других языков программирования арифметический цикл жестко связан с использованием переменной цикла, которая должна принимать значения из арифметической прогрессии. В Си это не так, здесь инициализация, условие продолжения и итератор могут быть произвольными выражениями, что обеспечивает гораздо большую гибкость программы. Конструкцию цикла for можно реализовать с помощью цикла while:
Например, фрагмент с суммированием массива
for (i=0; i < n; ++i)
sum += a[i];
реализуется с использованием цикла while следующим образом:
i = 0;
while (i < n) {
sum += a[i];
++i;
}
Кроме того, что в цикле for в качестве инициализации и итератора можно использовать любые выражения, можно выполнить несколько действий при инициализации или в итераторе. Для этого язык Си предоставляет операцию "запятая", которая позволяет объединить несколько выражений в одно. Например, фрагмент суммирования массива
sum = 0.0;
for (i = 0; i < n; ++i)
sum += a[i];
можно переписать следующим образом:
for (sum = 0.0, i = 0; i < n; sum += a[i], ++i);
Здесь тело цикла вообще пустое, все действия вынесены в заголовок цикла. Лучше избегать такого стиля программирования: он ничего не добавляет в смысле эффективности готовой программы, но делает текст менее понятным и, таким образом, увеличивает вероятность ошибок.
3.4.6.Оператор цикла do...while
Цикл do...while является циклом с постусловием. Сначала выполняется тело цикла и только после этого проверяется условие продолжения цикла. В Си для определения цикла do...while используется следующаий оператор:
do
действие;
while (условие);
Вначале выполняется действие. Далее проверяется условие и если условие истинно, то снова выполняется действие и так до тех пор пока условие не станет ложным. Таким образом, действие будет выполнено 1 раз, даже если условие ложно с самого начала. Это является потенциальным источником ошибок. Лучше всегда использовать цикл с предусловием while.
Структуры
Структура - это конструкция, которая позволяет объединить несколько переменных с разными типами и именами в один составной объект. Она позволяет строить новые типы данных языка Си. В других я