Алгоритмические языки и программирование. Учебное пособие
.pdfТип |
Смысл |
Диапазон значений |
Операции |
|
|
|
|
|
|
int |
Целые числа |
-32768 – 32767 |
|
Арифметические: +, -, |
|
|
|
|
*, /, % |
|
|
|
|
Сравнения: ==, !=, <, |
|
|
|
|
>, <=, >= |
|
|
|
|
|
double |
Вещественные числа |
Минимальное |
|
Арифметические: +, -, |
|
(десятичные дроби) |
положительное |
число |
*, / |
|
|
1.7E-308, максимальное |
Сравнения: ==, !=, <, |
|
|
|
положительное |
число |
>, <=, >= |
|
|
1.7E+308 |
|
|
|
|
|
|
|
char |
Символьный тип |
0–255 |
|
Арифметические: +, -, |
|
|
|
|
*, /, % |
|
|
|
|
Сравнения: ==, !=, <, |
|
|
|
|
>, <=, >= |
|
|
|
|
|
bool |
Логический тип |
true, false |
|
Логические: &&, ||, !, ^ |
|
|
|
|
Cравнения: ==, !=, <, |
|
|
|
|
>, <=, >= |
|
|
|
|
|
Пример:
int A = 10; double b = 1.5; char B = 'z'; bool A1 = true;
Каждая операция требует один или два аргумента. Наиболее важной является операция присвоения:
A = 0;
Эта операция записывает в участок памяти, который связан с именем переменной, стоящей слева от знака «=», значение, стоящее справа. У операции два аргумента: «A» и «0». Заметьте: порядок аргументов только такой! Операция 0 = A; является синтаксической ошибкой, поскольку 0 не является переменной.
11
Операция присвоения, как и все остальные, имеет результат применения. Этот результат совпадает со значением, стоящим справа от знака «=». Поэтому далее можно написать
int a = A = 0;
Результатом арифметической операции является значение, тип которого определяет по типам аргументов компилятор. Так, результат операции 1+2 будет величина 3 типа int, в то время как результат операции 1.0 + 2.0 будет иметь тип double. Тип double в каком-то смысле «сильнее» типа int, поэтому результат операции, где участвуют величины типа int и double, будет иметь тип double. Заметьте, что один знак может обозначать разные операции в зависимости от типов операндов. Так, 3/2 = 1 (операнды целого типа), но 3.0/2 = 1.5 (один из операндов вещественного типа, поэтому второй также преобразуется к вещественному типу и результат операции имеет вещественный тип).
Результат операций сравнения и логических операций всегда будет иметь тип bool.
Выражением называется инструкция, связывающая несколько переменных и операций. Например:
int a;
a = 2+2 * 2;
Глядя на это выражение, можно заметить, что в зависимости от порядка вычисления операций мы получаем разный результат. Заметьте: операции имеют разный приоритет, так же как и в математических выражениях! Общей рекомендацией может служить расстановка скобок:
int a;
a = (2 + 2) * 2; bool t;
t = (a > 0) || (0 == a);
Обратите внимание на запись: 0 == a. Для операции сравнения порядок аргументов не имеет значения. Но практика показывает, что при использовании
12
оператора «==» его часто путают с операцией присвоения «=». Поскольку присвоение также является операцией, присутствие операции присвоения вместо операции сравнения допустимо с точки зрения синтаксиса языка. Но смысл выражения при этом меняется. То есть замена оператора «=» на оператор «==» – логическая или семантическая ошибка, но не синтаксическая. При поиске этой ошибки компилятор нам не сможет помочь. Поэтому полезной привычкой является при сравнении с некоторой величиной писать слева от знака «==» величину, а справа – переменную. При подмене оператора «==» на оператор «=» возникает синтаксическая ошибка и компилятор нам помогает ее вовремя обнаружить.
Величины, прямо указанные в тексте программы, такие как 10, 2.3, 'a', true, называются литеральными константами. Также есть возможность для любой переменной при ее описании запретить ее дальнейшее изменение. Такая переменная называется именованной константой:
const double Pi = 3.1415; Pi = 4; //syntax error
При описании именованной константы ее обязательно нужно инициализировать!
Функция
Код программы предназначен не только для компилятора, но и для чтения его в дальнейшем. Читать код программы приходится как автору, так и программисту, отвечающему за исправление ошибок и добавление новой функциональности. Компилятор устроила бы программа, написанная в одну строку без пробелов. Но для людей такой стиль программирования не подходит. В течение нашего курса мы будем исходить из того, что программа пишется для человека, а не для компилятора. Поэтому основными требованиями к коду программы считаются понятность кода, легкость изменения и отсутствие повторяющегося кода. Для того чтобы программа соответствовала этим
13
требованиям, она должна быть разбита на небольшие, относительно независимые части аналогично разбиению книги на главы и параграфы. Эти части называются функциями. В других языках программирования их называют процедурами или подпрограммами. Для того чтобы функции не содержали повторяющийся код, каждая из них должна выполнять одну определенную подзадачу, отличающуюся от остальных подзадач.
Основной функцией в программе является функция main, но можно создавать дополнительные функции.
Переменные, описанные внутри одной функции, недоступны внутри другой, поэтому их называют локальными переменными. Они создаются при описании и уничтожаются при выходе из функции. Для обмена данными с функцией используются параметры функции. При описании функции нужно описать формальные параметры, при вызове функции вместо них будут подставлены значения переменных или констант, которые называются фактическими параметрами.
Для работы с функцией требуется описать функцию, определить функцию и вызвать функцию.
Описание функции, или прототип, включает в себя тип возвращаемого значения, имя функции, имена и типы формальных параметров. Например, прототип функции, возвращающей истину, если целое число a больше целого числа b:
bool More(int a, int b);
Далее, требуется определение функции. Оно включает в себя заголовок функции, совпадающий с прототипом, и тело функции. Тело функции заключается в фигурные скобки и содержит инструкции, выполняющиеся при вызове функции.
Пример:
bool More(int a, int b){ return a > b;
}
14
Для возвращения из функции значения используется ключевое слово return. Из функции можно вернуть только одно значение. Когда поток управления достигает ключевое слово return или закрывающую тело функции фигурную скобку, происходит возврат из функции. После возврата из функции поток управления переходит на следующую за вызовом функции инструкцию. Но при возврате из функции main происходит завершение выполнения программы.
Если функция ничего не возвращает, в качестве типа возвращаемого значения указывается ключевое слово void:
void Output(bool a){ cout << a;
}
Для того чтобы функция выполнила свою работу, ее нужно вызвать. Вызов функции производится или в функции main, или в функции, вызванной из main. При вызове нужно указать имя функции и имена фактических параметров. При вызове функции происходят нижеследующие действия.
1.Создание формальных параметров.
2.Копирование значений фактических параметров в формальные.
3.Создание локальных переменных.
4.Выполнение тела функции.
5.Уничтожаются локальные переменные.
6.Уничтожение формальных параметров.
7.Передача управления на следующую за вызовом функции инструкцию.
Код программы полностью:
#include<iostream> using namespace std;
bool More(int a, int b); // description of the function More void Output(bool a); // description of the function Output
15
int main(){
bool m = More(2, 3); //call of the function More Output(m); //call of the function Output
}
bool More(int a, int b){ // definition of the function More return a > b; // body of the function More
}
void Output(bool a){ // definition of the function Output cout << a; // body of the function Output
}
Обратите внимание, что функция More возвращает значение в функцию main. Это значение нужно сохранить в переменную (здесь переменная m) либо сразу вывести на экран. При простом вызове функции
More(2,3);
значение, переданное функцией, пропадет – нигде не сохранится.
Передача параметров в функцию возможна двумя способами: по ссылке и по значению. До сих пор в примерах использовалась передача по значению. Для указания о передаче формального параметра по ссылке перед именем параметра вставляется символ амперсенд &. Пример:
void More(int a, int b, bool &result);
Формальный параметр result передается по ссылке. В случае передачи по значению при вызове функции значения фактических параметров копируется в формальные. Из этого вытекает, что изменения формальных параметров не влияют на фактические. Пример:
#include<iostream> using namespace std;
void More(int a, int b, bool result); //wrong! int main(){
bool m;
More(2, 3, m); //call of the function More
16
cout << m;
}
void More(int a, int b, bool result){//wrong! result = a > b;
}
В этой программе изменена функция More. Вместо передачи результата работы функции используется параметр result. Но в программе допущена ошибка. Вследствие того, что параметр result передается по значению, изменение его значения в функции More не повлияет на фактический параметр m внутри функции main. Для исправления программы нужно передавать формальный параметр result по ссылке:
#include<iostream> using namespace std;
void More(int a, int b, bool &result); //right! int main(){
bool m;
More(2, 3, m); //call of the function More cout << m;
}
void More(int a, int b, bool &result){//right! result = a > b;
}
При передаче по ссылке формальный параметр является той же переменной, что и фактический, то есть происходит передача в функцию самой переменной, а не ее значения. Поэтому, меняя в функции формальный параметр result, мы в функции main получаем изменение фактического параметра m.
В библиотеке cmath описаны часто используемые математические функции. Самые часто используемые функции приведены ниже.
17
Функция C++ |
Математическая функция |
|
|
fabs(x) |
Модуль числа |
|
|
exp(x) |
ex |
log(x) |
Натуральный логарифм x |
|
|
log10(x) |
Десятичный логарифм x |
|
|
sqrt(x) |
Квадратный корень из x |
|
|
pow(x,y) |
x y |
sin(x) |
sin x |
|
|
cos(x) |
cos x |
|
|
tan(x) |
tg x |
|
|
asin(x) |
arcsin x |
|
|
acos(x) |
arccos x |
|
|
atan(x) |
arctg x |
|
|
ceil(x) |
Округляет вверх |
|
|
floor(x) |
Округляет вниз |
|
|
Управляющие конструкции
Следование
Как уже говорилось, программа выполняется с первой инструкции функции main до последней. При этом поток управления переходит по порядку от одной инструкции к другой. Такой порядок выполнения называется следованием, и его можно изобразить в виде блок-схемы:
18
Выбор
В ряде задач алгоритм устроен более сложно: в зависимости от некоторого условия требуется изменить порядок выполнения инструкций. Механизм языка, обеспечивающий изменение порядка выполнения инструкций, называется управляющей конструкцией. Основные управляющие конструкции: выбор и повтор. Например, если требуется найти корни квадратного уравнения, в зависимости от знака дискриминанта нужно либо вычислить корни по известной формуле, либо сообщить, что вещественных корней нет. Такой случай называется выбором и изображается на блок-схеме следующим образом:
Выбор также бывает с одной веткой:
В языке C++ для описания выбора существует два ключевых слова: if и else. Ключевого слова then нет. Общая запись такова:
if(условие){ инструкция_1;… инструкция_N;
} else{
инструкция_1;… инструкция_N;
}
19
Фигурные скобки называются составным оператором, или блоком. Их смысл в том, что там, где требуется одна инструкция, можно записать несколько, объединяя их в фигурные скобки.
После ключевого слова if в круглых скобках записывается логическое выражение. При выполнении программы выражение вычисляется. Если значение выражения равно true, то выполняется блок операторов, идущий за ключевым словом if. В противном случае выполняются операторы, следующие за ключевым словом else. В случае, если ключевое слово else отсутствует, выполняются инструкции после блока, следующего за ключевым словом if.
Выбор может быть вложенным, т. е. внутри блоков if и else могут быть написаны еще блоки if и else. Эта ситуация возникает, когда требуется написать несколько условных выражений. Но лучше, если это возможно, объединить данные выражения в одно, используя логические операции. Пример – вычисление корней квадратного уравнения:
#include<iostream>
#include<cmath> //include math library using namespace std;
int main(){ double a = 0,
b = 0, c = 0;
cin >> a >> b >> c; //input coefficients double D = b*b - 4 * a *c;
if(D < 0){
cout << "No roots"<<endl;
}
else{
if(D == 0){
cout << "Root:" << -b / (2 * a) << endl;
}
20
