- •Лекция 1 Создание консольного приложения
- •2. Консоль. Построение консольного проекта
- •3. Запуск приложения
- •4. Сохранение и редактирование проекта
- •Лекция 2
- •4. Функции форматированного ввода и вывода
- •4.1. Функция форматированного ввода с клавиатуры
- •4.2. Функция форматированного вывода на экран
- •5. Математические функции
- •Лекция 3 Линейные вычислительные процессы
- •1. Алгоритм. Управляющие структуры
- •2. Линейные вычислительные алгоритмы
- •2.1. Условный оператор if()
- •2.2. Условное выражение
- •2.3. Оператор выбора switch()
- •Лекция 5 Программирование разветвляющихся вычислительных процессов
- •Лекция 6 Циклические вычислительные процессы.
- •1. Типы циклов
- •3. Операторы безусловного перехода
- •Лекция 7 Вычисление последовательностей
- •4. Примеры вычисления последовательностей
- •5. Структура алгоритмов вычисления рекуррентных последовательностей
- •Лекция 8 Одномерные массивы
- •1. Массивы
- •1.1. Примеры программ обработки одномерных массивов
- •1.2. Сортировка выбором
- •1.3. Сортировка простыми вставками
- •Лекция 10 Двухмерные массивы
- •1. Двухмерные массивы
- •Лекция 11 Алгоритмы матричной алгебры
- •1. Алгоритмы матричной алгебры
- •Лекция 12 Динамические массивы
- •1. Память компьютера. Адресное пространство
- •2. Динамическая память
- •3. Адреса и указатели
- •4. Указатели и массивы. Динамические массивы
- •5. Проблемы, связанные с указателями
- •6. Поразрядные операции
- •1.2. Способы объявления и обращения к элементам двухмерных массивов
- •Лекция 14 Символы и строки
- •1. Символьный тип данных
- •2. Строки
- •Лекция 15 Структуры
- •1. Понятие структуры
- •2. Определение нового имени типа
- •3. Массивы структур. Указатели на структуры
- •3.1. Определение статического массива структур
- •3.1. Определение динамического массива из n структур
- •Лекция 16 Файлы
- •1. Потоковый ввод-вывод данных
- •3. Понятие файла. Функции работы с файлами
- •Лекция 17 Файлы
- •Лекция 18 Функции пользователя
- •I. Приёмы построения алгоритмов
- •2. Понятие функции
- •2.1. Определение функции
- •2.2. Область видимости переменных
- •2.3. Параметры функции
- •2.4. Описание функции
- •2.5. Организация вызова функции
- •2.5. Передача параметров в функцию
- •3. Рекурсия
- •Лекция 20 Нахождение приближенного значения корня нелинейного уравнения
- •На отрезке [a;b] с заданной точностью eps
- •1.1. Метод дихотомии (половинного деления)
- •1.2. Метод хорд
- •1.3. Метод касательных (Ньютона)
- •Лекция 22 Объектно-ориентированное программирование
- •Полиморфизм – это свойство класса, позволяющее определить одно и то же по имени, но разное по смыслу действие. Основные этапы ооп:
- •Уточнённое имя принадлежит классу (т.Е. Компонентной) функции
- •Лекция 23 Объектно-ориентированное программирование
- •1. Конструкторы и деструкторы
- •1.2. Определение компонентных функций
- •Лекция 25 Объектно-ориентированное программирование
- •1. Свойства классов
- •1.1. Наследование классов
- •1.2. Полиморфизм
- •Библиографический список
5. Проблемы, связанные с указателями
Самые распространенные проблемами при использовании указателей:
-
попытка работать с указателем, не содержащим адреса ОП, выделенной под переменную;
-
потеря значения указателя из-за присваивания ему нового значения до освобождения ОП, которую он адресует;
-
неосвобождение ОП, запрошенной с помощью функции выделения динамической памяти.
При объявлении указателя на значение любого типа ОП для адресуемого значения не резервируется. Выделяется только ОП для переменной-указателя, но указатель при этом не получает никакого значения. Рассмотрим пример, содержащий грубую ошибку – попытку работать с неинициализированным указателем:
int *х; //указателю х выделена ОП, но х не содержит адреса ОП для
//конкретной переменной
*x=123; //ошибка
Присваивание ошибочно, так как указатель х не получил значения адреса, по которому должно быть расположено значение 123.
Не вдаваясь в подробности, можно сказать, что неинициализированное значение указателя может быть недопустимым значением адресов, что может привести к нарушению работоспособности всей программы. Компилятор не обнаруживает эту ошибку. Это должен делать разработчик программы.
Исправить ситуацию можно с помощью одной из функций выделения динамической памяти, например функции malloc(). Форма обращения к функции malloc():
<имя_указателя> = (<тип_указателя>)malloc(<объем_ОП>);
где <имя_указателя> – имя переменной-указателя; <тип_указателя> – тип значения, возвращаемого функцией malloc(); <объем_ОП> – количество байтов ОП, выделяемой адресуемой переменной.
Например,
х=(int*)malloc(sizeof(int));
При этом из кучи выделяется 4 байта ОП для целого значения, а адрес его размещения заносится в указатель х. При таком выделении памяти работоспособность программы не нарушается. Аргумент функции malloc() определяет объем ОП для целого значения с помощью операции определения объема ОП для размещения значения целого типа sizeof(int). Операция (int*), записанная перед функцией, означает, что адрес, возвращаемый функцией malloc(), рассматривается как указатель на переменную целого типа. Это операция приведения типов.
Таким образом, ошибки не будет при использовании операторов:
int *х; //x – имя указателя
x=(int*)malloc(sizeof(int));
//выделена ОП целому значению, на которое указывает х, и переменой х
//присваивается адрес выделенной ОП
*x=123; // переменная, на которую указывает х, получила значение
// 123
Освобождение ОП в куче выполняет функция free(). Ее аргументом является имя указателя, содержащего адрес освобождаемой и ранее выделенной динамической памяти, например free(x);
6. Поразрядные операции
Каждая переменная, объявленная в программе занимает в ОП некоторый участок, который содержит столько байт, сколько требует для размещения в ОП тип переменной. В свою очередь каждый байт – совокупность из восьми бит. Бит – это минимальная единица хранения информации в памяти компьютера. В языке С++ изменять значение переменной можно не только при помощи операции присваивания (т.е. целиком), но и с помощью поразрядных операций (по битам). Поразрядные операции называют побитовыми операциями – операции, с помощью которых можно изменять значение переменной по битам. Побитовыми операциями являются проверка (логические), сдвиг и присвоение значения битам данных. Такие операции могут осуществляться только над переменными типа int или char. Побитовые операции и результаты их работы приведены в табл.7.
Таблица 7 |
|||||
Значение операнда 1 (оп1) |
Значение операнда 2 (оп2) |
Название операции |
|||
Поразрядное отрицание |
Исключающее ИЛИ |
Поразрядное И |
Поразрядное ИЛИ |
||
~(оп1) |
(оп1)^(оп2) |
(оп1)&(оп2) |
(оп1)|(оп2) |
||
Результат |
|||||
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
0 |
0 |
1 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
При помощи операций легко проверить «состояние» того или иного бита переменной. Для этого на переменную «накладывают» маску – переменную, которая содержит проверяемую последовательность 0 и 1. Типы маски и переменной, на которую накладывается маска, должны совпадать. Наложение осуществляется при помощи операций ^, &, | :
~
Результат
двойного побитового отрицания –
первоначальное значение переменной
~00110010 == 11001101
^ |
11001101 |
& |
11001101 |
| |
11001101 |
01101100 |
01101100 |
01101100 |
|||
|
10100001 |
|
01001100 |
|
10101101 |
Кроме поразрядных операций (см. табл.7) существуют две побитовые операции сдвига: << – операция сдвига всех битов переменной влево; >> – операция сдвига всех битов переменной вправо. При использовании этих операций указывается число разрядов, на которое происходит сдвиг битов вправо или влево. Побитовый сдвиг не является циклическим, т.е. если при сдвиге битов произошел выход за границы участка ОП, где размещена изменяемая переменная, то вышедшие за границу биты теряются. С помощью операций сдвига можно умножать или делить переменную на число, кратное двум. Форма записи этих операций:
<имя_переменной> >> <количество_разрядов>;
<имя_переменной> << <количество_разрядов>;
Например,
p=p>>3; //значение переменной p уменьшается в 23 = 8 раз
c=c<<4; // значение переменной p увеличивается в 24 = 16 раз
Наиболее часто побитовые операции используются при системном программировании. Однако они бывают полезны и при решении прикладных задач.
Лекция 13
Динамические массивы
Цели:
-
получить представление о способах объявления и обращения к элементам массивов;
-
освоить методику написания алгоритмов с использованием динамических массивов, перевода таких алгоритмов на язык программирования С++ и разработки соответствующего проекта в среде Visual C++ 6.0.
1. Способы объявления и обращения к элементам массивов
1.1. Способы объявления и обращения
к элементам одномерных массивов
Объявление одномерного массива с явным указанием количества элементов массива:
<тип_элементов_массива> <имя_массива> [<количество_элементов>];
Обращение к элементам одномерного массива в общем случае можно представить индексированием:
<имя_массива> [<выражение>];
где <имя_массива> – указатель–константа, адрес нулевого элемента массива; <выражение> определяет индекс элемента массива.
Элементы одномерного массива располагаются в ОП последовательно друг за другом: нулевой, первый и т.д. При объявления массива
int а[10];
компилятор выделяет массиву ОП для размещения его элементов в размере sizeof(<тип_элементов_массива>)*<размер_массива> байт, т.е. 4 ∙ 10 = 40 байт. Запись &a[i] равносильна записи (а + i). В программе &a[i] или (а + i) определяет адрес i-го элемента массива. На машинном уровне (в соответствии с определением операций над указателями) адрес i-го элемента массива &a[i] формируется в виде а + i*sizeof(int) .
В связи с вышеизложенным, будут справедливы соотношения:
&а а + 0 &а[0] – адрес нулевого элемента массива а;
а + 2 &а[2] – адрес второго элемента массива а;
а + i &a[i] – адрес i-го элемента массива а;
*а *(а + 0) *(&а[0]) а[0] – значение нулевого
элемента массива а;
*(а+2) а[2] – значение второго элемента массива;
*(а + i) a[i] – значение i-го элемента массива;
*а + 2 а[0] + 2 – сумма значений а[0] и 2.
Если р – указатель на тип данных, совпадающий с типом элементов массива а и адрес, который содержит указатель р, совпадает с адресом массива а, то а и р взаимозаменяемы. При этом
р &а[0] а + 0;
р + 2 &а[2] а + 2;
*(р + 2) *(&а[2]) а[2];
*(p + i) *(&a[i]) a[i].