- •3.8. Упражнения 49
- •Введение
- •1. Базовые элементы
- •1.1. Комментарии
- •1.2. Функция «main»
- •1.3. Идентификаторы и пространства имен
- •1.4. Типы
- •1.5. Переменные
- •1.7. Константы
- •1.6. Преобразование типов
- •1.8. Потоковый ввод – вывод
- •1.9. Операции
- •1.9.1. Арифметические операции
- •1.9.2. Поразрядные операции
- •1.9.3. Условный оператор
- •1.9.4. Операции отношения
- •1.9.5. Логические операции
- •1.10. Стандартные математические функции
- •1.11. Упражнения
- •2. Базовые конструкции
- •2.1 Ветвление
- •2.1.1. Конструкция «if – else»
- •2.1.2. Конструкция «switch»
- •2.2. Циклы
- •2.2.1. Цикл «for»
- •2.2.2. Цикл «while»
- •2.2.3. Цикл «do – while»
- •2.2.4. Операторы циклов
- •2.2.5. Вложенность
- •2.3. Упражнения
- •2.3.1. Ветвления
- •2.3.2. Циклы
- •2.3.3. Вложенность
- •3. Указатели. Массивы. Структуры
- •3.1. Указатели
- •3.2. Массивы
- •3.2.1. Статические массивы
- •3.2.2. Динамические массивы
- •3.4. Ссылки
- •3.5. Структуры
- •3.6. Объединение
- •3.7. Переопределение типов
- •3.8. Упражнения
- •3.8.1. Статические и динамические массивы
- •3.8.2. Многомерные массивы
- •3.8.3. Строки
- •4. Функции
- •4.1. Простые функции
- •4.2. Рекурсия
- •4.3. Указатели и ссылки
- •4.3.1. Передаваемые параметры
- •4.3.2. Возвращаемые значения
- •4.3.3. Ссылки и структуры в функциях
- •4.3.4. Указатели и массивы в функциях
- •4.3.5. Указатели и ссылки на функцию
- •4.4. Шаблоны функций
- •4.5. Упражнения
- •4.5.1. Простые функции
- •4.5.2. Параметры – ссылки и параметры — указатели
- •5. Стандартные классы
- •5.1. Класс «pair»
- •5.2. Класс «complex»
- •5.3. Класс «string»
- •5.4. Контейнеры. Алгоритмы. Итераторы
- •5.5. Класс «vector»
- •5.6. Упражнения
- •5.6.1. Класс «string»
- •5.6.2. Класс «vector»
- •6. Файловый ввод – вывод
- •6.1. Операции
- •6.2. Методы
- •6.3. Флаги
- •6.4. Двоичный режим
- •6.5. Упражнения
- •Ллитература
3.2. Массивы
Массивы предназначены для хранения ряда однотипных значений так, чтобы доступ к отдельным значениям был возможен по их позиции в этом массиве. Элементы массива располагаются в памяти последовательно, занимая идущие друг за другом ячейки памяти. Так как память для массива выделяется в момент компиляции, его размерность должна быть известна заранее, другими словами, она должна являться константой.
3.2.1. Статические массивы
Массив индексируется с нуля, поэтому и номер его последнего элемента на единицу меньше его размера. Определение массива происходит с использованием фигурных скобок, в которых помещаются требуемые значения. При определении значений элементов массива без указания его размера, он вычисляется путем подсчета числа элементов в «{}» – списке инициализации. В случае, если список неполный, оставшиеся элементы инициализируются нулевыми значениями, что справедливо и для вовсе пустого списка. Список инициализации, превышающий размер массива, вызовет ошибку. Доступ к элементам массива осуществляется с помощью операции «[]» с указанием индекса элемента.
Листинг 3.2. Массивы.
#include <iostream>
using namespace std;
int main (void)
{
int array1[10];
for (int i = 0; i < 10; i++)
array1[i] = i;
int array2[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int array3[2*5] = { 0, 1, 2, 3, 4 };
for (int i = 0; i < 10; i++)
cout << array1[i] << " " << array2[i] << " " << array3[i] << endl;
return 0;
}
Массивы в С++ могут быть многомерными. Такие массивы располагаются в памяти, также как и одномерные. Обращение к элементам массива происходит через обращение к подмассивам. Инициализацию многомерных массивов продемонстрирует следующий листинг.
Листинг 3.3. Многомерные массивы.
#include <iostream>
using namespace std;
int main (void)
{
const int dasize = 3;
double darray2d[dasize][dasize] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } };
for (int i = 0; i < dasize; i++)
{
for (int j = 0; j < dasize; j++)
cout << darray2d[i][j] << " ";
cout << endl;
}
const int fasize = 10;
float farray4d[fasize][fasize][fasize] = {};
return 0;
}
3.2.2. Динамические массивы
В том случае, если количество элементов массива неизвестно заранее, можно воспользоваться одним из двух способов. Первый заключается в том, чтобы заранее выделить в стековом фрейме достаточное количество памяти, указав такую размерность, которая, как ожидается, не будет превышена. Однако мало того, что такой подход крайне неэффективен в аспекте занимаемой программой памяти, так еще все же может быть превышена указанная размерность. При втором подходе, память для элементов массива выделяется динамически, а доступ к ней производится через указатель. Важным аспектом здесь является тот факт, что доступ к обычному массиву происходит по указателю на его первый элемент, а адрес некоторого элемента массива представляет собой число, равное сумме адреса начального элемента и произведения индекса требуемого элемента на тип элементов массива. Так третий элемент массива «int» – значений (для «int», размер которого составляет четыре байта), расположенного начиная с адреса, к примеру, 0x22FF00, имеет адрес 0x22FF0С.
Благодаря этому, после выделения памяти работа с идентификатором указателя на динамически выделенную память происходит также, как и с идентификатором обычного массива.
Листинг 3.4. Динамические массивы.
#include <iostream>
using namespace std;
int main (void)
{
unsigned int x;
int *pinta;
cin >> x;
//выделение памяти под x значений типа int;
pinta = new int[x];
for (int i = 0; i < x; i++)
pinta[i] = i;
//операция освобождения памяти для массивов имеет «[]»;
delete[] pinta;
return 0;
}
При использовании многомерных динамических массивов следует учитывать тот факт, что динамические подмассивы не располагаются в смежных ячейках. В связи с этим, для создания, к примеру, двухмерного массива необходимо создать указатель на одномерный массив, каждый элемент которого, в свою очередь, будет также являться указателем на массив. Создание каждого отдельного подмассива, как и его удаление, должно быть явным.
Листинг 3.5. Многомерные динамические массивы.
#include <iostream>
using namespace std;
int main (void)
{
unsigned int x, y;
cin >> x >> y;
//указатель на массив указателей на миссив
// из x элементов типа int каждый;
int **pinta = new int*[x];
//явное создание подмассивов;
for (int i = 0; i < x; i++)
pinta[i] = new int[y];
//явное удаление подмассивов;
for (int i = 0; i < x; i++)
delete[] pinta[i];
delete[] pinta;
return 0;
}
3.3. C – Строки
В языке С++ существует два вида строк. Первый тип – строки в стиле языка С, представляющие из себя «char» – массивы с нулевым завершающим элементом. Работа с такими строками не отличается от работы с обыкновенными «char» – массивами. В стандартном библиотечном файле «cstring» объявлены функции по работе с С – строками.
Вторым типом строк являются объекты стандартного класса «string». Данный класс будет рассмотрен позднее.
Таблица 3.1. Основные функции по работе с С – строками.
Определение |
Описание |
char *strcat(str1, str2), char *str1, *str2; |
Сцепление двух строк |
char *strncat(str1, str2, n); char *str1, *str2; int n; |
Сцепление двух строк, где из второй берется не более n символов |
int strcmp(str1, str2); char *str1, *str2; |
Сравнение двух строк в лексикографическом порядке |
int strncmp(str1, str2, n); char *str1, *str2; int n; |
Сравнение первых n символов двух строк в лексикографическом порядке |
char *strcpy(str1, str2); char *str1, *str2; |
Копирование строки str1 в строку str2 |
char *strncpy(str1, str2, n); char *str1, *str2; int n; |
Копирование не более первых n символов строки str1 в строку str2 |
int strlen(str); char *str; |
Определение длинны строки (без нулевого символа) |
char *strchr(str, n); char *str; int n; |
Нахождение первого вхождения символа n |
char *strrchr(str, n); char *str; int n; |
Нахождение последнего вхождения символа n |
char *strpbrk(str1, str2); char *str1, *str2; |
Нахождение в строке str1 любой символ из строки str2 |
int strspn(str1, str2); char *str1, *str2; |
Определение длинны части строки str1, содержащей символы из строки str2 |
int strcspn(str1, str2); char *str1, *str2; |
Определение длинны части строки str1, не содержащей символы из строки str2 |
char *strtok(str1, str2); char *str1, *str2; |
Выделение из строки str1 отрезков, разделенных символами из строки str2 |
Листинг 3.6. Подсчет длинны С – строки.
#include <iostream>
#include <cstring>
using namespace std;
int main (void)
{
char *cstr = "Hellow, World!";
char *pstr = cstr; //указатель на начало строки;
int i = 0;
do
{
//подсчет количества итераций;
i++;
//инкремент указателя, пока он не
//станет ссылаться на конец строки;
} while (*++pstr);
//вывод строки;
cout << cstr << endl;
//длинна через счетчик;
cout << i << endl;
//длинна через указатели;
cout << pstr - cstr << endl;
//длинна через стандартную функцию;
cout << strlen(cstr) << endl;
return 0;
}
Стандартный класс «string» предоставляет больше функциональности и удобства, чем строки в стиле языка С. Однако, в ряде задач данный класс не может быть использован, в таких случаях применяются старый формат строк. Примером может служить разбор параметров командной строки.
Функции «main()» оболочкой передается количество входных параметров и сами параметры, с которыми программа была вызвана. Однако, эти параметры могут быть проигнорированы, если определить функцию «main()» как непринимающую значений. Для количества параметров обычно используется «int» – переменная с именем «argc», а для самих параметров – указатель на С-строки «argv», с которым можно работать как с двухмерным массивом символов. Первым параметром всегда является имя самой программы. Любой параметр может быть задан в двойных кавычках «""», тогда строка – параметр будет завершаться не символом пробела, а завершающей кавычкой, что позваляет задавать параметры с внутренними разделительными символами.
Листинг 3.7. Удаление из строки всех вхождений символа.
/*Заданы должны быть три параметра: имя программы (по умолчанию), * строка для удаления символов и сам удаляемый символ,
* представляемый в программе массивом из одного элемента.
*/
#include <iostream>
#include <cstring>
using namespace std;
int main (int argc, char *argv[])
{
if (argc == 3 && !argv[2][1]) { //если параметры верны;
for (int i = 0; i < strlen(argv[1]); i++) //для всех символов строки;
if (argv[1][i] == argv[2][0]) //если найден искомый;
for (int j = i; j < strlen(argv[1]); j++) //начиная с него;
argv[1][j] = argv[1][j+1]; //смещение на один символ;
cout << argv[1] << endl;
} else {
cout << "use two params - word and char" << endl;
}
return 0;
}
Смещение всех символов на одну позицию влево «затирает» найденный символ, уменьшая размер строки. Изменение размера строки происходит посредством смещения завершающего нулевого символа. Однако, следует помнить, что функция «strlen()» определяет длину строки без учета этого символа.
