- •Алфавиты и типы данных. Целые и плавающие типы.
- •Выражение присваивания. Арифметические операции с целыми и плавающими переменными.
- •Логические операции, операции автоувеличения и автоуменьшения, тернарная операция.
- •Составной оператор. Условный оператор.
- •Оператор switch – case. Оператор безусловного перехода, break, continue.
- •Операторы цикла. Оператор безусловного перехода, break, continue.
- •Указатели. Указатели и массивы. Адресная арифметика.
- •Символьные массивы и строки. Указатели и многомерные массивы.
- •9. Операции для работы с динамической памятью.
- •10. Объявления и определения. Область существования имени.
- •11. Область видимости имён. Классы памяти.
- •12. Объявления объектов и типов. Синоним имени типа.
- •13. Правила преобразования стандартных типов. Неявные преобразования стандартных базовых типов. Преобразования производных стандартных типов.
- •14. Функции. Передача аргументов. Указатели на функции.
- •15. Ссылки. Передача аргументов в функции по ссылке.
- •16. Функции. Аргументы по умолчанию и переопределение функций.
- •17. Шаблоны функций.
- •Перечисления
- •Классы. Конструкторы и деструкторы.
- •Статические члены класса
- •Указатель this. Статические функции-члены.
- •Указатели на члены класса
- •Конструктор копирования и операция присваивания
- •Дружественные (привилегированные) функции
- •Производные классы. Построение. Защищенные классы. Производные классы Построение производного класса
- •Защищенные члены класса
- •Управление уровнем доступа к членам класса
- •19.4. Последовательность вызова конструктора и деструктора при построении производного класса на основе одного базового
- •Преобразования типов. Связь с наследованием. Преобразование типов
- •Раннее и позднее связывание (полиморфизм). Виртуальные функции. Полиморфизм
- •Раннее и позднее связывание
- •Виртуальные функции
-
Указатели. Указатели и массивы. Адресная арифметика.
Определение указателей
Указатель - это переменная, содержащая адрес некоторого объекта, например, другой переменной. Точнее - адрес первого байта этого объекта. Это дает возможность косвенного доступа к этому объекту через указатель. Пусть x - переменная типа int. Обозначим через px указатель. Унарная операция & выдает адрес объекта, так что оператор
px = &x;
присваивает переменной px адрес переменной x. Говорят, что px "указывает" на x. Операция & применима только к адресным выражениям, так что конструкции вида &(x-1) и &3 незаконны.
Унарная операция * называется операцией разадресации или операцией разрешения адреса. Эта операция рассматривает свой операнд как адрес и обращается по этому адресу, чтобы извлечь объект, содержащийся по этому адресу.
Следовательно, если y тоже имеет тип int, то
y = *px;
присваивает y содержимое того, на что указывает px. Так, последовательность
px = &x; y = *px;
присваивает y то же самое значение, что и оператор
y = x;
Все эти переменные должны быть описаны:
int x, y; int *px;
Последнее - описание указателя. Его можно рассматривать как мнемоническое. Оно говорит, что комбинация *px имеет тип int или, иначе, px есть указатель на int. Это означает, что если px появляется в виде *px, то это эквивалентно переменной типа int.
Из описания указателя следует, что он может указывать только на определенный вид объекта (в данном случае int). Разадресованный указатель может входить в любые выражения там, где может появиться объект того типа, на который этот указатель указывает. Так, оператор
y = *px + 2;
присваивает y значение, на 2 больше, чем х.
Заметим, что приоритет унарных операций * и & таков, что эти операции связаны со своими операндами более крепко, чем арифметические операции, так что выражение
y = *px + 2
берет то значение, на которое указывает px, прибавляет 2 и присваивает результат переменной y.
Если px указывает на х, то
*px = 3;
полагает х равным 3, а
*px + = 1;
увеличивает х на 1 , также как и выражение
(*px) ++
Круглые скобки здесь необходимы. Если их опустить, то есть написать *px ++, то, поскольку унарные операции, подобные * и ++, выполняются справа - налево, это выражение увеличит px, а не ту переменную, на которую он указывает.
Если py - другой указатель на int, то можно выполнить присвоение py = px;
Здесь адрес из px копируется в py. В результате py указывает на то же, что и px.
Указатели и массивы
Массив - это совокупность элементов одного типа, которые расположены в памяти ЭВМ подряд, один за другим.
Признаком объявления массива являются квадратные скобки. Объявить массив из 10 элементов типа float можно так:
float a[10];
Чтобы обратиться к элементу этого массива, нужно применить операцию индексирования a[ind]. Внутри квадратных скобок помещается целое выражение, которое называется индексом. Нумерация элементов массива начинается с 0 и поэтому вышеприведенное описание говорит о том, что в памяти ЭВМ зарезервировано место под 10 переменных типа float и эти переменные есть a[0], a[1], . . . , a[9].
Приведем пример с использованием массива.
Напишем программу подсчета числа появлений каждой цифры, пробельных символов и всех остальных символов.
Число пробельных символов будем хранить в nwhite, прочих символов - в nother, а число появлений каждой из цифр - в массиве ndigit:
# include < iostream.h> void main( ) { int c, i, nwhite = 0, nother = 0; int ndigit [10]; for ( i=0; i<10; i++) ndigit[i]=0; while ( ( c=cin.get( ) )!=EOF) if(c>='0' && c<='9') ++ ndigit[c - '0']; else if (c = = ' '|| c = = '\n' || c = = '\t') ++ nwhite; else ++ nother; cout<<" цифра \n"; for( i=0; i<10; i ++) cout<< i<<" вошла"<< ndigit[i]<< " раз \n"; cout<< " пробельных символов - "<< nwhite << " прочих символов - " << nother << "\n"; }
При объявлении массива его можно инициализировать:
int c[ ] = { 1, 2, 7, 0, 3, 5, 5 }; char array[ ] = { 'h', 'e', 'l', 'l', 'o', '\n', '\0'};
Последнюю инициализацию разрешается выполнять проще:
char array[ ] = "hello\n";
Такой синтаксис инициализации разрешен только для строк. Компилятор сам вычислит необходимый размер памяти с учетом автоматически добавляемого в конец строки символа '\0' с кодом 0, который является признаком завершения строки.
В языке С++ имя массива является константным указателем на первый элемент этого массива:
int mas[20]; int *pmas; pmas = &mas[0];
Последний оператор можно записать и так: pmas = mas;
Операция индексирования массива [ ] имеет 2 операнда - имя массива, т.е. указатель, и индекс, т.е. целое: a[i]. В языке С++ любое выражение указатель[индекс] по определению трактуется как
*(указатель + индекс)
и автоматически преобразуется к такому виду компилятором.
Таким образом, a[3] эквивалентно *(a + 3). Более того, это можно записать даже так 3[a], т.к. это все равно будет проинтерпретировано как *(3+a). Здесь складываются указатель a и целое 3. В связи с этим рассмотрим так называемую адресную арифметику.
Адресная арифметика
Указатель можно складывать с целым.
Если к указателю pa прибавляется целое приращение i, то приращение масштабируется размером памяти, занимаемой объектом, на который указывает указатель pa.
Таким образом, pa+i - это адрес i-го элемента после pa, причем считается, что размер всех этих i элементов равен размеру объекта, на который указывает pa.
Итак, если a - массив, то a+i - адрес i-го элемента этого массива, т.е. &a[i] равен a+i и a[i] равняется *(a+i).
float b[10]; float *pb=b; pb++; // Это эквивалентно pb=pb_1. // Здесь указатель pb будет указывать // на элемент массива b[1]. pb+=3; // Здесь pb указывает на элемент массива b[4].
Отметим, что нельзя написать b++ или b = b+i, т.к. имя массива b - это константный указатель и его изменять нельзя.
Указатели можно сравнивать.
Если p и q указывают на элементы одного и того же массива, то такие отношения, как < >= и т.д. работают надлежащим образом. Например,
p < q истинно, т.е. = = 1, если p указывает на более ранний элемент массива, чем q. Любой указатель можно сравнить на равенство или неравенство с так называемым нулевым указателем NULL, который ни на что не указывает. Однако не рекомендуется сравнивать указатели, указывающие на различные массивы.
Указатели можно вычитать.
Если p и q указывают на элементы одного и того же массива, то p - q дает количество элементов массива между p и q.