Функция 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 Указатели и вектора
Указатели и вектора в С++ связаны очень тесно. Имя вектора можно использовать как указатель на его первый элемент, поэтому пример с алфавитом можно было написать так:
char alpha[] = «abcdefghijklmnopqrstuvwxyz»; char* p = alpha; char ch;
while (ch = *p++) cout «„ chr(ch) „« " = " «« ch «« « = 0“ «« oct(ch) «« «\n“;
Описание p можно было также записать как
char* p = amp;alpha[0];
Эта эквивалентность широко используется в вызовах функций, в которых векторный параметр всегда передается как указатель на первый элемент вектора. Так, в примере
extern int strlen(char*); char v[] = «Annemarie»; char* p = v; strlen(p); strlen(v);
функции strlen в обоих вызовах передается одно и то же значение. Вся штука в том, что этого невозможно избежать; то есть не существует способа описать функцию так, чтобы вектор v в вызове функции копировался (#4.6.3). Результат применения к указателям арифметических операций +, -, ++ или – зависит от типа объекта, на который они указывают. Когда к указателю p типа T* применяется арифметическая операция, предполагается, что p указывает на элемент вектора объектов типа T; p+1 означает следующий элемент этого вектора, а p предыдущий элемент. Отсюда следует, что значение p+1 будет на sizeof(T) больше значения p. Например, выполнение
main (* char cv[10]; int iv[10];
char* pc = cv; int* pi = iv;
cout «„ "char* " „« long(pc+1)-long(pc) «« «\n“; cout «« "int* " «« long(ic+1)-long(ic) «« «\n“; *)
дает
char* 1 int* 4
поскольку на моей машине каждый символ занимает один байт, а каждое целое занимает четыре байта. Перед вычитанием значения указателей преобразовывались к типу long с помощью явного преобразования типа (#3.2.5). Они преобразовывались к long, а не к «очевидному» int, поскольку есть машины, на которых указатель не влезет в int (то есть, sizeof(int)«sizeof(long) ).
Вычитание указателей определено только тогда, когда оба указателя указывают на элементы одного и того же вектора (хотя в языке нет способа удостовериться, что это так). Когда из одного указателя вычитается другой, результатом является число элементов вектора между этими указателями (целое число). Можно добавлять целое к указателю или вычитать целое из указателя; в обоих случаях результатом будет значение типа указателя. Если это значение не указывает на элемент того же вектора, на который указывал исходный указатель, то результат использования этого значения неопределён. Например:
int v1[10]; int v2[10];