C++
Шрифт:
void f // f не возвращает значение void* pv; // указатель на объект неизвестного типа
Переменной типа указатель на void (void *), можно присваивать указатель любого типа. На первый взгляд это может показаться не особенно полезным, поскольку void* нельзя разименовать, но именно это ограничение и делает тип void* полезным. Главным образом, он применяется для передачи указателей функциям, которые не позволяют сделать предположение о типе объекта, и для возврата из функций нетипизированных объектов. Чтобы использовать такой объект, необходимо применить
void* allocate(int size); // выделить void deallocate(void*); // освободить
f (* int* pi = (int*)allocate(10*sizeof(int)); char* pc = (char*)allocate(10); //... deallocate(pi); deallocate(pc); *)
2.3.5 Указатели
Для большинства типов T T* является типом арифметический указатель на T. То есть, в переменной типа T* может храниться адрес объекта типа T. Для указателей на вектора и указателей на функции вам, к сожалению, придется пользоваться более сложной записью:
int* pi; char** cpp; // указатель на указатель на char int (*vp)[10]; // указатель на вектор из 10 int'ов int (*fp)(char, char*); // указатель на функцию //получающую параметры(char, char*) // и возвращающую int
Основная операция над указателем – разыменование, то есть ссылка на объект, на который указывает указатель. Эта операция также называется косвенным обращением. Операция разыменования – это унарное * (префиксное). Например:
char c1 = 'a'; char* p = amp;c1; // в p хранится адрес c1 char c2 = *p; // c2 = 'a'
Переменная, на которую указывает p,– это c1, а значение, которое хранится в c1, это 'a', поэтому присваиваемое c2 значение *p есть 'a'.
Над указателями можно осуществлять некоторые арифметические действия. Вот, например, функция, подсчитывающая число символов в строке (не считая завершающего 0):
int strlen(char* p) (* int i = 0; while (*p++) i++; return i; *)
Другой способ найти длину состоит в том, чтобы сначала найти конец строки, а затем вычесть адрес начала строки из адреса ее конца:
int strlen(char* p) (* char* q = p; while (*q++) ; return q-p-1; *)
Очень полезными могут оказаться указатели на функции. Они обсуждаются в #4.6.7.
2.3.6 Вектора
Для типа T T[size] является типом «вектор из size элементов типа T». Элементы индексируются (нумеруются) от 0 до size-1. Например:
float v[3]; // вектор из трех float: v[0], v[1], v[2] int a[2][5]; // два вектора из пяти int char* vpc; // вектор из 32 указателей на символ
Цикл для печати целых значений букв нижнего регистра можно было бы написать так:
extern int strlen(char*);
char alpha[] = «abcdefghijklmnoprstuvwxyz»;
main
(* int sz = strlen(alpha);
for (int i=0; i«sz; i++) (* char ch = alpha[i]; cout „„ "'" „« chr(ch) «« "'" «« " = " «« ch «« « = 0“ «« oct(ch)
Функция chr возвращает представление небольшого целого в виде строки; например, chr(80) это "P" на машине, на которой используется набор символов ASCII. Функция oct строит восьмеричное представление своего целого аргумента, а hex строит шестнадцатеричное представление своего целого аргумента; chr oct и hex описаны в «stream.h». Функция strlen использовалась для подсчета числа символов в alpha; вместо этого можно было использовать значение размера alpha (#2.4.4). Если применяется набор символов ASCII, то выдача выглядит так:
'a' = 97 = 0141 = 0x61 'b' = 98 = 0142 = 0x62 'c' = 99 = 0143 = 0x63 ...
Заметим, что задавать размер вектора alpha необязательно. Компилятор считает число символов в символьной строке, указанной в качестве инициализатора. Использование строки как инициализатора для вектора символов – удобное, но к сожалению и единственное применение строк. Аналогичное этому присваивание строки вектору отсутствует. Например:
char v[9]; v = «строка»; // ошибка
ошибочно, поскольку присваивание не определено для векторов.
Конечно, для инициализации символьных массивов подходят не только строки. Для остальных типов нужно применять более сложную запись. Эту запись можно использовать и для символьных векторов. Например:
int v1[] = (* 1, 2, 3, 4 *); int v2[] = (* 'a', 'b', 'c', 'd' *);
char v3[] = (* 1, 2, 3, 4 *); char v4[] = (* 'a', 'b', 'c', 'd' *);
Заметьте, что v4 – вектор из четырех (а не пяти) символов; он не оканчивается нулем, как того требуют соглашение и библиотечные подпрограммы. Обычно применение такой записи ограничивается статическими объектами.
Многомерные массивы представляются как вектора векторов, и применение записи через запятую, как это делается в некоторых других языках, дает ошибку при компиляции, так как запятая (,) является операцией следования (см. #3.2.2). Попробуйте, например, сделать так:
int bad[5,2]; // ошибка
и так:
int v[5][2];
int bad = v[4,1]; // ошибка int good = v[4][1]; // ошибка
Описание
char v[2][5];
описывает вектор из двух элементов, каждый из которых является вектором типа char[5]. В следующем примере первый из этих векторов инициализируется первыми пятью буквами, а второй – первыми пятью цифрами.
char v[2][5] = (* 'a', 'b', 'c', 'd', 'e', '0', '1', '2', '3', '4' *)
main (* for (int i = 0; i«2; i++) (* for (int j = 0; j„5; j++) cout „„ „v[“ «« i «« «][“ «« j «« «]=“ «« chr(v[i][j]) «« " "; cout «« «\n“; *) *)
это дает в результате
v[0][0]=a v[0][1]=b v[0][2]=c v[0][3]=d v[0][4]=e v[1][0]=0 v[1][1]=1 v[1][2]=2 v[1][3]=3 v[1][4]=4
2.3.7 Указатели и вектора
Указатели и вектора в С++ связаны очень тесно. Имя вектора можно использовать как указатель на его первый элемент, поэтому пример с алфавитом можно было написать так: