Kernighan, B. W. and Ritchie, D. M. "The 'C' Programming Language"; Chapter 23

23. Снова о типах.

В этом разделе обобщаются сведения об операциях, которые можноприменять только к об'ектам определенных типов.

Содержание

23.1. Структуры и об'единения.
23.2. Функции.
23.3. Массивы, указатели и индексация.
23.4. Явные преобразования указателей.


23.1. Структуры и об'единения.

Только две вещи можно сделать со структурой или об'единением:назвать один из их членов (с помощью операции) или извлечь их адрес (с помощью унарной операции &). Другие операции, такие как присваиваниеим или из них и передача их в качестве параметров, приводят ксообщению об ошибке. В будущем ожидается, что эти операции, но необязательно какиe-либо другие, будут разрешены.

В п. 16.1 говорится, что при прямой или косвенной ссылке на структуру(с помощью . или ->) имя справа должно быть членом конструкции,названной или указанной выражением слева. Это ограничение ненавязывается строго компилятором, чтобы дать возможность обойти правилатипов. В действительности перед '.' допускается любое l-значение изатем предполагается, что это l-значение имеет форму структуры, длякоторой стоящее справа имя является членом. Таким же образом, отвыражения, стоящего перед '->', требуется только быть указателем илицелым. В случае указателя предполагается, что он указывает на структуру,для которой стоящее справа имя является членом. В случае целого онорассматривается как абсолютный адрес соответствующей структуры,заданный в единицах машинной памяти.

Такие структуры не являются переносимыми.


23.2. Функции.

Только две вещи можно сделать с функцией: вызвать ее или извлечь ееадрес. Если имя функции входит в выражение не в позиции имени функции,соответствующей обращению к ней, то генерируется указатель на этуфункцию. Следовательно, чтобы передать одну функцию другой, можнонаписать

 int f(); ... g(f);
Тогда определение функции g могло бы выглядеть так:
 g(funcp) int(*funcp)(); {        ...        (*funcp)();        ... }
Обратите внимание, что в вызывающей процедуре функция f должна бытьописана явно, потому что за ее появлением в g(f) не следует скобка ( .


23.3. Массивы, указатели и индексация.

Каждый раз, когда идентификатор, имеющий тип массива, появляется ввыражении, он преобразуется в указатель на первый член этого массива.Из-за этого преобразования массивы не являются l-значениями. Поопределению операция индексация [] интерпретируется таким образом, чтоe1[e2] считается идентичным выражению *((e1)+(e2)). Согласно правилампреобразований, применяемым при операции +, если e1 - массив, а e2 -целое, то el[e2] ссылается на e2-й член массива e1. Поэтому, несмотряна несимметричный вид, операция индексации является коммутативной.

В случае многомерных массивов применяется последовательное правило.Если е является n-мерным массивом размера i*j*...*к, то припоявлении в выражении е преобразуется в указатель на (n-1)-мерныймассив размера j*...*к. Если операция * либо явно, либо неявно, какрезультат индексации, применяется к этому указателю, то результатомоперации будет указанный (n-1)-мерный массив, который сам немедленнопреобразуется в указатель.

Рассмотрим, например, описание

 int x[3][5];
Здесь x массив целых размера 3*5. При появлении в выражении х Pпреобразуется в указатель на первый из трех массивов из 5 целых. Ввыражении x[i], которое эквивалентно *(x+i), сначала x преобразуется вуказатель так, как описано выше; затем i преобразуется к типу x, чтовызывает умножение i на длину об'екта, на который указывает указатель,а именно на 5 целых об'ектов. Результаты складываются, и применениекосвенной адресации дает массив (из 5 целых), который в свою очередьпреобразуется в указатель на первое из этих целых. Если в выражениевходит и другой индекс, то таже самая аргументация применяется снова;результатом на этот раз будет целое.

Из всего этого следует, что массивы в языке "C" хранятся построчно (последний индекс изменяется быстрее всего) и что первый индекс вописании помогает определить общее количество памяти, требуемое дляхранения массива, но не играет никакой другой роли в вычислениях,связанных с индексацией.


23.4. Явные преобразования указателей.

Разрешаются определенные преобразования, с использованием указателей, но они имеют некоторые зависящие от конкретной реализации аспекты.Все эти преобразования задаются с помощью операции явного преобразованиятипа; см. п. 16.2 и 17.7.

Указатель может быть преобразован в любой из целочисленных типов,достаточно большой для его хранения. Требуется ли при этом int или long,зависит от конкретной машины. Преобразующая функция также являетсямашинно-зависимой, но она будет вполне естественной для тех, ктознает структуру адресации в машине. Детали для некоторых конкретныхмашин приводятся ниже.

Об'ект целочисленного типа может быть явным образом преобразован вуказатель. Такое преобразование всегда переводит преобразованное изуказателя целое в тот же самый указатель, но в других случаях онобудет машинно-зависимым.

Указатель на один тип может быть преобразован в указатель на другойтип. Если преобразуемый указатель не указывает на об'екты, которыеподходящим образом выравнены в памяти, то результирующий указательможет при использовании вызывать ошибки адресации. Гарантируется, чтоуказатель на об'ект заданного размера может быть преобразован вуказатель на об'ект меньшего размера и снова обратно, не претерпевпри этом изменения.

Например, процедура распределения памяти могла бы принимать запросна размер выделяемого об'екта в байтах, а возвращать указатель насимволы; это можно было бы использовать следующим образом.

 extern char *alloc(); double *dp; dp=(double*) alloc(sizeof(double)); *dp=22.0/7.0;
Функция alloc должна обеспечивать (машинно-зависимым способом),что возвращаемое ею значение будет подходящим для преобразования вуказатель на double; в таком случае использование этой функциибудет переносимым.

Представление указателя на pdp-11 соответствует 16-битовому целомуи измеряется в байтах. Об'екты типа char не имеют никаких ограниченийна выравнивание; все остальные об'екты должны иметь четные адреса.

На honeywell 6000 указатель соответствует 36-битовому целому; словусоответствует 18 левых битов и два непосредственно примыкающих кним справа бита, которые выделяют символ в слове. Таким образом,указатели на символы измеряются в единицах 2 в степени 16 байтов;все остальное измеряется в единицах 2 в степени 18 машинных слов.Величины типа double и содержащие их агрегаты должны выравниватьсяпо четным адресам слов (0 по модулю 2 в степени 19). Эвм ibm 370 иinterdata 8/32 сходны между собой. На обеих машинах адресаизмеряются в байтах; элементарные об'екты должны быть выровнены погранице, равной их длине, так что указатели на short должны бытькратны двум, на int и float - четырем и на double - восьми. Агрегатывыравниваются по самой строгой границе, требуемой каким-либо из ихэлементов.