- •1.1.1 Пример программы, выводящей текст на экран (пример 1)
- •1.1.2 Директивы препроцессору (подключение заголовочных файлов)
- •1.1.3 Комментарии
- •1.1.4 Функции
- •1.1.5 Ввод и вывод на экран
- •1.2. Переменные и их объявление
- •1.2.1 Пример программы cложения целых чисел (пример 2)
- •1.2.2 Переменные и их объявление
- •1.3. Арифметические операторы
- •1.3.1 Примеры арифметических операций (пример 3)
- •1.3.2 Группировка подвыражений с помощью скобок
- •1.4. Логические выражения и оператор if
- •1.4.1 Условные конструкции. Пример условных конструкций (пример 4)
- •1.4.2 Логические выражения. Логические операции и, или, не (пример 5)
- •1.4.3 Типичные ошибки
- •1.4.4 Вложенные условия
- •1.5. Арифметический логический оператор (пример 6)
- •1.6. Селективные конструкции
- •1.6.1 Селективные конструкции. Пример определения оценки в зависимости от количества баллов (пример 6)
- •1.6.2 Оператор Switch. Пример меню с выбором действия (пример 7)
- •1.7. Циклы while и do…while
- •1.7.1 Цикл с предусловием while. Пример возведения в степень в цикле (пример 8)
- •1.7.2 Цикл с постусловием do...While
- •1.8. Пошаговый цикл for
- •1.8.1 Пример работы оператора for - вычисление суммы чисел (пример 9)
- •1.8.2 Пошаговый цикл for
- •1.8.3 Операторы break и continue
- •1.8.4 Пример вычисление факториала (пример 10)
- •1.9. Функции
- •1.9.1 Использование функций библиотеки stl (пример 11)
- •1.9.2 Определение новых функций
- •1.9.3 Пример функции (пример 12)
- •1.10. Размещение программ и данных в памяти
- •1.11. Ссылки и указатели
- •1.11.1. Ссылки
- •1.11.2. Указатели
- •1.11.3. Передача параметров в функцию по ссылке и указателю
- •2.2 Организация ввода/вывода
- •2.3 Строковые переменные и константы
- •2.4 Математические функции
- •3.1. Массивы
- •3.1.1. Одномерный массив
- •3.1.2. Динамическое размещение одномерного массива
- •3.1.3. Передача массива в функцию (пример 3.1)
- •3.1.4. Двумерный массив
- •3.1.5. Динамическое размещение двумерного массива (пример 3.2)
- •3.2 Контейнеры
- •3.3. Вектор vector (пример 3.3)
- •4.4. Список list
- •3.4.1. Списки
- •3.4.2. Итераторы
- •3.4.3. Пример работы со списком с использованием итераторов (пример 3.4)
- •3.5. Очереди и стек
- •3.5.1. Двусторонняя очередь deque (пример 3.5)
- •3.5.2. Стек stack
- •3.5.3. Очередь queue
- •3.6. Ассоциативные контейнеры
- •3.6.1. Контейнер map (пример 3.7)
- •3.6.2. Контейнер set (пример 3.8)
- •3.7. Алгоритмы
- •4.1 Структуры
- •4.1.1. Пример 4.1. Структура для работы с компонентами цвета
- •4.1.2. Передача абстрактных типов в функцию
- •4.1.3. Создание функций-членов для абстрактного типа данных. Пример 4.2. Структура для работы с компонентами цвета со встроенной функцией.
- •4.2. Классы
- •4.2.1. Пример 4.3. Класс Линза
- •4.2.2. Директивы препроцессору # if ! defined, # endif (проверка на повторное подключение)
- •4.2.3. Тип доступа к членам класса
- •4.2.4. Принципы объектно-ориентированного проектирования
- •4.2.5. Типы функций-членов класса
- •4.3 Конструкторы и деструкторы класса
- •4.3.1. Конструкторы
- •4.3.2. Деструктор (пример 4.4. Конструктор и деструктор класса Матрица)
- •4.3.3. Проверка правильности параметров. Исключительные ситуации
- •4.4. Модификаторы, селекторы и другие члены классов
- •4.4.1. Модификаторы и селекторы
- •4.4.2. Ключевые слова const и inline
- •4.4.3. Функции-утилиты
- •4.4.4. Сохраняемость
- •5.1. Типы наследования. Видимость членов классов
- •5.1.1. Наследование
- •5.1.2. Пример 5.1. Линза и зеркало как оптические детали
- •5.1.3. Последовательность вызова конструкторов
- •5.1.4. Типы наследования. Видимость членов классов
- •5.1.5. Множественное наследование
- •5.2. Виртуальные функции. Абстрактные классы
- •5.2.1. Виртуальные функции
- •5.2.2. Абстрактные классы
- •6. Полиморфизм
- •6.1. Перегрузка функций
- •6.1.1. Перегрузка функций
- •6.1.2. Преобразование типов
- •6.1.3. Параметры функций по умолчанию
- •6.2. Перегрузка операторов
- •6.2.1. Пример 6.1 (класс Complex (комплексное число))
- •6.2.6. Перегрузка операторов с присваиванием
- •6.2.7. Перегрузка преобразования типов
- •6.2.8. Перегрузка оператора доступа по индексу
- •6.2.9. Перегрузка операторов ввода/вывода
- •6.2.10. Неперегружаемые операторы
- •6.3. Шаблоны функций и классов
- •6.3.1. Шаблоны функций. Пример 6.2 (шаблон функции)
- •6.3.2. Шаблоны функций с несколькими параметрами. Пример 6.3 (шаблон функции с несколькими параметрами)
- •6.3.3. Шаблоны классов. Пример 6.4 (шаблон класса Комплексное число)
- •6.4. Объекты-функции. Предикаты
- •6.4.1. Объекты-функции. Пример 6.5 (использование объектов-функций)
- •6.4.2. Предикаты. Пример 6.6 (использование предикатов)
1.11. Ссылки и указатели
1.11.1. Ссылки
Ссылка (referenсe) - это переменная особого вида, которая представляет собой альтернативное имя (псевдоним, alias) другой переменной:
int &a = b;
В данном примере a - новое альтернативное имя для переменной b. a и b ссылаются на одно и то же место в памяти.
Особенность ссылок заключается в том, что ссылку нельзя объявить без инициализации, она сразу должна на что-то ссылаться, то есть быть псевдонимом для какого-то объекта. Псевдонима для "ничто" не может существовать.
/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 14. Ссылки
//
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// СПб НИУ ИТМО
/////////////////////////////////////////////////////////////////////////////
// подключение библиотеки ввода-вывода
#include <iostream>
// подключение стандартного пространства имен для использования библиотек
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// функция main начинает исполнение программы
void main()
{
//----------------------
int i=1; // объявление переменной
int &r=i; // объявление и инициализация ссылки на переменную
// вывод на экран значений переменной и ссылки на нее
cout<<i<<endl; // 1
cout<<r<<endl; // 1
cout<<endl;
// вывод на экран адресов переменной и ссылки на нее
cout<<&i<<endl; // 0x0012ff6c
cout<<&r<<endl; // 0x0012ff6c
cout<<endl;
//----------------------
i+=5; // изменение переменной
// вывод на экран значений переменной и ссылки на нее
cout<<i<<endl; // 6
cout<<r<<endl; // 6
cout<<endl;
//----------------------
i-=5; // изменение переменной
// вывод на экран значений переменной и ссылки на нее
cout<<i<<endl; // 1
cout<<r<<endl; // 1
cout<<endl;
//----------------------
r=0; // изменение ссылки
// вывод на экран значений переменной и ссылки на нее
cout<<i<<endl; // 0
cout<<r<<endl; // 0
cout<<endl;
}
/////////////////////////////////////////////////////////////////////////////
1.11.2. Указатели
На хранение адреса в современных вычислительных системах отводится 32 бита (4 байта) или 64 бита (8 байт). Это значит, что теоретически программы могут управлять памятью объемом около 4096 Мб (а для 64-разрядных систем - около 16777215 Тб). Для работы с памятью каждая из её ячеек нумеруется.
Пусть для хранения переменной i типа int отводится 4 байта. Тогда структура хранения этой переменной в оперативной памяти может иметь следующий вид:
адрес ячейки |
байт памяти
|
...... |
......... |
0x00A0 |
00000000 |
0x00A1 |
00000000 |
0x00A2 |
00000000 |
0x00A3 |
00000001 |
0x00A4 |
......... |
....... |
.......... |
Указатель (pointer) - это переменная особого вида, предназначенная для хранения адреса объекта (адреса ячейки памяти, в которых хранится значение переменной). Обычно переменная непосредственно содержит некоторое значение. Указатель же содержит адрес переменной, которая, в свою очередь, содержит значение. Диапазон значений для любого указателя включает специальный адрес 0 (null) и диапазон положительных целых чисел, который интерпретируется как машинные адреса.
Для получения значения переменной хранящейся в ячейке, на которую указывает указатель, используется оператор разыменования (*).
Объявление указателя
При объявлении указателя перед именем переменной ставится *, например:
int *p;
что означает, что переменная p имеет тип int * (т.е. указатель на int) и указывает на объект типа int.
Если в списке объявляется несколько переменных, перед каждой переменной, объявляемой как указатель, должна предшествовать звездочка (*):
double *xPtr, *yPtr;
В данном случае переменные xPtr, и yPtr являются указателями на значения типа double. Когда звездочка появляется в объявлении, она не является операцией разименования, она только сообщает, что переменная объявляется как указатель.
Присваивание указателя
Для получения адреса ячейки памяти, в которой хранится переменная, и последующей инициализации переменной типа указатель используется операция получения адреса (&).
int i=1; // объявление целой переменной i
int *p; // объявление переменной типа указатель, т.е. значение по адресу p является целым числом
p=&i; // p=0x00A0 (оператор получения адреса)
В данном примере, значение переменной p представляет собой адрес ячейки (указатель на то место в памяти), в которой хранится значение переменной i.
Операция разименования
Для получения значения переменной хранящейся в ячейке, на которую указывает указатель, используется оператор разыменования (*). Если нужно использовать значение переменной, хранящейся по адресу p, перед p нужно ставить оператор разыменования:
int i=1; // объявление целой переменной i
int *p ; // объявление переменной типа указатель, т.е. значение по адресу p является целым числом
p=&i; // p=0x00A0 (оператор получения адреса)
int t=*p+1; // эквивалентно t=i+1;
p++; // прибавляем 1 к указателю – переходим к следующей ячейке памяти с другой переменной
Рассмотрим пример работы с указателем.
/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 15. Указатели
//
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// СПб НИУ ИТМО
/////////////////////////////////////////////////////////////////////////////
// подключение библиотеки ввода-вывода
#include <iostream>
// подключение стандартного пространства имен для использования библиотек
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// функция main начинает исполнение программы
void main()
{
//----------------------
int i=1; // объявление переменной
int* p; // объявление указателя на переменную
p=&i; // инициализация указателя на переменную
// вывод на экран значения переменной
cout<<i<<endl; // 1
// вывод на экран ссылки
cout<<p<<endl; // 0x0012ff6c
// вывод на экран значения переменной, на которую указывает указатель
cout<<*p<<endl; // 1
cout<<endl;
//----------------------
i+=5; // изменение переменной
cout<<i<<endl; // 6
cout<<*p<<endl; // 6
cout<<endl;
//----------------------
*p-=5; // изменение переменной, хранящейся по указателю
cout<<i<<endl; // 1
cout<<*p<<endl; // 1
cout<<endl;
//----------------------
p++; // изменение указателя (смещение в памяти на следующюу переменную)
cout<<i<<endl; // 1 (значение переменной не изменилось)
cout<<p<<endl; // 0x0012ff70 (адрес ячейки памяти изменился)
cout<<*p<<endl; // ???? (что хранится по новому адресу - неизвестно)
cout<<endl;
//----------------------
p=0; // изменение указателя (указатель никуда не указывает)
cout<<p<<endl; // 0x00000000
cout<<*p<<endl; // ошибка !!! по этому адресу нет никаких значений
cout<<endl;
}
