C++
Шрифт:
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 в обоих вызовах передается
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];
int i = amp;v1[5]– amp;v1[3]; // 2 i = amp;v1[5]– amp;v2[3]; // результат неопределен
int* p = v2+2; // p == amp;v2[2] p = v2-2; // p неопределено
2.3.8 Структуры
Вектор есть совокупность элементов одного типа, struct является совокупностью элементов (практически) произвольных типов. Например:
struct address (* // почтовый адрес char* name; // имя «Jim Dandy» long number; // номер дома 61 char* street; // улица «South Street» char* town; // город «New Providence» char* state[2]; // штат 'N' 'J' int zip; // индекс 7974 *)
определяет новый тип, названный address (почтовый адрес), состоящий из пунктов, требующихся для того, чтобы послать
Переменные типа address могут описываться точно также, как другие переменные, а доступ к отдельным членам получается с помощью операции . (точка). Например:
address jd; jd.name = «Jim Dandy»; jd.number = 61;
Запись, которая использовалась для инициализации векторов, можно применять и к переменным структурных типов. Например:
address jd = (* «Jim Dandy», 61, «South Street», «New Providence», (*'N','J'*), 7974 *);
Однако обычно лучше использовать конструктор (#5.2.4). Заметьте, что нельзя было бы инициализировать jd.state строкой «NJ». Строки оканчиваются символом '\0', поэтому в «NJ» три символа, то есть на один больше, чем влезет в jd.state.
К структурным объектам часто обращаются посредством указателей используя операцию -». Например:
void print_addr(address* p) (* cout «„ p-“name „„ „\n“ „„ p-“number „„ " " „„ p-“street „« «\n“ «« p-“town «« «\n“ «« chr(p-“state[0]) «« chr(p-“state[1]) «« " " «« p-“zip «« «\n“; *)
Объекты типа структура можно присваивать, передавать как параметры функции и возвращать из функции в качестве результата. Например:
address current;
address set_current(address next) (* address prev = current; current = next; return prev; *)
Остальные осмысленные операции, такие как сравнение (== и !=) не определены. Однако пользователь может определить эти операции, см. Главу 6. Размер объекта структурного типа нельзя вычислить просто как сумму его членов. Причина этого состоит в том, что многие машины требуют, чтобы объекты определенных типов выравнивались в памяти только по некоторым зависящим от архитектуры границам (типичный пример: целое должно быть выровнено по границе слова), или просто гораздо более эффективно обрабатывают такие объекты, если они выровнены в машине. Это приводит к «дырам» в структуре. Например, (на моей машине) sizeof(address) равен 24, а не 22, как можно было ожидать.
Заметьте, что имя типа становится доступным сразу после того, как оно встретилось, а не только после того, как полностью просмотрено все описание. Например:
struct link(* link* previous; link* successor; *)
Новые объекты структурного типа не могут быть описываться, пока все описание не просмотрено, поэтому
struct no_good (* no_good member; *);
является ошибочным (компилятор не может установить размер no_good). Чтобы дать возможность двум (или более) структурным типам ссылаться друг на друга, можно просто описать имя как имя структурного типа. Например: