основы программирования на С++
.pdfусловие ложно. Для выполнения такой условной обработки программы используют операторы if-else, как показано ниже:
if (условие_истинно) оператор;
else
оператор;
Когда программе требуется выполнить несколько операторов, если условие ложно или истинно, вы должны сгруппировать связанные операторы внутри левой и правой фигурных скобок {}.
Оператор switch
В тех случаях, когда необходимо осуществить проверку определенных значений, можно использовать оператор switch.
Если используется оператор switch, нужно указать условие и затем один или несколько вариантов (case), которые программа попытается сопоставить с условием. Например, следующая программа SWITCH.CPP использует оператор switch для вывода сообщения, основываясь на текущей оценке студента:
#include <iostream.h> void main(void)
{
char grade = 'B'; switch (grade)
{
case 'A': cout << "Поздравляем, вы получили A" << endl; break; case 'B': cout << "Хорошо, у вас B" << endl; break;
case 'C': cout << "У вас всего лишь C" << endl; break;
case 'D': cout << "Плохо, у вас D" << endl; break; default: cout << "Ужасно! Учите лучше!" << endl;
break;
}
}
Оператор switch состоит из двух частей. Первая часть оператора switch представляет собой условие, которое появляется после ключевого слова switch. Вторая часть представляет собой возможные варианты соответствия. Когда программа встречает оператор switch, она сначала исследует условие, а затем пытается найти среди возможных вариантов тот, который соответствует условию. Если программа находит соответствие, выполняются указанные операторы. Следует обратить внимание на использование оператора break в каждом варианте предыдущей программы. Если C++ встречает вариант, соответствующий условию оператора switch, то он подразумевает, что все последующие варианты тоже соответствуют условию. Оператор break указывает C++ завершить текущий оператор switch и продолжить выполнение программы с первого оператора, следующего за оператором switch.
11
Циклы
Одна из наиболее общих операций, которую могут выполнять программы, состоит в повторении одного или нескольких операторов определенное количество раз. Оператор for предназначен именно для этого. Общий формат for выглядит так:
for (инициализация; проверка; увеличение) оператор;
При запуске этот цикл for присваивает начальное значение управляющей переменной цикла. Далее программа проверяет условие цикла. Если условие истинно, она выполняет операторы внутри цикла, затем увеличивает управляющую переменную цикла и повторяет проверку условия. Если условие истинно, процесс повторяется. Если же условие ложно, цикл for завершается и программа продолжает свое выполнение с первого оператора, следующего за циклом for. Следующая программа выведет на экран числа от 0 до 9:
#include <iostream.h> int main()
{
for(int i = 0; i != 10; ++i)
{
cout << i << endl;
}
return 0;
}
Однако в некоторых случаях программе необходимо повторять операторы, пока удовлетворяется (истинно) некоторое условие. В таких ситуациях можно использовать оператор while. Его общий формат:
while (условие_верно) оператор;
Если программа встречает оператор while, она проверяет заданное условие. Если условие истинно, программа выполняет операторы цикла while. После выполнения последнего оператора в цикле while опять проверяет условие. Если условие все еще истинно, повторяются операторы цикла и повторяется данный процесс. Когда условие, наконец, становится ложным, цикл завершается, и программа продолжает свое выполнение с первого оператора, следующего за циклом. В следующей программе, если пользователь вводит значение, отличное от Д (да) или Н (нет), программа сигналит встроенным динамиком, записывая символ сигнала '\а' в выходной поток cout, до тех пор, пока не будет введено одно из требуемых значений:
#include <iostream.h> void main(void)
{
int done = 0; // Устанавливается в состояние //„истина", если введены Д или Н char letter;
while (! done)
12
{
cout << "\nВведите Д или Н" << " и нажмите Enter для продолжения: ";
cin >> letter;
if ((letter == 'Д') II (letter == 'д')) done = 1;
else if ((letter == 'Н') II (letter == 'н')) done = 1;
else cout << '\а'; // Играть сигнал динамика //для неверного символа
}
cout << "Вы ввели букву " << letter << endl;
}
Цикл while тоже поддерживает несколько операторов, сгруппированных внутри фигурных скобок.
В зависимости от назначения программы, возможно, потребуется выполнить набор операторов, по крайней мере, один раз, и повторить операторы, если заданное условие истинно. В таких случаях используется оператор do while:
do {
оператор;
} while (условие);
Когда программа встречает оператор do while, она сразу же выполняет операторы, содержащиеся в цикле. Затем программа исследует условие цикла. Если условие истинно, программа повторяет операторы цикла и процесс продолжается. Если условие цикла становится ложным, программа продолжает свое выполнение с первого оператора, следующего за оператором do while.
Массивы и строки
Массив – это несколько пронумерованных переменных одного типа, объединенных общим именем. Нумерация ячеек идет с нуля. Пусть, например, объявляется массив целых чисел с именем container: int container[N], здесь N – его размер, число ячеек. Это описание как бы объявляет N пере-
менных типа int с именами container[0] ... container [N-1].
Массив – набор переменных, которые не именованы разными именами, как, например, container0, container1, ..., а пронумерованы под одним именем: container[0], container[1], и т. д. Индекс – часть имени переменной.
На самом деле индексация – это:
1)выбор элемента в массиве;
2)справа от присваиваний и в выражениях – еще и разыменование, то есть взятие вместо имени переменной значения, в ней хранящегося.
Если в переменную не было занесено значение, а мы используем эту переменную, то в ней лежит мусор (любое непредсказуемое значение), т.е.
printf("container4 есть %d\n", container [4]);
напечатает все что угодно. Поэтому переменные надо всегда инициализировать. Глобальные переменные автоматически инициализируются нулем,
13
если не задано иначе. Локальные переменные не инициализируются автоматически.
В С++ массивы нельзя присваивать целиком. Также нельзя присвоить значение сразу всем элементам массива. Вследствие этого массивы приходится копировать (и инициализировать) поэлементно, в цикле перебирая все ячейки массива или их часть.
Рассмотрим пример работы с массивами – вывод чисел Фибоначчи, за-
даваемых формулами: f[1] = 1, f[2] = 1, f[n+2] = f[n+1] + f[n].
#include <iostream.h>
#define N 20 /* сколько первых чисел посчитать */ void main(){
int fibs[N], index;
fibs[0] = 1; /* индексы отсчитываются с нуля!!! */ fibs[1] = 1;
/* Тут показано, что индекс элемента массива может вычисляться */
for(index=2; index < N; index++)
fibs[index] = fibs[index-1] + fibs[index-2]; /* Распечатка в обратном порядке */
for(index = N-1; index >= 0; index--) printf("%d-ое число Фибоначчи есть %d\n",
index+1, fibs[index]);
}
Здесь присутствует новый оператор #define, который задает текстуальную замену слова N на слово 20, в данном случае просто являясь эквивалентом
const int N = 20;
К сожалению, размер массива не может быть задан при помощи переменной, а при помощи имени, определенного в #define – может.
Большинство программ на C++ широко используют символьные строки, которые хранятся в массиве типа char, заканчивающемся символом NULL (или ASCII 0). Следует помнить, что:
• для объявления символьной строки нужно объявить массив типа
char;
•чтобы присвоить символы символьной строке, следует просто присвоить символы элементам массива;
•символ NULL (ASCII 0, изображается как специальный символ '\0') используется для пометки последнего символа строки;
•символьные строки можно инициализировать при объявлении;
•программы могут передавать символьные строки в функцию;
•большинство библиотек этапа выполнения C++ обеспечивают
набор функций, которые управляют символьными строками. Если в программе используются строковые константы, заключенные в
двойные кавычки, компилятор C++ автоматически добавляет символ NULL.
14
Следующая программа использует цикл for для вывода содержимого строки:
#include <iostream.h> void main(void)
{
char alphabet[34]; //33 символа плюс NULL char letter;
int index;
for (letter = 'A', index = 0; letter <= 'Я'; letter++,index++) alphabet[index] = letter;
alphabet[index] = NULL;
for (index = 0; alphabet[index] 1= NULL; index++) cout << alphabet[index];
cout << endl;
}
При рассмотрении программ на C++ можно встретить символы, заключенные в одинарные (например, 'А') и в двойные кавычки ("А"). Символ внутри одинарных кавычек представляет собой символьную константу. Компилятор C++ выделяет только один байт памяти для хранения символьной константы. Символ в двойных кавычках представляет собой строковую константу — указанный символ и символ NULL (добавляемый компилятором). Таким образом, компилятор будет выделять два байта для символьной строки.
Для инициализации символьной строки при объявлении укажите требуемую строку внутри двойных кавычек:
char title[64] = "Учимся программировать на языке C++";
Функции
Функция представляет собой набор связанных операторов, которые выполняют определенную задачу. Для вызова функции программы просто обращаются к имени функции, за которым следуют круглые скобки, как показано ниже:
function_name();
Если программа передает информацию (параметры) в функцию, она размещает эту информацию внутри круглых скобок, разделяя ее запятыми:
payroll(employee_name, employee_id, salary);
После того как последний оператор функции завершен, выполнение программы продолжается с первого оператора следующего за вызовом функции.
Когда функция возвращает значение, вызвавшая программа может использовать его различным образом. Например, использовать возвращаемое значение в условии:
if (payroll(employee, hours, salary) < 500.00) cout << "Этот служащий нуждается в повышении" << endl;
Каждая функция, вызываемая в программе, должна быть где-то определена (только один раз). Определение функции – это описание функции, в котором приводится тело функции. Например:
15
extern void swap(int*, int*); // описание void swap(int*, int*) // определение
{
int t = *p; *p =*q;
*q = t;
}
Когда вызывается функция, дополнительно выделяется память под ее формальные параметры, и каждый формальный параметр инициализируется соответствующим ему фактическим параметром. Тип фактического параметра сопоставляется с типом формального параметра, и выполняются все стандартные и определенные пользователем преобразования типов. Есть особые правила для передачи векторов, средство передавать параметр без проверки и средство для задания параметров по умолчанию. Из функции, которая не описана как void, можно (и должно) возвращать значение. Возвращаемое значение задается оператором return, которых может быть и больше одного:
int fac(int n)
{
if (n > 1)
return n*fac(n-1); else
return 1;
}
Каждый раз, когда вызывается функция, создается новая копия ее параметров и автоматических переменных. После возврата из функции память используется заново, поэтому возвращать указатель на локальную переменную неразумно, т.к. содержание указываемого места изменится непредсказуемо.
Если в качестве параметра функции используется вектор, то передается указатель на его первый элемент. Например:
int strlen(const char*); void f()
{
char v[] = "a vector" strlen(v); strlen("Nicholas");
};
Вектор отличается от всех остальных типов тем, что вектор не передается (и не может передаваться) по значению. Размер вектора недоступен вызываемой функции. Это может быть неудобно, но эту сложность можно обойти несколькими способами. Строки оканчиваются нулем, поэтому их размер можно легко вычислить. Для других векторов можно передавать второй параметр, который задает размер, или определить тип, содержащий указатель и индикатор длины, и передавать его вместо просто вектора. Например:
void compute1(int* vec_ptr, int vec_size); // один способ
struct vec { |
// другой способ |
|
16 |
int* ptr; int size;
};
void compute2(vec v);
С многомерными массивами все сложнее, но часто можно вместо них использовать векторы указателей, которые не требуют специального рассмотрения. Например:
char* day[] = {"mon", "tue", "wed", "thu", "fri", "sat",
"sun"
};
Если размерность известна на стадии компиляции, то никаких проблем
нет:
void print_m34(int m[3][4])
{
for (int i = 0; i<3; i++) { for (int j="0;" j<4; j++) cout << " " << m[i][j]; cout << "\n"; } }
Матрица, конечно, все равно передается как указатель, а размерности используются просто для удобства записи. Первая размерность массива не имеет отношения к задаче отыскания положения элемента. Поэтому ее можно передавать как параметр.
Как правило, давать разным функциям разные имена – мысль хорошая, но когда некоторые функции выполняют одинаковую работу над объектами разных типов, может быть более удобно дать им одно и то же имя. Использование одного имени для различных действий над различными типами называется перегрузкой (overloading). Метод уже используется для основных операций C++: у сложения существует только одно имя: +, но его можно применять для сложения значений целых, плавающих и указательных типов. Эта идея легко расширяется на обработку операций, определенных пользователем, то есть, функций. Во избежание случайного повторного использования имени, имя может использоваться более, чем для одной функции только если оно сперва описано как перегруженное. Например:
overload print; void print(int); void print(char*);
Для некоторых функций невозможно задать число и тип всех параметров, которые можно ожидать в вызове. Такую функцию описывают, завершая список описаний параметров многоточием (...), что означает «и может быть, еще какие-то параметры». Например:
int printf(char* ...);
Это задает, что в вызове printf должен быть, по меньшей мере, один параметр, char*, а остальные могут быть, а могут и не быть.
Такая функция полагается на информацию, которая недоступна компилятору при интерпретации ее списка параметров. В случае printf() первым параметром является строка формата, содержащая специальные последовательности символов, позволяющие printf() правильно обрабатывать осталь-
17
ные параметры. Использование %s означает «жди параметра char*», а %d означает «жди параметра int». Однако, компилятор этого не знает, поэтому он не может убедиться в том, что ожидаемые параметры имеют соответствующий тип. Например:
printf("Мое имя %s %s\n",2);
откомпилируется и в лучшем случае приведет к какой-нибудь странного вида выдаче.
Указатели void f(int x){
x = 7; |
|
} |
|
main(){ |
|
int y = 17; |
|
f(y); |
/* печатает: y=17 */ |
printf("y=%d\n", y); |
|
} |
|
В аргументе x передаётся копия значения y, поэтому x=7; не изменяет значения у. Для того, чтобы вызываемая функция могла изменять значение переменной, используются указатели.
void f(int *ptr){ |
|
*ptr = 7; |
|
} |
|
main (){ |
|
int y=17; |
|
f(&y); |
/* печатает: y=7 */ |
printf("y=%d\n", y); |
|
} |
|
Здесь &y обозначает "указатель на переменную y" или "адрес переменной y", *ptr означает "разыменование указателя ptr", int *ptr; означает объявление переменной ptr, которая может содержать в себе указатель на переменную, хранящую int-число.
Указатели несколько различно ведут себя слева и справа от оператора присваивания.
Справа от присваиваний и в формулах *pointer означает взять значение переменной, на которую указывает указатель, «хранящийся в переменной pointer».
Слева от присваивания *pointer = 123; означает «положить значение правой части (т.е. 123) в переменную, на которую указывает указатель, хранящийся в переменной pointer».
Ниже описан обмен значений двух переменных с использованием адресов и указателей:
void swap(int *a, int *b){ int tmp;
tmp = *a; *a = *b; *b = tmp;
}
void main(){
18
int x, y;
x = 1; y = 2; swap(&x, &y);
printf("x=%d y=%d\n", x, y);
}
Структуры
Если программе требуется хранить связанную информацию разных типов, она может использовать структуру. Структура представляет собой переменную, группирующую связанные части информации (элементы), типы которых могут различаться.
Для определения структуры внутри программы следует указать имя структуры и ее элементы. Например, следующее определение создает структуру, содержащую информацию о служащем:
struct employee
{
char name [64] ; long employee_id; float salary; char phone[10];
int office_number;
};
Каждый элемент структуры имеет тип, а имя каждого элемента должно быть уникальным. После того как программа определит структуру, она может объявить переменные типа этой структуры, используя указанное имя (иногда называемое структурным тэгом), как показано ниже:
employee boss, worker, new_employee;
Для обращения к элементу структуры следует указать имя переменной, за которым следует точка и имя элемента. Следующая программа иллюстрирует использование структуры типа employee:
#include <iostream.h> #include <string.h> void main(void)
{
struct employee
{
char name [64]; long employee_id; float salary; char phone[10];
int office_number;
}worker;
//Копировать имя в строку strcpy(worker.name, "Джон Дой"); worker.employee_id = 12345;
worker.salary = 25000.00; worker.office_number = 102;
// Копировать номер телефона в строку
19
strcpy(worker.phone, "555-1212") ;
cout << "Служащий: " << worker.name << endl; cout << "Телефон: " << worker.phone << endl;
cout << "Номер служащего: " << worker.employee_id << endl; cout << "Оклад: " << worker.salary << endl;
cout << "Офис: " << worker.office_number << endl;
}
Замечание. Для копирования символьной строки в элементы name и phone использована функция strcpy. Если при объявлении переменной типа данной структуры не инициализируются элементы, нужно копировать символьные строки в символьно-строковые элементы.
Если функция изменяет элемент структуры, вызвавшая программа должна передать структуру в функцию с помощью адреса. Функция, в свою очередь, использует указатель на структуру. Для обращения к элементу структуры функции следует использовать следующий формат:
value = variable->member; variable->other_member = some_value;
Знакомство с классами C++
Класс представляет собой главное инструментальное средство C++ для объектно-ориентированного программирования. Класс очень похож на структуру, в которой сгруппированы элементы, соответствующие данным о некотором объекте, и оперирующие ими методы.
Для определения класса программа должна указать имя класса, элементы данных класса и функции класса (методы). Класс должен иметь уникальное имя, за которым следует открывающая фигурная скобка, один или несколько элементов и закрывающая фигурная скобка:
class class_name
{
int data_member; // Элемент данных
void show_member(int); // Функция-элемент
};
Определение класса обеспечивает шаблон, с помощью которого программы могут создать объекты типа этого класса, подобно тому, как программы создают переменные типа int, char и т. д.. После определения класса можно объявлять переменные типа этого класса – объекты.
Программа присваивает значения элементам данных класса, используя оператор точка. Вызов функции-элемента класса производится также с использованием оператора точка.
При увеличении функций определение встроенных функций внутри класса может внести беспорядок в описание класса. В качестве альтернативы можно поместить прототип функции внутри класса, а затем определить функцию вне класса. Ваше определение класса с прототипом становится следующим:
class employee
20
