Б. Керриган, Д. Ритчи Язык программирования C. Б. Керниган, Д. зык программирования и . Издание 3е, исправленное Перевод с английского под редакцией Вс. С. Штаркмана СанктПетербург 2003
Скачать 31.48 Mb.
|
А 5. Объекты и Lvalues Объект - это некоторая именованная область памяти; lvalue - это вы- ражение, обозначающее объект. Очевидным примером lvalue является идентификатор с соответствующим типом и классом памяти. Существу- ют операции, порождающие lvalue. Например, если Е - выражение типа указатель, то * Е есть выражение для lvalue, обозначающего объект, на ко- торый указывает Е. Термин "lvalue" произошел от записи присваивания Е1 = Е2, в которой левый (left - левый (англ.), отсюда буква 1, value - значе- ние) операнд Е1 должен быть выражением lvalue. Описывая каждый опе- ратор, мы сообщаем, ожидает ли он lvalue в качестве операндов и выдает ли lvalue в качестве результата. 250 Приложение А. Справочное руководство 6. Преобразования Некоторые операторы в зависимости от своих операндов могут вызы- вать преобразование их значений из одного типа в другой. В этом пара- графе объясняется, что следует ожидать от таких преобразований. В А6.5 формулируются правила, по которым выполняются преобразования для большинства обычных операторов. При рассмотрении каждого отдель- ного оператора эти правила могут уточняться. А Целочисленное повышение Объект типа перечисление, символ, короткое целое, целое в битовом поле - все они со знаком или без могут использоваться в выражении там, где возможно применение целого. Если тип int позволяет "охватить" все значения исходного типа операнда, то операнд приводится к int, в про- тивном случае он приводится к unsigned int. Эта процедура называется целочисленным Целочисленные преобразования Любое целое приводится к некоторому заданному беззнаковому типу путем поиска конгруэнтного (т. е. имеющего то же двоичное представле- наименьшего неотрицательного значения и получения остатка от де- ления его на птах + 1, где птах - наибольшее число в этом беззнаковом типе. Для двоичного представления в дополнительном коде это означает либо выбрасывание лишних старших разрядов, если беззнаковый тип "уже" исходного либо заполнение недостающих старших разрядов нулями (для значения без знака) или значением знака (для значения со знаком), если беззнаковый тип "шире" исходного. В результате приведения любого целого к знаковому типу преобразуе- мое значение не меняется, если оно представимо в этом новом типе, в про- тивном случае результат зависит от реализации. А 6.3. Целые и числа с плавающей точкой При преобразовании из типа с плавающей точкой в целочисленный дробная часть значения отбрасывается; если полученное при этом значе- ние нельзя представить в заданном целочисленном типе, то результат не определен. В частности, не определен результат преобразования отрица- тельных значений с плавающей точкой в беззнаковые целые. Если значение преобразуется из целого в величину с плавающей точ- кой и она находится в допустимом диапазоне, но представляется в новом - повышение — также переводят как "ин- тегральное - Примеч. ред. А 6. Преобразования 251 типе неточно, то результатом будет одно из двух значений нового типа, ближайших к исходному. Если результат выходит за границы диапазона допустимых значений, поведение программы не определено. А 6.4. Типы с плавающей точкой При преобразовании из типа с плавающей точкой меньшей точности в тип с плавающей точкой большей точности значение не изменяется. Если, наоборот, переход осуществляется от большей точности к меньшей и значение остается в допустимых пределах нового типа, то результатом будет одно из двух ближайших значений нового типа. Если результат выходит за границы диапазона допустимых значений, поведение програм- мы не определено. А 6.5. Арифметические преобразования Во многих операциях преобразование типов операндов и определение типа результата осуществляются по одним и тем же правилам. Они со- стоят в том, что операнды приводятся к некоторому общему типу, кото- рый также является и типом результата. Эти правила называются обыч- ными арифметическими преобразованиями. • Если какой-либо из операндов имеет тип long double, то другой приво- дится long double. • В противном случае, если какой-либо из операндов имеет тип double, то другой приводится к double. • В противном случае, если какой-либо из операндов имеет тип то другой приводится к • В противном случае для обоих операндов осуществляется целочислен- ное повышение; затем, если один из операндов тип unsigned long int, другой преобразуется в unsigned long int. • В противном случае, если один из операндов принадлежит типу long int, а другой - unsigned int, то результат зависит от того, покрывает ли long int все значения unsigned int, и если это так, то unsigned int приводится к long int; если нет, то оба операнда преобразуются в unsigned long int. • В противном случае, если один из операндов имеет тип то дру- гой приводится к long int. • В противном случае, если один из операндов - unsigned int, то другой приводится к unsigned int. • В противном случае оба операнда имеют тип int. Здесь есть два изменения. Во-первых, арифметика с операндами с плаваю- щей точкой теперь может производиться с одинарной точностью, а не только с двойной; в первой редакции языка вся арифметика с плавающей точкой про- изводилась с двойной точностью. Во-вторых, более беззнаковый 252 Приложение А. Справочное руководство тип в комбинации с более длинным знаковым типом не распространяет свой- ство беззнаковости на тип результата; в первой редакции беззнаковый тип всегда доминировал. Новые правила немного сложнее, но до некоторой сте- пени уменьшают вероятность появления неожиданных эффектов в комби- нациях знаковых и беззнаковых величин. При сравнении беззнакового вы- ражения со знаковым того же размера все же может возникнуть неожидан- ный результат. А 6.6. Указатели и целые К указателю можно прибавлять (и вычитать из него) выражение цело- численного типа; последнее в этом случае подвергается преобразованию, описанному в при рассмотрении оператора сложения. К двум указателям на объекты одного типа, принадлежащие одному массиву, может применяться операция вычитания; результат приводит- ся к целому посредством преобразования, описанного в при рассмот- рении оператора вычитания. Целочисленное константное выражение со значением 0 или оно же, но приведенное к типу void может быть преобразовано в указатель лю- бого типа операторами приведения, присваивания и сравнения. Резуль- татом будет который равен любому другому NULL-указа- телю того же типа, но не равен никакому указателю на реальный объект или функцию. Для указателей допускаются и другие преобразования, но в связи с ними возникает проблема зависимости результата от реализации. Эти преоб- разования должны быть специфицированы явным оператором преобразо- вания типа или оператором приведения (А7.5 и А8.8). Указатель можно привести к целочисленному типу, достаточно боль- шому для его хранения; требуемый размер зависит от реализации. Функ- ция преобразования также зависит от реализации. Объект целочисленного типа можно явно преобразовать в указатель. Если целое получено из указателя и имеет достаточно большой размер, это преобразование даст тот же указатель; в противном случае результат зависит от реализации. Указатель на один тип можно преобразовать в указатель на другой тип. Если исходный указатель ссылается на объект, должным образом не вы- ровненный по границам слов памяти, то в результате может произойти ошибка адресации. Если требования на выравнивание у нового типа мень- ше или совпадают с требованиями на выравнивание первоначального типа, то гарантируется, что преобразование указателя в другой тип и обратно его не изменит; понятие "выравнивание" зависит от реализации, однако в любой реализации объекты типа c h a r предъявляют минимальные тре- бования на выравнивание. Как описано в А6.8, указатель может также А 6. Преобразования преобразовываться в void * и обратно, значение указателя при этом не изменяется. Указатель может быть преобразован в другой указатель того же типа с добавлением или удалением квалификаторов (А4.4, А8.2) того типа объекта, на который этот указатель показывает. Новый указатель, полу- ченный добавлением квалификатора, имеет то же значение, но с допол- нительными ограничениями, внесенными новыми квалификаторами. Операция по удалению квалификатора у объекта приводит к тому, что восстанавливается действие его начальных квалификаторов, заданных в объявлении этого объекта. указатель на функцию может быть преобразован в указатель на функцию другого типа. Вызов функции по преобразованному указате- лю зависит от реализации; однако, если указатель еще раз преобразовать к его исходному типу, результат будет идентичен вызову по первоначаль- ному указателю. А 6.7. Тип void Значение (несуществующее) объекта типа void никак нельзя использо- вать, его также нельзя явно или неявно привести к типу, отличному от Поскольку выражение типа void обозначает отсутствие значения, его мож- но применять только там, где не требуется значения. Например, в ка- честве выражения-инструкции (А9.2) или левого операнда у оператора "запятая" (А7.18). Выражение можно привести к типу void операцией приведения типа. Например, применительно к вызову функции, используемому роли вы- ражения-инструкции, операция приведения к void явным образом под- черкивает тот факт, что результат функции отбрасывается. Тип void не фигурировал в первом издании этой книги, однако за прошед- шее время стал общеупотребительным. А 6.8. Указатели на void Любой указатель на объект можно привести к типу void * без потери информации. Если результат подвергнуть обратному преобразованию, то мы получим прежний указатель. В отличие от преобразований указа- тель-в-указатель (рассмотренных в А6.6), которые требуют явных опера- торов приведения к типу, в присваиваниях и сравнениях указатель любо- го типа может выступать в паре с указателем типа void * без каких-либо предварительных преобразований типа. Такая интерпретация указателей void * - новая; ранее роль обобщенного указателя отводилась указателю типа char *. Стандарт ANSI официально 254 Приложение А. Справочное руководство разрешает использование указателей void * совместно с указателями других типов в присваиваниях и сравнениях; в иных комбинациях указателей стан- дарт требует явных преобразований типа. 7. Выражения Приоритеты описываемых операторов имеют же порядок, и пунк- ты данного параграфа (от высших к низшим). Например, для оператора +, Описанного в А7.7, термин "операнды" означает "выражения, определен- ные в В каждом пункте описываются операторы, имеющие оди- наковый приоритет, и указывается их ассоциативность (левая или правая). Приоритеты и ассоциативность всех операторов отражены в грамматике, приведенной в А13. Приоритеты и ассоциативность полностью определены, а вот порядок вычисления выражения не определен за некоторым исключением даже для подвыражений с побочным эффектом. Это значит, что если в определе- нии оператора последовательность вычисления его операндов специаль- но не оговаривается, то в реализации можно свободно выбирать любой порядок вычислений и даже чередовать правый и левый порядок. Однако любой оператор использует значения своих операндов в точном соответ- ствии с грамматическим разбором выражения, в котором он встречается. Это правило отменяет ранее предоставлявшуюся свободу в выборе порядка выполнения операций, которые математически коммутативны и ассоциатив- ны, но которые в процессе вычислений могут таковыми не оказаться. Это изменение затрагивает только вычисления с плавающей точкой, выполня- ющиеся "на грани точности", и ситуации, когда возможно переполнение. В языке не определен контроль за переполнением, делением на нуль и другими исключительными ситуациями, возникающими при вычисле- нии выражения. В большинстве существующих реализаций Си при вы- числении знаковых целочисленных выражений и присваивании пере- полнение игнорируется, но результат таких вычислений не определен. Трактовки деления на нуль и всех исключительных ситуаций, связанных с плавающей точкой, могут не совпадать в разных реализациях; иногда для обработки исключительных ситуаций предоставляется нестандарт- ная библиотечная функция. А Генерация указателя Если тип выражения или подвыражения есть "массив из Т", где Т - некоторый тип, то значением этого выражения является указатель на пер- вый элемент массива, и тип такого выражения заменяется на тип "указа- А 7. Выражения 255 тель на Г". Такая замена типа не если выражение является опе- рандом унарного оператора &, или операндом операций или левым операндом присваивания, или операндом оператора . (точка). Аналогично, выражение типа "функция, возвращающая Т", кроме слу- чая, когда оно является операндом для преобразуется в тип "указатель на функцию, возвращающую А 7.2. Первичные выражения Первичные выражения - это идентификаторы, константы, строки и вы- ражения в скобках. идентификатор константа строка (выражение) Идентификатор, если он был должным образом объявлен (о том, как это делается, речь пойдет ниже), - первичное выражение. Тип идентифи- катора специфицируется в его объявлении. Идентификатор есть lvalue, если он обозначает объект (А5) арифметического типа либо объект типа "структура", "объединение" или "указатель". Константа - первичное выражение. Ее тип зависит от формы записи, которая была рассмотрена в А2.5. Строковый литерал - первичное выражение. Изначально его тип - "массив из г" ("массив из wcha для строки символов расширенного набора), но в соответствии с приведенным в А7.1, указанный тип обычно превращается в "указатель на cha г" ("указатель на wcha с результирующим значением "указатель на первый символ строки". Для некоторых инициализаторов такая замена типа не делается. (См. А8.7.) Выражение в скобках - первичное выражение, тип и значение которо- го идентичны типу и значению этого же выражения без скобок. Наличие или отсутствие скобок не влияет на то, является ли данное выражение lvalue или нет. А 7.3. Постфиксные выражения В постфиксных выражениях операторы выполняются слева направо. первичное-выражение [ выражение ] постфиксное- выражение ( ) . идентификатор идентификатор 256 Приложение А. Справочное руководство ++ — список-аргументов-выражений: выражение-присваивание список-аргументов-выражений , А Обращение к элементам массива Постфиксное выражение, за которым следует выражение в квадратных скобках, есть постфиксное выражение, обозначающее обращение к индек- сируемому массиву. Одно из этих двух выражений должно принадлежать типу "указатель на где Г- некоторый тип, а другое - целочисленному типу; тип результата индексирования есть Т. Выражение Е1 [ Е2] по опре- делению идентично выражению Подробности см. в А8.6.2. Вызов функции Вызов функции есть постфиксное выражение (оно называется имену- ющим выражением функции - function designator), за которым следуют скобки, содержащие (возможно пустой) список разделенных запятыми выражений-присваиваний (А7.17), представляющих собой аргументы этой функции. Если постфиксное выражение - идентификатор, не объяв- ленный в текущей области видимости, то считается, что этот идентифи- катор как бы неявно описан объявлением extern помещенным в самом внутреннем содержащем вызов соответству- ющей функции. Постфиксное выражение (после, возможно неявного, описания и генерации указателя, см. должно иметь тип "указатель на функцию, возвращающую где тип возвращаемого значения. В первой версии языка для именующего выражения функции допускался только тип "функция", и чтобы вызвать функцию через указатель, требо- вался явный оператор поощряет практику некоторых су- ществующих компиляторов, иметь одинаковый синтаксис для обращения просто к функции и обращения к функции, специфицирован- ной указателем. Возможность применения старого синтаксиса остается. Термин аргумент используется для выражения, задаваемого в вызове функции; термин параметр - для обозначения получаемого ею объекта (или его идентификатора) в определении или объявлении функции. Вме- сто этих понятий иногда встречаются термины "фактический аргумент (параметр)" и "формальный аргумент имеющие те же смыс- ловые различия. А 7. Выражения 257 При вызове функции каждый ее аргумент копируется; передача аргу- ментов осуществляется строго через их значения. Функции разрешается изменять значения своих параметров, которые являются лишь копиями аргументов-выражений, но эти изменения не могут повлиять на значе- ния самих аргументов. Однако можно передать указатель, чтобы позво- лить функции изменить значение объекта, на который указывает этот указатель. Имеются два способа объявления функции. В новом способе типы па- раметров задаются явно и являются частью типа функции; такое объяв- ление называется прототипом функции. При старом способе типы па- раметров не указываются. Способы объявления функций обсуждаются А10.1. Если вызов находится в области видимости объявления, написанного по-старому, каждый его аргумент подвергается операции повышения типа: для целочисленных аргументов осуществляется целочисленное повыше- ние (А6.1), а для аргументов типа - преобразование в double. Если число аргументов не соответствует количеству параметров в определе- нии функции или если типы аргументов после повышения не согласуют- ся с типами соответствующих параметров, результат вызова не опреде- лен. Критерий согласованности типов зависит от способа определения функции (старого или нового). При старом способе сравниваются повы- шенный тип аргумента в вызове и повышенный тип соответствующего параметра; при новом способе повышенный тип аргумента и тип пара- метра (без его повышения) должны быть одинаковыми. Если вызов находится в области видимости объявления, написанного по-новому, аргументы преобразуются, как если бы они присваивались переменным, имеющим типы соответствующих параметров прототипа. Число аргументов должно совпадать с числом явно описанных парамет- ров, если только список параметров не заканчивается многоточием ). В противном случае число аргументов должно быть больше числа пара- метров или равно ему; "скрывающиеся" под многоточием аргументы под- вергаются операции повышения типа (так, как это было описано в преды- дущем абзаце). Если определение функции задано по-старому, то типы параметров в прототипе, которые неявно присутствуют в вызове, долж- ны соответствовать типам параметров в определении функции после их повышения. Эти правила особенно усложнились из-за того, что они призваны обслужи- вать смешанный способ (старого с новым) задания функций. По возмож- ности его следует избегать. Очередность вычисления аргументов не определяется, в разных компи- ляторах она различна. Однако гарантируется, что аргументы и именующее 1116 258 Приложение А. Справочное руководство выражение функции вычисляются полностью (включая и побочные эф- до входа в нее. Любая функция допускает рекурсивное обращение. А 7.3.3. Обращение к структурам Постфиксное за которым стоит точка с последующим идентификатором, является постфиксным выражением. Выражение пер- вого операнда должно быть структурой или объединением, а идентифи- катор - именем элемента структуры или объединения. Значение - име- нованный элемент структуры или объединения, тип значения - тип эле- мента структуры или объединения. Выражение является lvalue, если пер- вое выражение - lvalue и если тип второго выражения - не "массив". Постфиксное выражение, за которым стоит стрелка (составленная из знаков - и >) с последующим идентификатором, является постфиксным выражением. Выражение первого операнда должно быть указателем на структуру (объединение), а идентификатор - именем элемента структу- ры (объединения). Результат - именованный элемент структуры (объе- динения), на которую указывает указатель, а тип значения — тип элемен- та структуры (объединения); результат - lvalue, если тип не есть "мас- сив". Таким образом, выражение означает то же что и выра- жение Структуры и объединения рассматриваются в А8.3. В первом издании книги уже было приведено правило, по которому имя элемента должно принадлежать структуре или объединению, упомянутому в постфиксном выражении. Там, однако, оговаривалось, что оно не являет- ся строго обязательным. Последние компиляторы и ANSI делают его обя- зательным. Постфиксные операторы инкремента и декремента Постфиксное выражение, за которым следует ++ или есть постфик- сное выражение. Значением такого выражения является значение его опе- ранда. После того как значение было взято, операнд увеличивается (++) или уменьшается на 1. Операнд должен быть lvalue; информация об ограничениях, накладываемых на операнд, и деталях операций содержится в где обсуждаются аддитивные операторы, и в где рассматри- вается присваивание. Результат или вания не есть lvalue. |