- •Предисловие
- •Введение
- •Введение в программирование
- •1.1. Предисловие к курсу
- •1.2. Идеология языка
- •1.3. Обзор среды Microsoft Developer Studio
- •1.4. Жизненный цикл программного обеспечения
- •1.5. Общая структура программы
- •1.6. Директивы препроцессора
- •1.7. Построение исполняемого файла
- •1.8. Строительные блоки программы
- •Контрольные вопросы
- •Типы данных. Переменные. Массивы. Операции и Указатели
- •Стандартные типы и размеры данных
- •2.1.1. Объявление переменных
- •Управляющие символьные константы
- •2.2. Объявление указателя
- •2.2.1. Операции разыменования и взятия адреса
- •2.2.2. Указатели на указатели
- •2.2.3. Арифметические операции с указателями
- •2.3. Массивы
- •2.3.1. Инициализация массивов
- •2.3.2 Динамические массивы
- •2.3.3. Методы доступа к элементам массивов
- •2.3.4. Массивы указателей
- •2.4. Строки
- •2.5. Операции
- •2.5.1. Арифметические операции
- •Арифметические операции
- •2.5.2 Операции сравнения и логические операции
- •Операции сравнения и логические операции
- •2.5.3. Побитовые операции
- •Побитовые операции
- •Контрольные вопросы
- •3.1. Базовые операторы
- •3.1.1. Оператор выражение
- •3.2.2. Оператор switch
- •3.3.4. Оператор goto
- •3.4. Операторы цикла
- •3.4.1. Оператор for
- •3.4.2. Оператор while
- •3.4.3. Оператор do..While
- •Контрольные вопросы
- •Стандартный ввод/вывод. Работа с файлами.
- •4.1. Роль стандартного ввода/вывода
- •4.1.1. Основные функции стандартного ввода/вывода
- •4.2. Понятие файла
- •4.2.1. Строение файлов
- •4.2.2. Порядок работы с файлом
- •4.2.3. Обзор библиотечных функций с для работы с файлами
- •4.3. Программные конструкции при работе с файлами
- •4.3.1. Открытие/закрытие файла
- •4.3.2. Цикл посимвольного чтения содержимого файла
- •4.3.3. Цикл построчного чтения содержимого файла
- •Контрольные вопросы
- •Функция. Пользовательские типы данных.
- •5.1. Понятие функции
- •5.1.1. Определение функции
- •5.1.2. Формальные параметры
- •5.1.3. Тип возвращаемого значения
- •5.1.4. Тело функции
- •5.1.5. Фактические параметры
- •5.1.6. Рекурсивные вызовы
- •5.1.7. Передача параметров
- •5.1.8. Библиотеки стандартных функций
- •5.2. Пользовательские типы данных.
- •5.2.1. Ключевое слово typedef
- •5.2.2. Перечислимый тип данных
- •5.2.3. Понятие структуры
- •5.2.4. Указатели на структурный объект
- •Контрольные вопросы
- •Работа с динамической памятью. Динамические структуры данных
- •6.1. Работа с динамической памятью
- •6.1.1. Статическое и динамическое распределение памяти
- •6.1.2. Основные принципы динамического распределения
- •6.1.3. Способы работы с динамической памятью
- •6.2. Динамические структуры данных
- •6.2.1. Стек
- •6.2.2.Линейный список
- •Контрольные вопросы
- •Объектно-ориентированное программирование
- •7.1. Критерии качества декомпозиции проекта
- •7.2. Новые концепции программирования
- •7.3. Достоинства ооп
- •7.4. Объекты и классы в ооп
- •7.4.1. Определение класса
- •7.4.2. Использование класса
- •7.4.3. Вложенные классы
- •Контрольные вопросы
- •Конструкторы и Перегрузка операций.
- •8.1. Перегрузка операций
- •8.1.1. Перегрузка операций внешними функциями
- •8.1.2. Перегрузка операций методами класса
- •8.2. Конструкторы и деструктор
- •8.2.1. Конструкторы и параметры
- •Контрольные вопросы
- •9.1. Простое открытое наследование
- •9.1.1 Конструкторы и деструкторы при наследовании
- •9.1.2. Поля и методы при наследовании
- •9.1.3. Вложенные классы и наследование
- •9.1.4. Закрытое наследование
- •9.1.5. Виртуальные функции
- •9.1.6. Чистые виртуальные функции и абстрактные классы
- •9.3. Основы программирования под Windows
- •9.3.1. Типы данных в Windows
- •9.4. Cреда Microsoft Developer Studio
- •9.4.1. Библиотека mfc
- •9.4.2. Архитектура приложения
- •9.4.3. Каркас приложения
- •9.4.4. Проект приложения
- •Контрольные вопросы
- •Заключение
- •Список Литературы
2.3. Массивы
Массив представляет собой набор однотипных ячеек, располагающихся в памяти последовательно. У набора есть общее имя, а доступ к конкретной ячейке осуществляется через индекс или смещение.
При объявлении массива необходимо указать тип элемента, имя и количество элементов (ячеек).
тип имя_переменной[размер]
Например:
int arr1[10]; // массив из 10 ячеек типа int;
double arr2[50]; // массив из 50 ячеек типа double;
Для доступа к i-му элементу используют запись:
vals[i] где i может принимать значение от 0 до N-1, N-число элементов.
Как и другие переменные, массив должен объявляться явно, чтобы компилятор мог выделить память для него. Здесь тип объявляет базовый тип массива, т.е. тип его элементов, а размер определяет, сколько элементов содержится в массиве.
В языке С/С++ массивы могут иметь больше двух размерностей. Максимально допустимое количество размерностей задается компилятором. Общий вид объявления многомерного массива таков:
тип имя_переменной[размер1] [размер2] [размер3]… [размерN]
Например:
int vals[5][7]; // двумерный массив, размером 5х7;
char text[10][25][80]; // трехмерный массив 10 x 25 x 80.
Таким образом, для двумерного массива объявлением является прямоугольная матрица, а для трехмерного - куб с ячейками. Однако в памяти ЭВМ все массивы похожи на цепочки – элементы располагаются друг за другом. Например:
int a[2][3]; /* представлено в виде матрицы;
a[0][0] a[0][1] a[0][2];
a[1][0] a[1][1] a[1][2] */.
Массивы, имеющие больше трех размерностей, используются редко, поскольку они занимают слишком большой объем памяти. Например, четырехмерный массив символов размерностью 10×6×9×4 занимает 2160 байт.
2.3.1. Инициализация массивов
Общий вид инициализации массива не отличается от инициализации обычных переменных:
тип_массива имя_массива [размер1]…[размерN] = {список_значений};
Список_значений представляет собой список констант, разделенных запятыми. Тип констант должен быть совместным с типом массива. Первая константа присваивается первому элементу массива, вторая – второму и т.д. Обратите внимание на то, что после закрывающейся фигурной скобки } обязательно должна стоять точка с запятой.
Инициализация одномерного массива осуществляется так:
int vals[10]={1,2,3,4,5,6,7,8,10};
Инициализация двумерного массива может быть выполнена следующим образом:
int b[2][2] = { { 1,2 }, { 3,4 } };
При инициализации массива можно опустить одну или несколько размерностей:
int b[]3[] = { { 1,2 }, { 3,4 } };
Если при инициализации указано меньше значений для строк, то оставшиеся элементы инициализируются 0, т.е. при описании int b[2][2] = { { 1,2 }, { 3 } }; элементы первой строки получат значения 1 и 2, а второй 3 и 0.
2.3.2 Динамические массивы
Если до начала работы программы неизвестно, сколько в массиве элементов, в программе следует использовать динамические массивы. Память под них выделяется с помощью операции new или функции malloc в динамической области памяти во время выполнения программы. Адрес начала массива хранится в переменной, называемой указателем. Например:
int n = 10;
int *a = new int[n];
double *b - (double *)malloc(n *sizeof (double));
Во второй строке описан указатель на целую величину, которому присваивается адрес начала непрерывной области динамической памяти, выделенной с помощью операции new. Выделяется столько памяти, сколько необходимо для хранения n величин типа int. Величина n может быть переменной.
Обращение к элементу динамического массива осуществляется так же, как и к элементу обычного – например а[3]. Можно обратиться к элементу массива и другим способом – *(а + 3). В этом случае мы явно задаем те же действия, что выполняются при обращении к элементу массива обычным образом. Рассмотрим их подробнее. В переменной-указателе а хранится адрес начала массива. Для получения адреса третьего элемента к этому адресу прибавляется смещение 3. Операция сложения с константой для указателей учитывает размер адресуемых элементов, то есть на самом деле индекс умножается на длину элемента массива: а + 3 *sizeof(int). Затем с помощью операции * (разадресации) выполняется выборка значения из указанной области памяти.
Если динамический массив в какой-то момент работы программы перестает быть нужным и мы собираемся впоследствии использовать эту память повторно, необходимо освободить ее с помощью операции delete[], например:
delete [] a; // Размерность массива при этом не указывается.
Сумма элементов правее последнего отрицательного. Написать программу, которая для вещественного массива из п элементов определяет сумму его элементов, расположенных правее последнего отрицательного элемента.
В этой задаче размерность массива задана переменной величиной. Предполагается, что она будет нам известна на этапе выполнения программы до того, как мы будем вводить сами элементы. В этом случае мы сможем выделить в динамической памяти непрерывный участок нужного размера, а потом заполнять его вводимыми значениями. Если же стоит задача вводить заранее неизвестное количество чисел до тех пор, пока не будет введен какой-либо признак окончания ввода, то заранее выделить достаточное количество памяти не удастся и придется воспользоваться так называемыми динамическими структурами данных, например списком. Мы рассмотрим эти структуры на девятом семинаре, а пока остановимся на первом предположении – что количество элементов массива вводится с клавиатуры до начала ввода самих элементов.
Вот как выглядит построенная по этому алгоритму программа:
finclude <iostream.h>
int main()
{
int n;
cout << " Введите количество элементов "; cin >> n;
int i, ineg;
float sum, *a = new float [n] // 1
cout << " Введите элементы массива ";
for (i = 0; i < n; i++) cin >> a[i];
for (i = 0; i < n; i++) cout << a[i] << ' '; // 2.
for (i = 0; i < n; i++) if (a[i] < 0) ineg = i; // 3
for (sum = 0; i = ineg + 1; i < n; i++) sum +- a[i]; // 4
cout << " Сумма " << sum;
return 0;
}
Поскольку количество элементов заранее не задано, память под массив выделяется в операторе 1 на этапе выполнения программы с помощью операции new. Выделяется столько памяти, сколько необходимо для хранения п элементов вещественного типа, и адрес начала этого участка заносится в указатель а.
Номер последнего отрицательного элемента массива формируется в переменной i neg. При просмотре массива с помощью оператора 3 в эту переменную последовательно записываются номера всех отрицательных элементов массива, таким образом, после выхода из цикла в ней остается номер самого последнего.
С целью оптимизации программы может возникнуть мысль объединить цикл нахождения этого номера с циклами ввода и контрольного вывода элементов массива, но мы не советуем так делать, потому что ввод данных, их вывод и анализ – разные по смыслу действия, и смешивание их в одном цикле не прибавит программе ясности. После отладки программы контрольный вывод (оператор 2) можно удалить или закомментировать.
Если не останавливаться на достигнутом и подумать, можно предложить и более рациональное решение этой задачи: просматривать массив в обратном порядке, суммируя его элементы, и завершить цикл, как только встретится отрицательный элемент:
finclude <iostream.h>
int main()
{
int n;
cout << "Введите количество элементов: "; cin >> n;
float *a = new float [n];
int i;
cout << "Введите элементы массива: ";
for (i = 0; i < n; i++) cin >> a[i];
bool flag_neg = false;
float sum = 0;
for (i = n – 1; i >= 0; i++)
if (a[i]<0) { flag_neg = true; break; }
sum+= a[i];
}
В этой программе каждый элемент массива анализируется не более одного раза, а ненужные элементы не просматриваются вообще. Для больших массивов это играет роль, поэтому последний вариант программы предпочтительнее, хотя на вид он отличается от предыдущего незначительно.
Для исчерпывающего тестирования этой программы необходимо ввести по крайней мере три варианта исходных данных – когда массив содержит один, несколько или ни одного отрицательного элемента.