Массивы хранят несколько значений одного и того же типа
Для объявления массива вы должны указать тип и уникальное имя массива, а также количество элементов, которые будет содержать массив. Например, следующие операторы объявляют три разных массива:
float part_cost[50];
int employee_age[100];
float stock_prices[25];
Обращение к элементам массива
Как вы уже знаете, массив позволяет вашим программам хранить несколько значений в одной и той же переменной. Для обращения к определенным значениям, хранящимся в массиве, используйте значение индекса, которое указывает на требуемый элемент. Например, для обращения к первому элементу массива test_scores вы должны использовать значение индекса 0. Для обращения ко второму элементу используйте индекс 1. Подобно этому, для обращения к третьему элементу используйте индекс 2. первый элемент массива всегда имеет индекс 0, а значение индекса последнего элемента на единицу меньше размера массива:
Важно помнить, что C++ всегда использует 0 для индекса первого элемента массива, а индекс последнего элемента на единицу меньше размера массива. Следующая программа ARRAY. CPP создает массив с именем values, который вмещает пять целочисленных значений. Далее программа присваивает элементам значения 100, 200, 300, 400 и 500:
#include <iostream.h>
void main(void)
{
int values[5]; // Объявление массива
values[0] = 100;
values[1] = 200;
values[2] = 300;
values[3] = 400;
values [4] = 500;
cout << "Массив содержит следующие значения" << endl;
cout << values [0] << ' ' << values [1] << ' ' << values [2] << ' ' << values [3] << ' ' << values [4] << endl;
}
Как видите, программа присваивает первое значение элементу 0 (va lues[0]). Она также присваивает последнее значение элементу 4 (размер Массива (5) минус 1).
Использование индекса для обращения к элементам массива
Массив позволяет вашим программам хранить несколько значений внутри одной и той же переменной. Для обращения к определенному значению внутри массива программы используют индекс. Говоря кратко, значение индекса указывает требуемый элемент массива. Все массивы C++ начинаются с элемента с индексом 0. Например, следующий оператор присваивает значение 100 первому элементу массива с именем scores:
scores[0] = 100;
Когда ваша программа объявляет массив, она указывает количество элементов, которые массив может хранить. Например, следующий оператор объявляет массив, способный хранить 100 значений типа int.
int scores[100];
В данном случае массив представляет собой элементы от scores[0] до scores[99].
Как вы уже знаете, C++ позволяет вашим программам инициализировать переменные при объявлении. То же верно и для массивов. При объявлении массива вы можете указать первоначальные значения, поместив их между левой и правой фигурными скобками, следующими за знаком равенства. Например, следующий оператор инициализирует массив values:
int values[5] = { 100, 200, 300, 400, 500 };
Подобным образом следующее объявление инициализирует массив с плавающей точкой:
float salaries[3] = { 25000.00. 35000.00, 50000.00 };
Если вы не указываете первоначальное значение для какого-либо элемента массива, большинство компиляторов C++ будут инициализировать такой элемент нулем. Например, следующее объявление инициализирует первые три из пяти элементов массива:
int values[5] = { 100, 200, 300 };
Программа не инициализирует элементы values[3] и values[4]. В зависимости от вашего компилятора, эти элементы могут содержать значение 0. Если вы не указываете размер массива, который вы инициализируете при объявлении, C++ распределит достаточно памяти, чтобы вместить все определяемые элементы. Например, следующее объявление создает массив, способяый хранить четыре целочисленных значения:
int numbers[] = { 1, 2, 3, 4 };
Вопрос №20
Строки в с++ позволяют нам работать с символьными данными. Благодаря ним мы можем читать с клавиатуры текст, как-то его обрабатывать и затем, например, снова его выводить в консоль.
В С++ существует 2 типа строк.
Первый из них - это массив переменных типа char.
Если кто не помнит, то переменная типа char хранит в себе 1 символ. Размер такой строки равняется размеру массива - 1, т.к. последний элемент содержит NULL (пустая переменная без значения), который обозначает символ конца строки.
Например:
char name[50];
cin>>name;
cout<<"Hello "<<name<<endl;
Второй из вариантов, более удобный - это специальный класс string
Для его работы необходимо в начале программы подключить заголовочный файл string:
#include <string>
В отличии от типа char, string является классом. Более подробно об классах я расскажу позднее, сейчас вам достаточно знать, что классы содержат в себе сразу несколько вещей: переменные, константы и функции для работы с переменными. Это достаточно грубое объяснение, но на первое время вам хватит.
Для создания строки вам необходимо в начале программы написать using namespace std;
Теперь чтоб создать строку достаточно написать:
string s;
Для записи в строку можно использовать оператор =
s="Hello";
Пример работы с классом string:
string name;
cout<<"Enter your name"<<endl;
cin>>name;
cout<<"Hi "<<s<<"!"<<endl;
Но пока вы воспользовались только одной прелестью строк: отсутствием необходимости задавать ее размер. Но кроме этого существует множество функций для работы со строками.
s.append(str) - добавляет в конец строки строку str. Можно писать как s.append(переменная), так и s.append("строка");
s.assign(str) - присваивает строке s значение строки str. Аналогично записи s=str;
int i=s.begin() - записывает в i индекс первого элемента строки
int i=s.end() - аналогично, но последнего
s.clear() - как следует из названия, отчищает строку. Т.е. удаляет все элементы в ней
s.compare(str) -сравнивает строку s со строкой str и возвращает 0 в случае совпадение (на самом деле сравнивает коды символов и возвращает из разность)
s.copy(куда, сколько, начиная с какого) - копирует из строки s в куда (там может быть как строка типа стринг, так и строка типа char). Последние 2 параметра не обязательные (можно использовать функцию с 1,2 или 3 параметрами)
bool b=s.empty() - если строка пуста, возвращает true, иначе false
s.erase(откуда, сколько) удаляет n элементов с заданной позиции
s.find(str,позиция) - ищет строку str начиная с заданной позиции
s.insert(позиция,str, начиная, beg, count) - вставляет в строку s начиная с заданной позиции часть строки str начиная с позиции beg и вставляя count символов
int len=s.length() - записывает в len длинну строки
s.push_back(symbol) - добавляет в конец строки символ
s.replace(index, n,str) - берет n первых символов из str и заменяет символы строки s на них, начиная с позиции index
str=s.substr(n,m) - возвращает m символов начиная с позиции n
s.swap(str) меняет содержимое s и str местами.
s.size() - возвращает число элементов в строке.
Вот собственно большинство необходимых функция для работы со строками в с++. Набор достаточно неплохой, больше вам пока не понадобится (Я лично пока и этот набор не выучил;))
Теперь немного примеров и затем практика. Постарайтесь сами понять, что делает каждый пример
string name,surname,text,fullname,s1,s2,s3,user;
user="Petya Petrov";
cout<<"Enter your name"<<endl;
cin>>name;
cout<<"Enter your surname"<<endl;
cin>>surname;
fullname=name;
fullname+=" "; // добавляем пробел
fullname.append(surname);
if (fullname.compare(user)==0) // <=> if (!(fullname.compare(user))
cout<<"Your are good user"<<endl;
else
cout<<"Bad user"<<endl;
cout<<"enter s1"<<endl;
cin>>s1;
cout<<"enter s2"<<endl;
cin>>s2;
s1.swap(s2);
cout<"new s1: "<<s1<<endl<<"new s2: "<<s2<<endl;
cout<<"Enter big text with your name"<<endl;
cin>>text;
int i=0;
i=text.find("name");
while (i!=-1)
{
text.replace(i,name.length(),name);
s3=text.substr(i,name.length());
cout<<"Replaced: "<<s3<<endl;
i=text.find("name");
}
cout<<"New text:"<<endl<<text<<endl;
text.clear();
cout<<"text: "<<text<<endl;
Вот собственно небольшой кусок программы, работающий со строками и непонятно что делающей.
Вопрос №21
Определение структуры
Структура – тип данных, задаваемый пользователем. В общем случае при работе со структурами следует выделить четыре момента:
- объявление и определение типа структуры,
- объявление структурной переменной,
- инициализация структурной переменной,
- использование структурной переменной.
Определение типа структуры представляется в виде
struct ID
{
<тип> <имя 1-го элемента>;
<тип> <имя 2-го элемента>;
…………
<тип> <имя последнего элемента>;
};
Определение типа структуры начинается с ключевого слова struct и содержит список объявлений, заключенных в фигурные скобки. За словом struct следует имя типа, называемое тегом структуры (tag – ярлык, этикетка). Элементы списка объявлений называются членами структуры или полями. Каждый элемент списка имеет уникальное для данного структурного типа имя. Однако следует заметить, что одни и те же имена полей могут быть использованы в различных структурных типах.
Определение типа структуры представляет собой шаблон (template), предназначенный для создания структурных переменных.
Объявление переменной структурного типа имеет следующий вид:
struct ID var1;
при этом в программе создается переменная с именем var1 типа ID. Все переменные, использующие один шаблон (тип) структуры, имеют одинаковый набор полей, однако различные наборы значений, присвоенные этим полям. При объявлении переменной происходит выделение памяти для размещения переменной. Шаблон структуры позволяет определить размер выделяемой памяти.
В общем случае, под структурную переменную выделяется область памяти не менее суммы длин всех полей структуры, например,
struct list
{
char name[20];
char first_name[40];
int;
}L;
В данном примере объявляется тип структура с именем list, состоящая из трех полей, и переменная с именем L типа struct list,при этом для переменной L выделяется 64 байта памяти.
Отметим, что определение типа структуры может быть задано в программе на внешнем уровне, при этом имя пользовательского типа имеет глобальную видимость (при этом память не выделяется). Определение типа структуры также может быть сделано внутри функции, тогда имя типа структуры имеет локальную видимость.
Создание структурной переменной возможно двумя способами: с использованием шаблона (типа) или без него.
Создание структурной переменной pt на основе шаблона выполняется следующим образом:
struct point //Определение типа структуры
{
int x;int y;
};
……
struct point pt; //Создание структурной переменной
Структурная переменная может быть задана уникальным образом:
struct //Определение анонимного типа структуры
{char name[20];
char f_name[40];
char s_name[20];
} copymy; //Создание структурной переменной
При размещении в памяти структурной переменной можно выполнить ее инициализацию. Неявная инициализация производится для глобальных переменных, переменных класса static. Структурную переменную можно инициализировать явно при объявлении, формируя список инициализации в виде константных выражений.
Формат: struct ID name_1={значение1, … значениеN};
Внутри фигурных скобок указываются значения полей структуры, например,
struct point pt={105,17};
при этом первое значение записывается в первое поле, второе значение – во второе поле и т. д., а сами значения должны иметь тип, совместимый с типом поля.
Над структурами возможны следующие операции:
- присваивание значений одной структурной переменной другой структурной переменной, при этом обе переменные должны иметь один и тот же тип;
- получение адреса переменной с помощью операции &;
- осуществление доступа к членам структуры.
Присваивание значения одной переменной другой выполняется путем копирования значений соответствующих полей, например:
struct point pt={105,15},pt1;
pt1=pt;
В результате выполнения этого присваивания в pt1.x будет записано значение 105, а в pt1.y – число 15.
Работа со структурной переменной обычно сводится к работе с отдельными полями структуры. Доступ к полю структуры осуществляется с помощью операции. (точка) посредством конструкции вида:
имя_структуры.имя_поля_структуры;
при этом обращение к полю структуры представляет собой переменную того же типа, что и поле, и может применяться везде, где допустимо использование переменных такого типа.
Например,
struct list copy = {"Ivanov","Petr",1980};
Обращение к "Ivanov" имеет вид copy.name. И это будет переменная типа указатель на char. Вывод на экран структуры copy будет иметь вид: printf("%s%s%d\n",copy.name,copy.first_name,copy.i);
Структуры нельзя сравнивать. Сравнивать можно только значения конкретных полей.
Объединение– это тип данных, позволяющий переменным различного типа использовать одну и ту же область памяти. Для объявления объединения используется ключевое слово union. Объединение, так же как и структура, содержит несколько полей. Однако в любой момент времени доступно только одно поле. Под объединение выделяется столько памяти, сколько требуется для размещения самого большого поля. Все поля поочередно используют выделенную память для хранения своих значений.
Определение типа:
union tag
{
тип1 переменная1;
тип2 переменная2;
……
};
где tag – имя типа.
Объявление переменной:
union tag u1;
или
union tag
{
тип1 переменная1;
тип2 переменная2;
……
} u1;
Инициализация объединения может быть выполнена при объявлении, при этом тип инициализирующего значения должен соответствовать первому полю объединения:
union tag
{
int i;
double d;
} u={10};
Доступ к полю объединения осуществляется с помощью операций – · (точка) и → (стрелка).
перечисления - это тип который может содержать значения указанные программистом. Целочисленные именованные константы могут быть определены как члены перечисления. Например:
enum { RED, GREEN, BLUE };
определяет три целочисленные константы и присваивает им значения. По умолчанию, значения присваиваются по порядку начиная с нуля, т.е. RED == 0, GREEN == 1 и BLUE == 2. Перечисление также может быть именованным:
enum color { RED, GREEN, BLUE };
Каждое перечисление - это отдельный тип, и тип каждого члена перечисления - это само перечисление. Например RED имеет тип color. Объявление типа переменной как color, вместо обычного unsigned, может подсказать и программисту и компилятору о том как эта переменная должна быть использована. Например:
void f(color c)
{
switch(c){
case RED:
// do something
break;
case BLUE:
// do something
break;
}
}
В этом случае компилятор может выдать предупреждение о том, что обрабатываются только два значения color из трёх возможных.
Таким образом перечисления это:
- Создание именованных констант с автоматическим увеличением значения константы
- Предупреждения о возможных ошибках со стороны компилятора
Вопрос №22
cout представляет собой объект типа ostream (выходной поток). Используя класс ostream, ваши программы могут выполнять вывод в cout с использованием оператора вставки или различных методов класса, например cout.put. Заголовочный файл iostream.h определяет выходной поток cout. Аналогично, заголовочный файл f stream.h определяет класс выходного файлового потока с именем ofstream. Используя объекты класса ofstream, ваши программы могут выполнять вывод в файл. Для начала вы должны объявить объект типа ofstream, указав имя требуемого выходного файла как символьную строку, что показано ниже:
ofstream file_object("FILENAME.EXT");
Если вы указываете имя файла при объявлении объекта типа ofstream, C++ создаст новый файл на вашем диске, используя указанное имя, или перезапишет файл с таким же именем, если он уже существует на вашем диске Следующая программа OUT_FILE.CPP создает объект типа ofstream и затем использует оператор вставки для вывода нескольких строк текста в файл BOOKINFO.DAT:
#include <fstream.h>
void main(void)
{
ofstream book_file("BOOKINFO.DAT");
book_file << "Учимся программировать на языке C++, " << "Вторая редакция" << endl;
book_file << "Jamsa Press" << endl;
book_file << "22.95" << endl;
}
В данном случае программа открывает файл BOOKINFO.DAT и затем записывает три строки в файл, используя оператор вставки. Откомпилируйте и запустите эту программу. Если вы работаете в среде MS-DOS, можете использовать команду TYPE для вывода содержимого этого файла на экран:
С:\> TYPE BOOKINFO.DAT <ENTER>
Как видите, в C++ достаточно просто выполнить операцию вывода в файл.
ЧТЕНИЕ ИЗ ВХОДНОГО ФАЙЛОВОГО ПОТОКА
Только что вы узнали, что, используя класс ofstream, ваши программы могут быстро выполнить операции вывода в файл. Подобным образом ваши программы могут выполнить операции ввода из файла, используя объекты типа ifstream. Опять же, вы просто создаете объект, передавая ему в качестве параметра требуемое имя файла:
ifstream input_file("filename.EXT");
Следующая программа FILE_IN.CPP открывает файл BOOKINFO.DAT, который вы создали с помощью предыдущей программы, и читает, а затем отображает первые три элемента файла:
#include <iostream.h>
#include <fstream.h>
void main(void)
{
ifstream input_file("BOOKINFO.DAT") ;
char one[64], two[64], three[64];
input_file >> one;
input_file >> two;
input_file >> three;
cout << one << endl;
cout << two << endl;
cout << three << endl;
}
Если вы откомпилируете и запустите эту программу, то, вероятно, предположите, что она отобразит первые три строки файла. Однако, подобно cin, входные файловые потоки используют пустые символы, чтобы определить, где заканчивается одно значение и начинается другое. В результате при запуске предыдущей программы на дисплее появится следующий вывод:
С:\> FILE_IN <ENTER>
учимся
программировать
Чтение целой строки файлового ввода
программы могут использовать cin.getline для чтения целой строки с клавиатуры. Подобным образом объекты типа ifstream могут использовать getline для чтения строки файлового ввода. Следующая программа FILELINE.CPP использует функцию getline для чтения всех трех строк файла BOOKINFO.DAT:
#include <iostream.h>
#include <fstream.h>
void main(void)
{
ifstream input_file("BOOKINFO.DAT");
char one[64], two[64], three [64] ;
input_file.getline(one, sizeof(one)) ;
input_file.get line(two, sizeof(two));
input_file.getline(three, sizeof(three)) ;
cout << one << endl;
cout << two << endl;
cout << three << endl;
}
В данном случае программа успешно читает содержимое файла, потому что она знает, что файл содержит три строки. Однако во многих случаях ваша программа не будет знать, сколько строк содержится в файле. В таких случаях ваши программы будут просто продолжать чтение содержимого файла пока не встретят конец файла.
· Заголовочный файл fstream.h определяет классы ifstream и ofstream, с помощью которых ваша программа может выполнять операции файлового ввода и вывода.
· Для открытия файла на ввод или вывод вы должны объявить объект типа ifstream или ofstream, передавая конструктору этого объекта имя требуемого файла.
· После того как ваша программа открыла файл для ввода или вывода, она может читать или писать данные, используя операторы извлечения (>>) и вставки (<<).
· Ваши программы могут выполнять ввод или вывод символов в файл или из файла, используя функции get и put.
· Ваши программы могут читать из файла целую строку, используя функцию getline.
· Большинство программ читают содержимое файла, пока не встретится конец файла. Ваши программы могут определить конец файла с помощью функции eof.
· Когда ваши программы выполняют файловые операции, они должны проверять состояние всех операций, чтобы убедиться, что операции выполнены успешно. Для проверки ошибок ваши программы могут использовать функцию fail.
· Если вашим программам необходимо вводить или выводить такие данные, как структуры или массивы, они могут использовать методы read и write.
· Если ваша программа завершила работу с файлом, его следует закрыть с помощью функции close.
Вопрос №23
При решении новых задач можно попытаться воспользоваться ранее написанными программами. Алгоритм, ранее разработанный и целиком используемый в составе других алгоритмов, называется вспомогательным. Применение вспомогательных алгоритмов позволяет разбить задачу на части, структурировать ее.
Вся программа условно может быть разделена на две части: основную и вспомогательную. В основной части производится простейшая обработка информации, организуется обращение к разным вспомогательным модулям (подпрограммам).
Вспомогательный алгоритм тоже может вызывать другие вспомогательные, длина такой цепочки вызовов теоретически не ограничена. Здесь и далее следующие пары слов используются как синонимы: алгоритм и программа, вспомогательный алгоритм и подпрограмма, команда и оператор, программа и модуль. Вспомогательными и основными алгоритмы являются не сами по себе, а по отношению друг к другу.
При использовании вспомогательных алгоритмов необходимо учитывать способ передачи значений исходных данных для них и получения результата от них. Аргументы вспомогательного алгоритма — это переменные, в которых должны быть помещены исходные данные для решения соответствующей подзадачи. Результаты вспомогательного алгоритма — это также переменные, где содержаться результаты решения этих подзадач, а также результатом может быть конкретное действие, которое совершает компьютер под действием подпрограммы.
Подпрограммы могут быть двух видов: подпрограмма без параметров и подпрограмма с параметрами. Обращение к подпрограмме может быть организовано из любого места основной программы или другой подпрограммы сколько угодно раз.
При работе с подпрограммами важными являются понятия формальных и фактических параметров. Формальные параметры — это идентификаторы входных данных для подпрограммы. Если формальные параметры получают конкретные значения, то они называются фактическими. Формальные параметры могут получить конкретные значения только в той программе, где производится обращение к данному модулю-подпрограмме. Тип и порядок записи фактических параметров должны быть такими же, как и формальных параметров. В противном случае результат работы программы будет непредсказуемым. Из этого следует, что фактические параметры используются при обращении к подпрограмме из основной, а формальные параметры — только в самом модуле.
Подпрограмма с параметрами используется для записи многократно повторяющихся действий при разных исходных данных.
При составлении подпрограмм с параметрами надо соблюдать следующие правила:
1) каждая подпрограмма имеет свое имя и список формальных параметров;
2) процедура из основной программы вызывается командой вызова, которая по форме ничем не отличается от вызова команды исполнителя. Результат присваивается одной или нескольким переменным, которые находятся в списке формальных параметров. Но результатом могут быть, конечно, не только значения переменных, но какое либо действие, выполненное ЭВМ
// программа НОД
#include <iostream.h>
#include <math.h>
void Nod(int m, int n, int &k);
void main()
{
int a, b, c, d, g, e, f;
cout << "Введите числители и знаменатели дробей:";
cin >> a >> b >> c >> d;
e = a * d - b * c;
f = b * d;
if (e==0)
cout << "Ответ: " << e;
else
{Nod(fabs(e),f,g);
e = e / g;
f = f / g;
cout << "Ответ: " << e << "/" << f;
}
}
void Nod(int m, int n, int &k)
{
while (m != n)
if (m > n) m -= n; else n -= m;
k = m;
}
Как видно из примера, объявление подпрограммы-функции находится в разделе описаний прототипов функций, а реализация после основной функции main. В заголовке подпрограммы содержится список формальных параметров с указанием их типа, которые условно можно разделить на входные и выходные (перед ними стоит &). Вообще при обращении к функции со списком параметров без &, внутри функции используются копии параметров, которые после выполнения удаляются. Знак & указывает компилятору что необходимо использовать саму переменную, а не ее копию. При обращении к функции указывается ее имя и список фактических параметров. Формальные и фактические параметры должны соответствовать по количеству и по типу.
Описание функции в С++ осуществляется следующим образом:
тип_возвращаемого_значения <Имя функции>(<список фактических параметров>);
Например,
void Nod(int e, int f, int &k);
int f1(float a);
long f2();
Функция всегда возвращает единственное значение. Как видно из примера 1, мы использовали тип void в качестве возращаемого типа. Т.е. указали компилятору, что наша функция не возвращает никакого значения.
Покажем, как изменится подпрограмма из примера, если ее записать в виде функции, возвращающей само значение НОД (без использования возвращаемой переменной).
int Nod(int m, int n)
{
while (m!=n)
if (m > n) m -=n; else n -= m;
return (n);
}
Итак, в теле функции хотя бы один раз встречается команда return, которая указывает, какое значение вернуть в качестве значения функции.
Вызов функции в основной будет следующим:
g = Nod(fabs(e), f);
Вообще, вызов функции может присутствовать в выражении, стоящем: в правой части операции присваивания, в операторе вывода, в качестве фактического параметра в вызове другой подпрограммы и т.д.
При решении задач целесообразно проанализировать условие, записать решение в крупных блоках (не являющихся операторами C++), детализировать каждый из блоков (записав в виде блоков, возможно, по-прежнему не операторов C++), и т.д., продолжать до тех пор, пока каждый из блоков не будет реализован с помощью операторов языка.