Операции с указателями
С указателями можно выполнять следующие операции: разадресация (*), присваивание, сложение с константой, вычитание, инкремент (++), декремент (– –), сравнение, приведение типов. При работе с указателями часто используется операция получения адреса (&). Операция разадресации, или разыменования, предназначена для доступа к величине, адрес которой хранится в указателе. Эту операцию можно использовать как для получения, так и для изменения значения величины (если она не объявлена как константа): char a;//переменная типа char char * p = new char;/*выделение памяти под указатель и под динамическую переменную типа char */ *p = 'Ю'; a = *p;//присваивание значения обеим переменным На одну и ту же область памяти может ссылаться несколько указателей различного типа. Примененная к ним операция разадресации даст разные результаты. Например, программа #include <stdio.h> int main() {unsigned long int A=0Xсс77ffaa; unsigned int* pint =(unsigned int *) &A; unsigned char* pchar =(unsigned char *) &A; printf(" | %x | %x |", *pint, *pchar);} на IBM PC выведет на экран строку: | ffaa | aa | В примере при инициализации указателей были использованы операции приведения типов. Синтаксис операции явного приведения типа прост: перед именем переменной в скобках указывается тип, к которому ее требуется преобразовать. При смешивании в выражении указателей разных типов явное преобразование типов требуется для всех указателей, кроме void*. Указатель может неявно преобразовываться в значение типа bool. Присваивание без явного приведения типов допускается только указателям типа void* или если тип указателей справа и слева от операции присваивания один и тот же. Присваивание указателей данных указателям функций (и наоборот) недопустимо. Арифметические операции с указателями (сложение, вычитание, инкремент и декремент) автоматически учитывают размер типа величин, адресуемых указателями. Эти операции применимы только к указателям одного типа и имеют смысл в основном при работе со структурами данных, последовательно размещенными в памяти, например, с массивами. Инкремент перемещает указатель к следующему элементу массива, декремент — к предыдущему. Фактически значение указателя изменяется на величину sizeof(тип). Разность двух указателей — это разность их значений, деленная на размер типа в байтах. Суммирование двух указателей не допускается. При записи выражений с указателями следует обращать внимание на приоритеты операций. В качестве примера рассмотрим последовательность действий, заданную в операторе *p++ = 10; То же самое можно записать подробнее: *p = 10; p++; Выражение (*p)++, напротив, инкрементирует значение, на которое ссылается указатель. Унарная операция получения адреса & применима к величинам, имеющим имя и размещенным в оперативной памяти. Нельзя получить адрес скалярного выражения, неименованной константы или регистровой переменной. Ссылки Ссылка представляет собой синоним имени, указанного при инициализации ссылки. Ссылку можно рассматривать как указатель, который всегда разыменовывается. Формат объявления ссылки: тип & имя; где тип — это тип величины, на которую указывает ссылка, & — оператор ссылки, означающий, что следующее за ним имя является именем переменной ссылочного типа, например: int kol; int& pal = kol;//ссылка pal — альтернативное имя для kol const char& CR = '\n';//ссылка на константу · Переменная-ссылка должна явно инициализироваться при ее описании, кроме случаев, когда она является параметром функции, описана как extern или ссылается на поле данных класса. · После инициализации ссылке не может быть присвоена другая переменная. · Тип ссылки должен совпадать с типом величины, на которую она ссылается. · Не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки. Ссылки применяются чаще всего в качестве параметров функций и типов возвращаемых функциями значений. Ссылки позволяют использовать в функциях переменные, передаваемые по адресу, без операции разадресации, что улучшает читаемость программы. Операция над ссылкой приводит к изменению величины, на которую она ссылается.
Лекция 8. Массивы При использовании простых переменных каждой области памяти для хранения данных соответствует свое имя. Если с группой величин одинакового типа требуется выполнять однообразные действия, им дают одно имя, а различают по порядковому номеру. Конечная именованная последовательность однотипных величин называется массивом. Описание массива в программе отличается от описания простой переменной наличием после имени квадратных скобок, в которых задается количество элементов массива (размерность): float a [10]; //Пример описания массива из 10 вещественных чисел Элементы массива нумеруются с нуля. Инициализирующие значения для массивов записываются в фигурных скобках.: int b[5] = {3, 2, 1};// b[0]=3, b[1]=2, b[2]=1, b[3]=0, b[4]=0 Размерность массива может быть задана только целой положительной константой или константным выражением. Для доступа к элементу массива после его имени указывается номер элемента (индекс) в квадратных скобках. В следующем примере подсчитывается сумма элементов массива.
Размерность массивов предпочтительнее задавать с помощью типизированных констант. Динамические массивы создают с помощью операции new, при этом необходимо указать тип и размерность, например: float *p = new float [100]; В этой строке создается переменная-указатель на float, в динамической памяти отводится непрерывная область, достаточная для размещения 100 элементов вещественного типа, и адрес ее начала записывается в указатель p. Динамические массивы нельзя при создании инициализировать, и они не обнуляются. Преимущество динамических массивов состоит в том, что их размерность может не быть константой. Память, зарезервированная под динамический массив с помощью new [], должна освобождаться оператором delete [], а память, выделенная функцией malloc — посредством функции free, например: delete [] p; free (q); Размерность массива не указывается, но квадратные скобки обязательны. Многомерные массивы задаются указанием каждого измерения в квадратных скобках, например, оператор int matr [6][8]; задает описание двумерного массива из 6 строк и 8 столбцов. В памяти такой массив располагается в последовательных ячейках построчно. Многомерные массивы размещаются так, что при переходе к следующему элементу быстрее всего изменяется последний индекс. Для доступа к элементу многомерного массива указываются все его индексы, например, matr[i][j]. Инициализация многомерного массива: int mass2 [][]={ {1, 1}, {0, 2}, {1, 0} }; int mass2 [3][2]={1, 1, 0, 2, 1, 0}; Для создания многомерного массива в динамической памяти необходимо указать все его размерности (первая из них может быть переменной): matr = new int [a] [b]; Освобождение памяти из-под массива с любым количеством измерений выполняется с помощью операции delete []. Указатель на константу удалить нельзя.
Строки Строка представляет собой массив символов, заканчивающийся нуль-символом. Нуль-символ — это символ с кодом, равным 0, что записывается в виде управляющей последовательности '\0'. По положению нуль-символа определяется фактическая длина строки. Строку можно инициализировать строковым литералом: char str[10] = "Vasia"; В этом примере под строку выделяется 10 байт, 5 из которых занято под символы строки, а шестой — под нуль-символ. Если строка при определении инициализируется, ее размерность можно опускать (компилятор сам выделит соответствующее количество байт): char str[] = "Vasia";//Выделено и заполнено 6 байт Оператор char *str = "Vasia" создает не строковую переменную, а указатель на строковую константу. Операция присваивания одной строки другой не определена и может выполняться с помощью цикла или функций стандартной библиотеки. Библиотека предоставляет возможности копирования, сравнения, объединения строк, поиска подстроки, определения длины строки и т.д. (возможности библиотеки описаны в разделе «Функции работы со строками и символами»), а также содержит специальные функции ввода строк и отдельных символов с клавиатуры и из файла.
При работе со строками часто используются указатели.
|