В этом разделе обобщаются сведения об операциях, которые можно применять только к об'ектам определенных типов.
Только две вещи можно сделать со структурой или об'единением: назвать один из их членов (с помощью операции) или извлечь их адрес ( с помощью унарной операции &). Другие операции, такие как присваивание им или из них и передача их в качестве параметров, приводят к сообщению об ошибке. В будущем ожидается, что эти операции, но не обязательно какиe-либо другие, будут разрешены.
В п. 16.1 говорится, что при прямой или косвенной ссылке на структуру (с помощью . или ->) имя справа должно быть членом конструкции, названной или указанной выражением слева. Это ограничение не навязывается строго компилятором, чтобы дать возможность обойти правила типов. В действительности перед '.' допускается любое l-значение и затем предполагается, что это l-значение имеет форму структуры, для которой стоящее справа имя является членом. Таким же образом, от выражения, стоящего перед '->', требуется только быть указателем или целым. В случае указателя предполагается, что он указывает на структуру, для которой стоящее справа имя является членом. В случае целого оно рассматривается как абсолютный адрес соответствующей структуры, заданный в единицах машинной памяти.
Такие структуры не являются переносимыми.
Только две вещи можно сделать с функцией: вызвать ее или извлечь ее адрес. Если имя функции входит в выражение не в позиции имени функции, соответствующей обращению к ней, то генерируется указатель на эту функцию. Следовательно, чтобы передать одну функцию другой, можно написать
int f(); ... g(f);Тогда определение функции g могло бы выглядеть так:
g(funcp) int(*funcp)(); { ... (*funcp)(); ... }Обратите внимание, что в вызывающей процедуре функция f должна быть описана явно, потому что за ее появлением в g(f) не следует скобка ( .
Каждый раз, когда идентификатор, имеющий тип массива, появляется в выражении, он преобразуется в указатель на первый член этого массива. Из-за этого преобразования массивы не являются 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" хранятся построчно ( последний индекс изменяется быстрее всего) и что первый индекс в описании помогает определить общее количество памяти, требуемое для хранения массива, но не играет никакой другой роли в вычислениях, связанных с индексацией.
Разрешаются определенные преобразования, с использованием указателей , но они имеют некоторые зависящие от конкретной реализации аспекты. Все эти преобразования задаются с помощью операции явного преобразования типа; см. п. 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 - восьми. Агрегаты выравниваются по самой строгой границе, требуемой каким-либо из их элементов.