| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553 |
- \chapter{Типы, операторы и выражения}
- \label{chapt:types-operators-expressions}
- Переменные и константы являются основными объектами, с которыми имеет дело
- программа. Переменные перечисляются в описаниях, где устанавливаются их типы, а
- возможно, и начальные значения. Операции специфицируют те действия, которые с
- ними совершаются. Для получения новых значений выражения могут оперировать с
- переменными и константами. Тип объекта определяет множество значений, которые
- этот объект может принимать, и операций, которые над ними могут выполняться.
- Названные <<кирпичики>> и будут предметом обсуждения в этой главе.
- Стандартом ANSI было утверждено значительное число небольших изменений и
- добавлений к основным типам и выражениям. Любой целый тип теперь может быть со
- знаком, \verb|signed|, и без знака, \verb|unsigned|. Предусмотрен способ записи
- беззнаковых констант и шестнадцатеричных литерных констант. Операции с плавающей
- точкой допускаются теперь и с одинарной точностью. Введён тип
- \verb|long double|, обеспечивающий повышенную точность. Стринговые константы
- конкатенируются (<<склеиваются>>) теперь во время компиляции. Перечислимый тип
- стал частью языка, формализующей установку диапазона значений типа.
- \index{const@\texttt{const}}%
- Объекты разрешено помечать как \verb|const| для защиты их от каких-либо
- изменений. В связи с введением новых типов расширены правила автоматического
- преобразования из одного арифметического типа в другой.
- \section{Имена переменных}
- \index{переменная!синтаксис имени}%
- \index{синтаксис имён переменных}%
- Хотя мы ничего не говорили об этом в гл.~\ref{chapt:tutorial_intro}, но
- существуют некоторые ограничения на задание имён переменных и именованных
- констант. Имена составляются из букв и цифр; первой литерой должна быть буква.
- \index{литера!подчёркивания \texttt{\_}}%
- Знак подчёркивания <<\verb|_|>> считается буквой; его иногда удобно
- использовать, чтобы улучшить восприятие длинных имён переменных. Не начинайте
- имена переменных с подчёркивания, так как многие переменные библиотечных
- программ начинаются именно с этого знака. Большие (прописные) и малые (строчные)
- буквы различаются, так что \verb|x| и \verb|X| -- два разных имени. Обычно в
- программах на Си малыми буквами набирают переменные, а большими -- именованные
- константы.
- \index{длина!имени}%
- Для внутренних имён значимыми являются первые 31 литера.
- \index{функция!длина имени}%
- Для имён функций и внешних переменных число значимых литер может быть меньше 31,
- так как эти имена обрабатываются ассемблерами и загрузчиками и языком не
- контролируются. Уникальность внешних имён гарантируется только в пределах 6
- литер, набранных безразлично в каком регистре. Ключевые слова \verb|if|,
- \verb|else|, \verb|int|, \verb|float| и т.д. зарезервированы, и их нельзя
- использовать в качестве имён переменных. Все они набираются на нижнем регистре
- (т.е. малыми буквами).
- Разумно переменным давать осмысленные имена в соответствии с их назначением,
- причём такие, чтобы их было трудно спутать друг с другом. Мы предпочитаем
- короткие имена для локальных переменных, особенно для счётчиков циклов, и более
- длинные для внешних переменных.
- \section{Типы и размеры данных}
- \index{типы!базовые}%
- \index{типы!плавающие}%
- \index{типы!целочисленные}%
- \index{целочисленные типы}%
- \index{числа!размер}%
- В Си существует всего лишь несколько базовых типов:
- \begin{LongRtAlTab2cols}{\codeIndent}{0pt}{\Verb|double|}
- \verb|char|%
- \index{char@\texttt{char}!тип}%
- &единичный байт, который может содержать одну литеру из допустимого набора
- литер\\
- \verb|int|%
- \index{int@\texttt{int}!тип}%
- &целое, обычно отображаемое на естественное представление целых в машине\\
- \verb|float|%
- \index{float@\texttt{float}!тип}%
- &число с плавающей точкой одинарной точности\\
- \verb|double|%
- \index{double@\texttt{double}!тип}%
- &число с плавающей точкой двойной точности
- \end{LongRtAlTab2cols}
- \index{long@\texttt{long}!тип}%
- \index{short@\texttt{short}}%
- Имеется также несколько квалификаторов, которые можно использовать вместе с
- указанными базовыми типами. Например, квалификаторы \verb|short| (короткий) и
- \verb|long| (длинный) применяются к целым:
- \begin{ShortCodePar}
- short int sh;
- long int counter;
- \end{ShortCodePar}
- \noindent В таких описаниях слово \verb|int| можно опускать, что обычно и
- делается.
- Если только не возникает противоречий со здравым смыслом, целое \verb|short| и
- целое \verb|long| должны быть разной длины, а \verb|int| соответствовать
- естественному размеру целых на данной машине. Чаще всего для представления
- целого, описанного с квалификатором \verb|short|, отводится 16 бит, с
- квалификатором \verb|long| -- 32 бита, а значению типа \verb|int| -- или 16,
- или 32 бита. Разработчики компилятора вправе сами выбирать подходящие размеры,
- сообразуясь с характеристиками своего компьютера и соблюдая только следующие
- ограничения: значения типов \verb|short| и \verb|int| представляются по крайней
- мере 16 битами, типа \verb|long| -- по крайней мере 32 битами, размер
- \verb|short| не больше размера \verb|int|, который в свою очередь не больше
- размера \verb|long|.
- \index{signed@\texttt{signed}!тип}%
- \index{unsigned@\texttt{unsigned}!тип}%
- \index{unsigned char, тип@\texttt{unsigned char}, тип}%
- Квалификаторы \verb|signed| (со знаком) или \verb|unsigned| (без знака) можно
- применять к типу \verb|char| и любому целому типу. Значения \verb|unsigned|
- всегда положительны или равны нулю и подчиняются законам арифметики по модулю
- $2^n$, где $n$ -- количество бит в представлении типа. Так, например, если
- значению \verb|char| отводится 8 бит, то \verb|unsigned char| имеет значения в
- диапазоне от $0$ до $255$, а \verb|signed char| -- от $-128$ до $127$ (в машине
- с двоичным дополнительным кодом). Являются ли значения типа просто \verb|char|
- знаковыми или беззнаковыми, зависит от машины, но в любом случае коды печатаемых
- литер положительны.
- \index{long double@\texttt{long double}!тип}%
- Тип \verb|long double| предназначен для арифметики с плавающей точкой повышенной
- точности. Как и в случае целых, размеры объектов с плавающей точкой зависят от
- реализации; \verb|float|, \verb|double| и \verb|long double| могут
- представляться одним размером, а могут -- двумя или тремя разными размерами.
- \index{файл!головной!<float.h>@\texttt{<float.h>}}%
- Именованные константы для всех размеров вместе с другими характеристиками машины
- и компилятора содержатся в стандартных головных файлах
- \verb|<limits.h>| и \verb|<float.h>|. (См.приложение~\ref{apx:stdlib}.)
- \paragraph{Упражнение 2.1.} Напишите программу, которая будет выдавать диапазоны
- значений типов \verb|char|, \verb|short|, \verb|int| и \verb|long|, описанных
- как \verb|signed| и как \verb|unsigned|, с помощью печати соответствующих
- значений из стандартных головных файлов и путём прямого вычисления. Определите
- диапазоны чисел с плавающей точкой различных типов. Вычислить эти диапазоны
- сложнее.
- \section{Константы}
- \label{sec:constants}
- \index{константа}%
- \index{константа!суффикс}%
- \index{константа!тип}%
- \index{суффикс в константе}%
- \index{тип!константа}%
- \index{целая константа}%
- \index{int@\texttt{int}!константа}%
- \index{signed@\texttt{signed}!константа}%
- \index{unsigned@\texttt{unsigned}!константа}%
- Целая константа, например, \verb|1234|, имеет тип \verb|int|.
- \index{long@\texttt{long}!константа}%
- \index{unsigned long, константа@\texttt{unsigned long}, константа}%
- Константа типа \verb|long| завершается буквой \verb|l| или \verb|L|, например
- \verb|123456789L|; слишком большое целое, которое невозможно представить как
- \verb|int|, будет представлено как \verb|long|. Беззнаковые константы
- заканчиваются буквой \verb|u| или \verb|U|, а окончание \verb|ul| или \verb|UL|
- говорит о том, что тип константы -- \verb|unsigned long|.
- \index{константа!с плавающей точкой}%
- \index{экспонента в записи числа}%
- \index{double@\texttt{double}!константа}%
- \index{E (спецификатор экспоненты)@\texttt{E} (спецификатор экспоненты)}%
- \index{float@\texttt{float}!константа}%
- \index{long double@\texttt{long double}!константа}%
- Константы с плавающей точкой имеют десятичную точку (\verb|123.4|) или
- экспоненциальную часть (\verb|1e-2|) или же и то и другое. Если у них нет
- окончания, считается, что они типа \verb|double|. Окончание \verb|f| или
- \verb|F| указывает на тип \verb|float|, а \verb|l| или \verb|L| -- на тип
- \verb|long double|.
- Помимо десятичного целое значение может иметь восьмеричное или шестнадцатеричное
- представление.
- \index{константа!восьмеричная \texttt{0}\ldots}%
- \index{восьмеричная константа \texttt{0}\ldots}%
- Если константа начинается с нуля, то она представлена в
- восьмеричном виде, если с \verb|0x| или
- \index{константа!шестнадцатеричная \texttt{0x}\ldots}%
- \index{шестнадцатеричная константа \texttt{0x}\ldots}%
- с \verb|0X|, то -- в шестнадцатеричном.
- Например, десятичное целое \verb|31| можно записать как \verb|037| или как
- \verb|0X1F|. Записи восьмеричной и шестнадцатеричной констант могут завершаться
- буквой \verb|L| (для указания на тип \verb|long|) и \verb|U| (если нужно
- показать, что константа беззнаковая). Например, константа \verb|0XFUL| имеет
- значение $15$ и тип \verb|unsigned long|.
- %
- % исправлены орфографические ошибки
- % в оригинале было
- % "шестнадцатиричное", "шестнадцатиричном", "шестнадцатиричной"
- %
- \index{константа!литерная}%
- \index{char@\texttt{char}!константа}%
- \emph{Литерная константа} есть целое, записанное в виде литеры, обрамленной
- одиночными кавычками, например \verb|'x'|.
- \index{переносимость}%
- Значением литерной константы является
- числовой код литеры из набора литер на данной машине.
- \index{литеры!набор!ASCII}%
- \index{ASCII}%
- Например, литерная константа \verb|'0'| в кодировке ASCII имеет значение $48$,
- которое никакого отношения к числовому значению 0 не имеет. Если мы пишем
- \verb|'0'|, а не какое-нибудь значение (например, $48$), которое следует из
- способа кодировки, мы тем самым делаем программу независимой от частного
- значения кода, к тому же она и легче читается. Литерные константы могут
- участвовать в операциях над числами точно так же, как и любые другие целые, хотя
- чаще они используются для сравнения с другими литерами.
- \index{литера!новая-строка \texttt{\textbackslash n}}%
- \index{эскейп-последовательность}%
- \index{n@\texttt{\textbackslash n}}%
- Некоторые литеры в литерных и стринговых константах записываются с помощью
- эскейп-последовательностей, например \verb|\n| (новая-строка); такие
- последовательности изображаются двумя литерами, но обозначают одну.
- \index{константа!литерная!восьмеричная \texttt{\textbackslash\textit{ooo}}}%
- \index{эскейп-последовательность!восьмеричная \texttt{\textbackslash\textit{ooo}}}%
- Кроме того, произвольный восьмеричный код можно задать в виде
- \begin{ShortCodeParWithCC}{+\{\}}
- '\+textit{ooo}'
- \end{ShortCodeParWithCC}
- \noindent где \textit{ooo} -- одна, две или три восьмеричные цифры
- (\verb|0|\ldots\verb|7|) или
- \index{константа!литерная!шестнадцатеричная \texttt{\textbackslash x\textit{hh}}}%
- \index{эскейп-последовательность!шестнадцатеричная \texttt{\textbackslash x\textit{hh}}}%
- \begin{ShortCodeParWithCC}{+\{\}}
- '\x+textit{hh}'
- \end{ShortCodeParWithCC}
- \noindent где \textit{hh} -- одна, две или более шестнадцатеричные цифры
- (\verb|0|\ldots\verb|9|, \verb|a|\ldots\verb|f|‚ \verb|A|\ldots\verb|F|). Таким
- образом, мы могли бы написать
- %
- % исправлена орфографическая ошибка
- % в оригинале было "шестнадцатиричные"
- %
- \begin{ShortCodePar}
- #define VTAB '\013' /* верт. табуляция в ASCII */
- #define BELL '\007' /* звонок в ASCII */
- \end{ShortCodePar}
- \noindent или в шестнадцатеричном виде:
- %
- % исправлена орфографическая ошибка
- % в оригинале было "шестнадцатиричном"
- %
- \begin{ShortCodePar}
- #define VTAB '\xb' /* верт. табуляция в ASCII */
- #define BELL '\x7' /* звонок в ASCII */
- \end{ShortCodePar}
- \noindent%
- \index{эскейп-последовательность}%
- Полный набор эскейп-последовательностей следующий:
- \smallskip
- \index{таблица!эскейп-последовательностей}%
- {\setlength{\LTleft}{\codeIndent-\tabcolsep}%
- \SetLenVarWithVal{\Halflen}{(\linewidth-\LTleft-\tabcolsep)/2}%
- \SetLenVarWithWidth{\colAlen}{\Verb|\textbackslash a|}%
- \SetLenVarWithWidth{\colClen}{\Verb|\textbackslash x|\textit{\Verb|hh|}}%
- \noindent\hspace{\LTleft}\begin{tabular}
- {p{\colAlen}p{\Halflen-6\tabcolsep-\colAlen}p{\colClen}p{\Halflen-\colClen}}
- \verb|\a|%
- \index{литера!сигнал-звонок \texttt{\textbackslash a}}%
- \index{a@\texttt{\textbackslash a}}%
- &сигнал-звонок&\verb|\\|%
- \index{литера!обратная наклонная черта \texttt{\textbackslash\textbackslash}}%
- &обратная-наклонная-черта\\
- \verb|\b|%
- \index{b (backspace)@\texttt{\textbackslash b} (backspace)}%
- &возврат-на-шаг&\verb|\?|&знак-вопроса\\
- \verb|\f|%
- \index{литера!новая-страница \texttt{\textbackslash f}}%
- \index{f литера новая-страница@\texttt{\textbackslash f} литера новая-страница}%
- &перевод-страницы&\verb|\'|%
- \index{литера!кавычка \texttt{'}}%
- &одиночная-кавычка\\
- \verb|\n|%
- \index{литера!новая-строка \texttt{\textbackslash n}}%
- \index{n@\texttt{\textbackslash n}}%
- &новая-строка&\verb|\"|%
- \index{литера!двойная кавычка \texttt{\char`\"}}%
- &двойная-кавычка\\
- \verb|\r|%
- \index{литера!возврат каретки \texttt{\textbackslash r}}%
- \index{r@\texttt{\textbackslash r}}%
- &возврат-каретки
- &\verb|\|\textit{\Verb|ooo|}%
- \index{ooo@\texttt{\textbackslash\textit{ooo}}}%
- &восьмеричный-код\\
- \verb|\t|%
- \index{t@\texttt{\textbackslash t}}%
- &гор-табуляция
- &\verb|\x|\textit{\Verb|hh|}%
- \index{xhh шестнадцатеричная эскейп-последовательность@\texttt{\textbackslash x\textit{hh}} шестнадцатеричная эскейп-последовательность}%
- &шестнадцатиричный-код\\
- \verb|\v|%
- \index{литера!вертикальная табуляция \texttt{\textbackslash v}}%
- \index{v@\texttt{\textbackslash v}}%
- &верт-табуляция &&\\
- \end{tabular}}
- \smallskip
- \index{null-литера, 0@null-литера, \texttt{\textbackslash 0}}%
- Литерная константа \verb|'\0'| -- это литера с нулевым значением -- так
- называемая литера null. Вместо просто 0 часто используют запись \verb|'\0'|‚
- чтобы подчеркнуть литерную природу выражения, хотя и в том и другом случае
- запись обозначает нуль.
- \index{выражение!константное}%
- \index{константное выражение}%
- \emph{Константные выражения} -- это выражения, оперирующие только с константами.
- Такие выражения вычисляются во время компиляции, а не во время счёта, и поэтому
- их можно использовать в любом месте, где допустимы константы, как, например, в
- \begin{ShortCodePar}
- #define MAXLINE 1000
- char line[MAXLINE+1];
- \end{ShortCodePar}
- \noindent или в
- \begin{ShortCodePar}
- #define LEAP 1 /* in leap years - в високосные годы */
- int days[31+28+LEAP+31+30+31+30+31+31+30+31+30+31];
- \end{ShortCodePar}
- \index{константа!стринговая}%
- \emph{Стринговая константа}, или \emph{стринговый литерал}, -- это нуль или
- более литер, заключённых в двойные кавычки, как, например,
- \begin{ShortCodePar}
- "Это стринговая константа"
- \end{ShortCodePar}
- \noindent или
- \index{стринг!пустой}%
- \begin{ShortCodePar}
- "" /* пустой стринг */
- \end{ShortCodePar}
- \noindent%
- \index{стринг!длина}%
- Кавычки не входят в стринг, а служат только её ограничителями. Так же,
- как и в литерные константы, в стринги можно включать эскейп-последовательности;
- \verb|\"|, например, представляет собой двойную кавычку.
- \index{конкатенация!стрингов}%
- \index{стринг!конкатенация}%
- Стринговые константы можно конкатенировать (<<склеивать>>) во время компиляции;
- например, запись двух стрингов
- \begin{ShortCodePar}
- "Здравствуй," " мир!"
- \end{ShortCodePar}
- \noindent эквивалентна записи одного следующего стринга:
- \begin{ShortCodePar}
- "Здравствуй, мир!"
- \end{ShortCodePar}
- \noindent Указанное свойство позволяет разбивать длинные стринги на части и
- располагать эти части на отдельных строчках.
- \index{длина!стринга}%
- \index{стринг!длина}%
- Фактически стринговая константа -- это массив литер. Во внутреннем представлении
- стринга в конце обязательно присутствует null-литера \verb|'\0'|, поэтому памяти
- для стринга требуется на один байт больше, чем число литер, расположенных между
- двойными кавычками. Это означает, что нет ограничения на длину задаваемого
- стринга, но чтобы определить его длину, требуется просмотреть весь стринг.
- \index{функция!strlen@\texttt{strlen}}%
- Функция \verb|strlen(s)| вычисляет длину стринга \verb|s| без учёта завершающей
- его литеры \verb|'\0'|. Ниже приводится наша версия этой функции:
- \begin{LongCodePar}
- /* strlen: возвращает длину стринга */
- int strlen(char s[])
- {
- int i;
- i = 0;
- while (s[i] != '\0')
- ++i;
- return i;
- }
- \end{LongCodePar}
- \noindent%
- \index{файл!головной!<string.h>@\texttt{<string.h>}}%
- Функция \verb|strlen| и некоторые другие, применяемые к стрингам, описаны в
- стандартном головном файле \verb|<string.h>|.
- Будьте внимательны и помните, что литерная константа и стринг, содержащий одну
- литеру, не одно и то же: \verb|'x'| не то же самое, что \verb|"x"|. Запись
- \verb|'x'| обозначает целое значение, равное коду буквы \verb|x| из стандартного
- набора литер, а запись \verb|"x"| -- массив литер, который содержит одну литеру
- (букву \verb|x|) и \verb|'\0'|.
- \index{константа!перечисления}%
- \index{перечисление!константа}%
- \index{спецификатор!enum@\texttt{enum}}%
- \index{enum@\texttt{enum}!спецификатор}%
- В Си имеется ещё один вид константы, \emph{константа перечисления}. Перечисление
- -- это список целых констант, как, например, в
- \begin{ShortCodePar}
- enum boolean { NO, YES };
- \end{ShortCodePar}
- \noindent Первое имя в \verb|enum|\footnote{От английского слова enumeration --
- перечисление. -- \textit{Примеч. ред.}} имеет значение $0$, следующее -- $1$ и
- т.д. (если не было явных спецификаций значений констант). Если не все значения
- специфицированы, то они продолжают прогрессию, начиная от последнего
- специфицированного значения, как в следующих двух примерах:
- \begin{ShortCodePar}
- enum escapes { BELL = '\a', BACKSPACE = '\b', TAB = '\t',
- NEWLINE = '\n', VTAB = '\v', RETURN = '\r' };
- enum months { JAN = 1, FEB, MAR, APR, MAY, JUN,
- JUL, AUG, SEP, OCT, NOV, DEC };
- /* FEB есть 2, MAR есть 3 и т.д. */
- \end{ShortCodePar}
- \noindent Имена в различных перечислениях должны отличаться друг от друга.
- Значения внутри одного перечисления могут совпадать.
- \index{define@\texttt{{\#}define}!вместо \texttt{enum}}%
- \index{enum@\texttt{enum}!а не \texttt{{\#}define}}%
- Средство \verb|enum| обеспечивает удобный способ присвоить константам имена,
- причём в отличие от \verb|#define| при этом способе значения констант могут
- генерироваться автоматически. Перечислимый тип разрешено использовать для
- определения переменных, однако компилятор не обязан контролировать, входят ли
- присваиваемые этим переменным значения в их тип. Но сама возможность такой
- проверки часто делает \verb|enum| лучше, чем \verb|#define|. Кроме того,
- отладчик получает возможность печатать значения перечислимых переменных в
- символьном виде.
- \section{Декларации}
- \index{декларация}%
- Все переменные должны быть декларированы раньше, чем будут использоваться, при
- этом некоторые декларации могут быть получены неявно -- из контекста. Декларация
- специфицирует тип и содержит список из одной или нескольких переменных этого
- типа, как, например, в
- \begin{ShortCodePar}
- int lower, upper, step;
- char c, line[1000];
- \end{ShortCodePar}
- \noindent%
- \index{программа!формат}%
- Переменные можно распределять по декларациям произвольным образом, так что
- указанные выше списки можно записать и в следующем виде:
- \begin{ShortCodePar}
- int lower;
- int upper;
- int step;
- char c;
- char line[1000];
- \end{ShortCodePar}
- Эта последняя форма записи занимает больше места, тем не менее она лучше‚
- поскольку позволяет добавлять к каждой декларации комментарий и более удобна для
- последующих модификаций.
- \index{инициализация}%
- \index{инициализация!переменных!автоматических}%
- В своей декларации переменная может быть инициализирована, как, например:
- \begin{ShortCodePar}
- char esc = '\\';
- int i = 0;
- int limit = MAXLINE+1;
- float eps = 1.0e-5;
- \end{ShortCodePar}
- Инициализация неавтоматической переменной осуществляется только один раз --
- перед тем, как программа начнёт выполняться, при этом инициализатор должен быть
- константным выражением. Явно инициализируемая автоматическая переменная получает
- начальное значение каждый раз при входе в функцию или блок, её инициализатором
- может быть любое выражение.
- \index{инициализация!переменных!внешних}%
- \index{инициализация!переменных!статических}%
- Внешние и статические переменные по умолчанию получают нулевые значения.
- Автоматические переменные, явным образом не инициализированные, содержат
- неопределённые значения (<<мусор>>).
- К любой переменной в декларации может быть применён квалификатор \verb|const|
- для указания того, что её значение далее не будет изменяться.
- \begin{ShortCodePar}
- const double e = 2.71828182845905;
- const char msg[] = "предупреждение: ";
- \end{ShortCodePar}
- \noindent Применительно к массиву квалификатор \verb|const| указывает на то, что
- ни один из его элементов не будет меняться. Указание \verb|const| можно также
- применять к аргументу-массиву, чтобы сообщить, что функция не изменяет этот
- массив:
- \begin{ShortCodePar}
- int strlen(const char[]);
- \end{ShortCodePar}
- \noindent Реакция на попытку изменить переменную, помеченную квалификатором
- \verb|const|, оставлена на усмотрение компилятора.
- \section{Арифметические операторы}
- \index{оператор!вычитания \texttt{\textminus}}%
- \index{оператор!деления \texttt{\slash}}%
- \index{оператор!деления по модулю \texttt{\%}}%
- \index{оператор!сложения \texttt{+}}%
- \index{оператор!умножения \texttt{*}}%
- \index{операторы!арифметические}%
- Бинарными арифметическими операторами являются \verb|+|, \verb|-|‚ \verb|*|,
- \verb|/|‚ а также оператор взятия модуля \verb|%|.
- \index{деление целых}%
- Деление целых сопровождается отбрасыванием дробной части, какой бы она ни была.
- Выражение
- \begin{ShortCodePar}
- x%y
- \end{ShortCodePar}
- \noindent даёт остаток от деления \verb|x| на \verb|y| и, следовательно, нуль,
- если \verb|x| делится на \verb|y| нацело. Например,
- \index{високосный год, вычисление}%
- год является високосным, если он делится на $4$ (но не на $100$). Кроме того,
- високосным считается год, если он делится на $400$. Следовательно,
- \begin{ShortCodePar}
- if ((year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
- printf(”%d високосный год\n, year);
- else
- printf("%d не високосный год\n, year);
- \end{ShortCodePar}
- \noindent Оператор \verb|%| к операндам типов \verb|float| и \verb|double| не
- применяется.
- \index{обрезание!при делении}%
- В какую сторону (в сторону увеличения или уменьшения числа) будет усечена
- дробная часть при выполнении \verb|/| и каким будет знак результата операции
- \verb|%| с отрицательными операндами, это зависит от машины.
- Бинарные операторы \verb|+| и \verb|-| имеют одинаковый приоритет, который ниже
- приоритета операторов \verb|*|, \verb|/| и \verb|%|, который в свою очередь ниже
- приоритета унарных операторов \verb|+| и \verb|-|. Арифметические операции
- одного приоритетного уровня выполняются слева направо.
- В конце этой главы приводится табл.~\ref{tab:precedence}, в которой показаны
- приоритеты всех операторов и порядок их выполнения.
- \section{Операторы отношения и логические операторы}
- \index{оператор!меньше или равно \texttt{\textless=}}%
- \index{оператор!меньше \texttt{\textless}}%
- \index{оператор!больше или равно \texttt{\textgreater=}}%
- \index{оператор!больше \texttt{\textgreater}}%
- \index{оператор!неравенства \texttt{"!=}}%
- \index{оператор!равенства \texttt{==}}%
- \index{операторы!отношения}%
- \index{операторы!равенства}%
- Операторами отношения являются
- \begin{ShortCodePar}
- > >= < <=
- \end{ShortCodePar}
- \noindent Все они имеют одинаковый приоритет. Ровно на одну ступень ниже
- приоритета операторов сравнения на равенство:
- \begin{ShortCodePar}
- == !=
- \end{ShortCodePar}
- \noindent Операторы отношения имеют более низкий приоритет, чем арифметические,
- поэтому выражение типа \verb|i < lim-1| будет выполняться так же, как
- \verb|i < (lim-1)|, т.е. как мы и ожидаем.
- \index{оператор!логического И \texttt{\&\&}}%
- \index{оператор!логического ИЛИ \texttt{"|"|}}%
- Более интересны логические операторы \verb|&&| и \verb+||+. Выражения, между
- которыми стоят операторы \verb|&&| или \verb+||+, вычисляются слева направо, и
- вычисление прекращается, как только становится известна истинность или ложность
- результата. Многие Си-программы опираются на это свойство, как, например, цикл
- из функции \verb|getline| ‚ которую мы приводили в
- гл.~\ref{chapt:tutorial_intro}:
- \begin{ShortCodePar}
- for (i=0; i<lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
- s[i] = c;
- \end{ShortCodePar}
- Прежде чем читать очередную литеру, нужно проверить, есть ли место для неё в
- массиве \verb|s|, иначе говоря, сначала необходимо проверить условие
- \verb|i < lim-1|. Если это условие не выполняется, мы не должны продолжать
- вычисление, в частности читать следующую литеру. Так же было бы неправильным
- сравнивать \verb|c| с \verb|EOF| до обращения к \verb|getchar|; следовательно, и
- вызов \verb|getchar| и присваивание должны выполняться перед указанной
- проверкой.
- Приоритет оператора \verb|&&| выше, чем оператора \verb+||+, однако их
- приоритеты ниже, чем приоритет операторов отношения и равенства; поэтому
- выражение вида
- \begin{ShortCodePar}
- i<lim-1 && (c = getchar()) != '\n' && c != EOF
- \end{ShortCodePar}
- \noindent не нуждается в дополнительных скобках. Но,
- \index{оператор!присваивания \texttt{=}}%
- \index{операторы!присваивания}%
- так как приоритет \verb|!=| выше, чем приоритет присваивания, в
- \begin{ShortCodePar}
- (c = getchar()) != '\n'
- \end{ShortCodePar}
- \noindent скобки необходимы, чтобы сначала выполнить присваивание, а затем
- сравнение с \verb|n|.
- \index{численное значение!выражения отношения}%
- По определению численным результатом вычисления выражения отношения или
- логического является $1$ в случае, если оно истинно, и $0$ в случае, если оно
- ложно.
- \index{оператор!логического отрицания \texttt{"!}}%
- Унарный оператор \verb|!| преобразует ненулевой операнд в $0$, а нуль в $1$.
- Обычно оператор \verb|!|, используют в конструкциях вида
- \begin{ShortCodePar}
- if (!valid)
- \end{ShortCodePar}
- \noindent что эквивалентно
- \begin{ShortCodePar}
- if (valid == 0)
- \end{ShortCodePar}
- \noindent Трудно сказать, какая из форм записи лучше. Конструкция вида
- \verb|!valid| хорошо читается (<<если не \verb|valid|>>)‚ но в случае более
- сложных выражений может оказаться, что её не так-то легко понять.
- \paragraph{Упражнение 2.2.} Напишите цикл, эквивалентный приведённому выше
- \verb|for|-циклу, не пользуясь операторами \verb|&&| и \verb+||+.
- \section{Преобразования типов}
- \label{chapt:types-operators-expressions:sec:typecasting}
- \index{преобразование!обычное арифметическое}%
- \index{тип!правила преобразования|(}%
- Если операнды оператора принадлежат разным типам, то они приводятся к некоторому
- общему типу. Приведение выполняется в соответствии с небольшим числом правил.
- Обычно автоматически производятся лишь те преобразования, которые без какой-либо
- потери информации превращают операнды с меньшим диапазоном значений в операнды с
- большим диапазоном значений, как, например, преобразование целого в число с
- плавающей точкой в выражении вроде \verb|f + i|. Выражения, не имеющие смысла,
- например число с плавающей точкой в роли индекса, не допускаются. Выражения, в
- которых могла бы теряться информация (скажем, при присваивании длинных целых
- переменным более коротких типов или при присваивании значений с плавающей точкой
- целым переменным), могут повлечь предупреждение, но они допустимы.
- \index{преобразование!литера -- целое}%
- Значения типа \verb|char| -- это всего лишь малые целые, и их можно свободно
- использовать в арифметических выражениях, что значительно облегчает
- всевозможные манипуляции с литерами.
- \index{функция!atoi@\texttt{atoi}}%
- В качестве примера приведём простенькую реализацию функции \verb|atoi|,
- преобразующей последовательность цифр в её числовой эквивалент.
- \begin{LongCodePar}
- /* atoi: преобразование s в целое */
- int atoi(char s[])
- {
- int i, n;
- n = 0;
- for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
- n = 10 * n + (s[i] - '0');
- return n;
- }
- \end{LongCodePar}
- Как мы уже говорили в гл.~\ref{chapt:tutorial_intro}, выражение
- \begin{ShortCodePar}
- s[i] - '0'
- \end{ShortCodePar}
- \noindent даёт числовое значение литеры хранящейся в \verb|s[i]|‚ так как
- значения \verb|'0'|, \verb|'1'| и т.д. образуют непрерывную возрастающую
- последовательность.
- \index{преобразование!литера -- целое}%
- \index{функция!lower@\texttt{lower}}%
- Другой пример приведения \verb|char| к \verb|int| связан с функцией
- \verb|lower|, которая одиночную литеру из набора ASCII, если она является
- заглавной буквой, превращает в прописную. Если же литера не является заглавной
- буквой, \verb|lower| её не изменяет.
- \begin{LongCodePar}
- /* lower: преобразование c в строчную; только для ASCII */
- int lower(int c)
- {
- if (c >= 'A' && c <= 'Z')
- return c + 'a' - 'A';
- else
- return c;
- }
- \end{LongCodePar}
- %%
- %% в оригинале отсутствует закрывающая фигурная скобка
- %%
- \noindent%
- \index{литеры!набор!ASCII}%
- \index{переносимость}%
- \index{ASCII}%
- В случае ASCII эта программа будет правильно работать потому, что между
- одноимёнными буквами верхнего и нижнего регистров -- одинаковое расстояние (если
- их рассматривать как числовые значения), и, кроме того, латинский алфавит --
- плотный в том смысле, что между буквами A и Z ничего кроме букв не существует.
- \index{литеры!набор!EBCDIC}%
- \index{EBCDIC}%
- Для набора EBCDIC последнее условие не выполняется, и поэтому наша программа в
- этом случае будет преобразовывать не только буквы.
- \index{файл!головной!<ctype.h>@\texttt{<ctype.h>}}%
- Стандартный головной файл \verb|<ctype.h>|‚ описанный в
- приложении~\ref{apx:stdlib}, определяет семейство функций, которые позволяют
- проверять и преобразовывать литеры независимо от набора литер. Например, функция
- \verb|tolower(c)| возвращает букву \verb|c| в коде нижнего регистра, если она
- была в коде верхнего регистра, поэтому \verb|tolower(c)| -- универсальная
- замена функции \verb|lower(c)|, рассмотренной выше. Аналогично проверку
- \begin{ShortCodePar}
- c >= '0' && c <='9'
- \end{ShortCodePar}
- \noindent можно заменить на
- \begin{ShortCodePar}
- isdigit(c)
- \end{ShortCodePar}
- \noindent Далее мы будем пользоваться функциями из \verb|<ctype.h>|.
- \index{литера!знаковая}%
- \index{литера!беззнаковая}%
- \index{оператор!приведения к типу}%
- \index{переносимость}%
- \index{повышение!типа аргумента}%
- \index{повышение!целочисленного типа}%
- \index{преобразование!литера -- целое}%
- \index{преобразование!оператором приведения}%
- \index{приведение к типу}%
- \index{целочисленное повышение}%
- Существует одна тонкость, касающаяся преобразования литер в целые: язык не
- определяет, являются ли переменные типа \verb|char| знаковыми или беззнаковыми.
- При преобразовании \verb|char| в \verb|int| может когда-нибудь получиться
- отрицательное целое? На машинах с разной архитектурой ответы могут отличаться.
- \index{знак, размножение}%
- На некоторых машинах значение типа \verb|char| с единичным старшим битом будет
- превращено в отрицательное целое (посредством <<размножения знака>>). На других
- -- преобразование \verb|char| в \verb|int| осуществляется добавлением нулей
- слева, и, таким образом, получаемое значение всегда положительно.
- Гарантируется, что любая литера из стандартного набора печатаемых литер никогда
- не будет отрицательным числом, поэтому в выражениях такие литеры всегда являются
- положительными операндами. Но произвольный восьмибитовый код в переменной типа
- \verb|char| на одних машинах может быть отрицательным числом, а на других --
- положительным. Для совместимости переменные типа \verb|char|, в которых хранятся
- нелитерные данные, следует специфицировать явно как \verb|signed| или
- \verb|unsigned|.
- \index{операторы!отношения}%
- Отношения типа \verb|i > j| и
- \index{выражение!логическое, численное значение}%
- \index{численное значение!выражения отношения}%
- \index{численное значение!логического выражения}%
- логические выражения, перемежаемые операторами \verb|&&| и \verb!||!, определяют
- выражение-условие, которое имеет значение $1$, если оно истинно, и $0$, если
- ложно. Так, присваивание
- \begin{ShortCodePar}
- d = c >='0' && c <= '9'
- \end{ShortCodePar}
- \noindent установит в \verb|d| значение $1$, если \verb|c| есть цифра, и $0$ в
- противном случае. Однако функции, подобные \verb|isdigit|, в случае истины могут
- выдавать любое ненулевое значение. В местах проверок внутри \verb|if|,
- \verb|while|, \verb|for| и т.д. <<истина>> просто означает <<не нуль>>.
- \index{оператор!приведения к типу}%
- \index{повышение!типа аргумента}%
- \index{повышение!целочисленного типа}%
- \index{преобразование!обычное арифметическое}%
- \index{преобразование!целое -- плавающее}%
- \index{преобразование!\texttt{float} -- \texttt{double}}%
- \index{целочисленное повышение}%
- Неявные арифметические преобразования, как правило, осуществляются естественным
- образом. В общем случае, когда оператор типа \verb|+| или \verb|*| с двумя
- операндами (бинарный оператор) имеет разнотипные операнды, прежде чем операция
- начнёт выполняться, <<младший>> тип \emph{подтягивается} к <<старшему>>.
- Результат будет иметь старший тип. В разд.~\ref{apx:sec:type_conversion}
- приложения~\ref{apx:ref_manual} правила преобразования сформулированы точно.
- Если же в выражении нет беззнаковых операндов, можно удовлетвориться следующим
- набором неформальных правил:
- \begin{itemize}
- \item Если какой-либо из операндов принадлежит типу \verb|long double|, то
- другой приводится к \verb|long double|.
- \item В противном случае, если какой-либо из операндов принадлежит типу
- \verb|double|, то другой приводится к \verb|double|.
- \item В противном случае, если какой-либо из операндов принадлежит типу
- \verb|float|, то другой приводится к \verb|float|.
- \item В противном случае операнды типов \verb|char| и \verb|short| приводятся
- к \verb|int|.
- \item И наконец, если один из операндов типа \verb|long|, то другой приводится
- к \verb|long|.
- \end{itemize}
- \index{преобразование!\texttt{float} -- \texttt{double}}%
- Заметим, что операнды типа \verb|float| не приводятся автоматически к типу
- \verb|double|; в этом данная версия языка отличается от первоначальной.
- \index{файл!головной!<math.h>@\texttt{<math.h>}}%
- Вообще говоря, математические функции, аналогичные собранным в библиотеке
- \verb|<math.h>|, базируются на вычислениях с двойной точностью. В основном
- \verb|float| используется для экономии памяти на больших массивах и менее часто
- для убыстрения счёта на тех машинах, где арифметика двойной точности слишком
- дорога.
- Правила преобразования усложняются с появлением \verb|unsigned|-операндов.
- Проблема в том, что сравнения знаковых и беззнаковых значений зависят от
- размеров целых типов, которые на разных машинах могут отличаться. Предположим,
- что значение типа \verb|int| занимает 16 бит, а значение типа \verb|long| -- 32
- бита. Тогда \verb|-1L < 1U|, поскольку \verb|1U| принадлежит типу \verb|int| и
- подтягивается к типу \verb|signed long|. Но \verb|-1L > 1UL|, так как
- \verb|-1L| подтягивается к типу \verb|unsigned long| и воспринимается как
- большое положительное число.
- \index{преобразование!присваиванием}%
- Преобразования имеют место и при присваиваниях: значение правой части
- присваивания приводится к типу левой части, который и является типом результата.
- \index{преобразование!литера -- целое}%
- Литера превращается в целое посредством
- \index{знак, размножение}%
- размножения знака или другим описанным выше способом.
- \index{преобразование!целое -- литера}%
- Длинные целые преобразуются в короткие целые или в значения типа \verb|char| с
- помощью отбрасывания старших разрядов. Так, в
- \begin{ShortCodePar}
- int i;
- char c;
- i = c;
- c = i;
- \end{ShortCodePar}
- \noindent значение \verb|c| не изменится. Это справедливо независимо от того,
- размножается знак при переводе \verb|char| в \verb|int| или нет. Однако, если
- изменить порядок присваиваний, возможна потеря информации.
- \index{обрезание!значения с плавающей точкой}%
- \index{преобразование!плавающее -- целое}%
- \index{преобразование!\texttt{double} -- \texttt{float}}%
- Если \verb|x| принадлежит типу \verb|float|, а \verb|i| типу \verb|float|, то и
- \verb|x = i|, и \verb|i = x| вызовут преобразования, причём перевод \verb|float|
- в \verb|int| сопровождается отбрасыванием дробной части. Если \verb|double|
- переводится в \verb|float|, то значение либо округляется, либо обрезается; это
- зависит от реализации.
- \index{преобразование!\texttt{float} -- \texttt{double}}%
- Так как аргумент в вызове функции есть выражение, при передаче его функции также
- возможно преобразование типа. При отсутствии прототипа функции аргументы типа
- \verb|char| и \verb|short| переводятся в \verb|int|, а \verb|float| -- в
- \verb|double|. Вот почему мы объявляли аргументы типа \verb|int| или
- \verb|double| даже тогда, когда в вызове функции использовали аргументы типа
- \verb|char| или \verb|float|.
- \index{преобразование!оператором приведения}%
- И наконец, для любого выражения можно явно указать преобразование его типа,
- используя унарный оператор, называемый \emph{приведением}. Конструкция вида
- \begin{ShortCodeParWithCC}{\\\[\]}
- (\textit[имя-типа]) \textit[выражение]
- \end{ShortCodeParWithCC}
- \noindent приводит \textit{выражение} к указанному в скобках типу по
- перечисленным выше правилам. Смысл операции приведения можно представить себе
- так: \textit{выражение} как бы присваивается некоторой переменной указанного
- типа, и эта переменная используется вместо всей конструкции. Например,
- библиотечная программа \verb|sqrt| рассчитана на аргумент типа \verb|double| и
- выдаёт чепуху, если ей подсунуть что-нибудь другое. (\verb|sqrt| описана в
- \verb|<math.h>|.) Поэтому, если \verb|n| есть целое, мы можем написать
- \begin{ShortCodePar}
- sqrt((double) n)
- \end{ShortCodePar}
- \noindent и перед тем, как значение \verb|n| будет передано функции, оно будет
- переведено в \verb|double|. Заметим, что операция приведения всего лишь
- вырабатывает \emph{значение} \verb|n| указанного типа, но саму переменную
- \verb|n| не затрагивает. Приоритет оператора приведения столь же высок, как и
- любого унарного оператора, что зафиксировано в таблице, показанной в конце этой
- главы.
- \index{прототип функции}%
- \index{функция!прототип}%
- В том случае, когда аргументы описаны в прототипе функции, как тому и следует
- быть, при вызове функции нужное преобразование включается автоматически. Так,
- при наличии прототипа функции \verb|sqrt|:
- \begin{ShortCodePar}
- double sqrt(double);
- \end{ShortCodePar}
- \noindent перед обращением к \verb|sqrt| в присваивании
- \begin{ShortCodePar}
- root2 = sqrt(2);
- \end{ShortCodePar}
- \noindent целое $2$ будет переведено в значение \verb|double| $2.0$
- автоматически без явного указания операции приведения.
- \index{функция!rand@\texttt{rand}}%
- \index{функция!srand@\texttt{srand}}%
- Операцию приведения проиллюстрируем на переносимой версии генератора
- псевдослучайных чисел и функции, осуществляющей начальную <<затравку>>, входящих
- в стандартную библиотеку.
- \begin{LongCodePar}
- unsigned long int next = 1;
- /* rand: получает псевдослучайное целое 0..32767 */
- int rand(void)
- {
- next = next * 1103515245 + 12345;
- return (unsigned int)(next/65536) % 32768;
- }
- /* srand: устанавливает "затравку" для rand() */
- void srand(unsigned int seed)
- {
- next = seed;
- }
- \end{LongCodePar}
- \index{тип!правила преобразования|)}%
- \paragraph{Упражнение 2.3.} Напишите функцию \verb|htoi(s)|, которая преобразует
- последовательность шестнадцатиричных цифр, начинающуюся с \verb|0x| или
- \verb|0X| в соответствующее целое. Шестнадцатиричными цифрами являются литеры
- \verb|0...9|, \verb|a...f|, \verb|A...F|.
- \section{Инкрементные и декрементные операторы}
- \index{оператор!инкрементации \texttt{++}}%
- \index{оператор!декрементации \texttt{\textminus\textminus}}%
- В Си есть два необычных оператора, предназначенных для увеличения и уменьшения
- переменных. Инкрементный оператор \verb|++| добавляет $1$ к своему операнду, а
- декрементный оператор \verb|--| вычитает $1$. Мы уже неоднократно использовали
- \verb|++| для наращивания значения переменных, как, например, в
- \begin{ShortCodePar}
- if (c == '\n')
- ++nl;
- \end{ShortCodePar}
- \index{операторы!постфиксные \texttt{++} и \texttt{\textminus\textminus}}%
- \index{операторы!префиксные \texttt{++} и \texttt{\textminus\textminus}}%
- Необычность \verb|++| и \verb|--| в том, что их можно использовать и как
- префиксные операторы (помещая перед переменной, например, \verb|++n|), и как
- постфиксные операторы (помещая после переменной: \verb|n++|) . В обоих случаях
- значение \verb|n| увеличивается на $1$. Но выражение \verb|++n| увеличивает
- \verb|n| \emph{до} того, как его значение будет использовано, а \verb|n++| --
- \emph{после} того. Предположим, что \verb|n| содержит $5$, тогда
- \begin{ShortCodePar}
- x = n++;
- \end{ShortCodePar}
- \noindent установит в \verb|x| значение $5$, а
- \begin{ShortCodePar}
- x = ++n;
- \end{ShortCodePar}
- \noindent установит в \verb|x| значение $6$. И в том и другом случае значение
- \verb|n| станет равным $6$. Инкрементные и декрементные операторы можно
- применять только к переменным. Например, запись \verb|(i+j)++| не верна. В
- контексте, где требуется только увеличить (или уменьшить) значение переменной,
- как в
- \begin{ShortCodePar}
- if (c == '\n')
- nl++;
- \end{ShortCodePar}
- \noindent безразлично, какой выбрать оператор -- префиксный или постфиксный. Но
- существуют ситуации, когда требуется оператор вполне определённого типа.
- \index{функция!squeeze@\texttt{squeeze}}%
- Например, рассмотрим функцию \verb|squeeze(s,c)|, которая удаляет из стринга
- \verb|s| все литеры, совпадающие с \verb|c|:
- \begin{LongCodePar}
- /* squeeze: удаляет все c из s */
- void squeeze(char s[], int c)
- {
- int i, j;
- for (i = j = 0; s[i] != '\0'; i++)
- if (s[i] != c)
- s[j++] = s[i];
- s[i] = '\0';
- }
- \end{LongCodePar}
- %
- % в оригинале опечатка squeese
- %
- \noindent Каждый раз, когда встречается литера, отличная от \verb|c|, она
- копируется в текущую \verb|j|-ю позицию, и только после этого переменная
- \verb|j| продвигается на 1, подготавливаясь таким образом к приёму следующей
- литеры. Это в точности совпадает со следующими действиями:
- \begin{ShortCodePar}
- if (s[i] != c) {
- s[j] = s[i];
- j++;
- }
- \end{ShortCodePar}
- Другой пример -- функция \verb|getline|, которая нам известна по
- гл.~\ref{chapt:tutorial_intro}. Приведённую там запись
- \begin{ShortCodePar}
- if (c == '\n') {
- s[i] = c;
- ++i;
- }
- \end{ShortCodePar}
- \noindent можно переписать более компактно:
- \begin{ShortCodePar}
- if (c == '\n')
- s[i++] = c;
- \end{ShortCodePar}
- \index{функция!strcat@\texttt{strcat}}%
- В качестве третьего примера рассмотрим стандартную функцию \verb|strcat(s,t)|,
- которая стринг \verb|t| помещает в конец стринга \verb|s|. Предполагается, что в
- \verb|s| достаточно пространства, чтобы в нём разместить суммарный стринг. Мы
- написали \verb|strcat| так, что она не возвращает никакого результата. На самом
- деле библиотечная \verb|strcat| возвращает ссылку на результирующий стринг.
- \begin{ShortCodePar}
- /* strcat: помещает t в конец s; s достаточно большой */
- void strcat(char s[], char t[])
- {
- int i,j;
- i = j = 0;
- while (s[i] != '\0') /* находим конец s */
- i++;
- while ((s[i++] = t[j++]) != '\0' /* копируем t */
- ;
- }
- \end{ShortCodePar}
- \noindent При копировании очередной литеры из \verb|t| в \verb|s| постфиксный
- оператор \verb|++| применяется и к \verb|i|, и к \verb|j|, чтобы на каждом шаге
- цикла переменные \verb|i| и \verb|j| правильно отслеживали позиции перемещаемой
- литеры.
- \paragraph{Упражнение 2.4.} Напишите версию функции \verb|squeeze(s1,s2)|,
- которая удаляет из \verb|s1| все литеры, встречающиеся в \emph{стринге}
- \verb|s2|.
- %
- % в оригинале опечатка squeese
- %
- \paragraph{Упражнение 2.5.} Напишите функцию \verb|any(s1,s2)|, которая
- возвращает либо ту позицию в \verb|s1|, где стоит первая литера, совпавшая с
- любой из литер в \verb|s2|, либо $-1$ (если ни одна литера \verb|s1| не
- совпадает с литерами из \verb|s2|). (Стандартная библиотечная функция
- \verb|strpbrk| делает то же самое, но выдаёт указатель на литеру, а не номер её
- позиции.)
- \section{Побитовые операторы}
- \index{биты, образцы манипулирования}%
- \index{оператор!дополнения побитового до единиц \texttt{\textasciitilde}}%
- \index{оператор!левого сдвига \texttt{\textless}\texttt{\textless}}%
- \index{оператор!правого сдвига \texttt{\textgreater}\texttt{\textgreater}}%
- \index{операторы!побитовые}%
- \index{операторы!сдвига}%
- В Си имеются шесть операторов для манипулирования с битами. Их можно применять
- только к целочисленным операндам, т.е. к операндам типов \verb|char|,
- \verb|short|, \verb|int|, \verb|long|, знаковым и беззнаковым.
- \begin{LongRtAlTab2cols}{\codeIndent}{0pt}{\Verb|<<<<<<<<<<|}
- \verb|&| &побитовое И\\
- \verb!|! &побитовое ИЛИ\\
- \verb|^| &побитовое исключающее ИЛИ\\
- \verb|<<| &сдвиг влево\\
- \verb|>>| &сдвиг вправо\\
- \verb|~| &побитовое отрицание (унарный)
- \end{LongRtAlTab2cols}
- Оператор \verb|&| (побитовое И) часто используется для обнуления некоторой
- группы разрядов. Например,
- \begin{ShortCodePar}
- n = n & 0177;
- \end{ShortCodePar}
- \noindent очищает в \verb|n| все разряды, кроме младших семи.
- Оператор \verb!|! (побитовое ИЛИ) применяют для установки разрядов; так,
- \begin{ShortCodePar}
- x = x | SET_ON;
- \end{ShortCodePar}
- \noindent устанавливает единицы в тех разрядах \verb|x|, которым соответствуют
- единицы в \verb|SET_ON|.
- Оператор \verb|^| (побитовое исключающее ИЛИ) в каждом разряде установит $1$,
- если соответствующие разряды операндов имеют различные значения, и $0$, когда
- они совпадают.
- \index{оператор!логического И \texttt{\&\&}}%
- \index{оператор!логического ИЛИ \texttt{"|"|}}%
- Поразрядные операторы \verb|&| и \verb!|! следует отличать от логических
- операторов \verb|&&| и \verb!||!, которые при
- \index{вычисление, порядок}%
- \index{порядок!вычислений}%
- вычислении слева направо дают значение истинности. Например, если \verb|x| есть
- $1$, а \verb|y| равно $2$, то \verb|x & y| даст нуль, а \verb|x && y| --
- единицу.
- Операторы \verb|<<| и \verb|>>| выполняют сдвиг, влево или вправо, своего левого
- операнда на число битовых позиций, задаваемое правым операндом, которое должно
- быть положительным. Так, \verb|x << 2| сдвигает значение \verb|x| влево на 2
- позиции, заполняя освобождающиеся биты нулями, что эквивалентно умножению
- \verb|x| на $4$. Сдвиг вправо беззнаковой величины всегда сопровождается
- заполнением освобождающихся разрядов нулями. Сдвиг вправо знаковой величины на
- одних машинах происходит с размножением знака (<<арифметический сдвиг>>), на
- других -- с заполнением освобождающихся разрядов нулями (<<логический сдвиг>>).
- Унарный оператор \verb|~| производит дополнение целого до единиц по всем
- разрядам, т. е. превращает единичные биты в нулевые и наоборот. Например,
- \begin{ShortCodePar}
- x = x & ~077
- \end{ShortCodePar}
- \noindent обнуляет в \verb|x| последние 6 разрядов. Заметим, что запись
- \verb|x & ~077| не зависит от длины слова, и, следовательно, она лучше, чем
- \verb|x & 0177700|, поскольку последняя подразумевает, что \verb|x| занимает 16
- бит. Независимая от машины форма записи \verb|~077| не потребует дополнительных
- затрат при счёте, так как \verb|~077| -- константное выражение, которое будет
- вычислено во время компиляции.
- \index{биты, образцы манипулирования}%
- \index{функция!getbits@\texttt{getbits}}%
- Для иллюстрации некоторых побитовых операций рассмотрим функцию
- \verb|getbits(x,p,n)|, которая формирует поле в \verb|n| бит, вырезанное из
- \verb|x|, начиная с позиции \verb|p|, прижимая его к правому краю.
- Предполагается, что 0-й бит -- крайний правый бит, а \verb|n| и \verb|p| --
- разумные положительные числа. Например, \verb|getbits(x,4,3)| вернёт в качестве
- результата 4, 3 и 2-й биты значения \verb|x|, прижимая их к правому краю. Вот
- эта функция:
- \begin{ShortCodePar}
- /* getbits: получает n бит, начиная с p-й позиции */
- unsigned getbits(unsigned x, int p, int n)
- {
- return (x >> (p+1-n)) & ~(~0 << n);
- }
- \end{ShortCodePar}
- \noindent Выражение \verb|x >> (p+1-n)| сдвигает нужное нам поле к правому краю.
- Константа \verb|~0| состоит только из единиц, и её сдвиг влево на \verb|n| бит
- \verb|(~0 << n)| приведёт к тому, что правый край этой константы займут \verb|n|
- нулевых разрядов. Ещё одна операция побитового отрицания, \verb|~(~0 << n)|,
- позволяет получить справа \verb|n| единиц.
- \paragraph{Упражнение 2.6.} Напишите функцию \verb|setbits(x,p,n,y)|,
- возвращающую значение \verb|x|, в котором \verb|n| бит, начиная с \verb|p|-й
- позиции, заменены на \verb|n| правых разрядов из \verb|y| (остальные биты не
- изменяются).
- \paragraph{Упражнение 2.7.} Напишите функцию \verb|invert(x,p,n)|, возвращающую
- значение \verb|x| с инвертированными \verb|n| битами, начиная с позиции \verb|p|
- (остальные биты не изменяются).
-
- \paragraph{Упражнение 2.8.} Напишите функцию \verb|rightrot(x,n)|, которая
- циклически сдвигает (<<вращает>>) вправо \verb|x| на \verb|n| разрядов.
- \section{Операторы присваивания и выражения}
- \index{выражение!присваивание}%
- \index{операторы!присваивания}%
- \index{оператор!присваивания \texttt{+=}}%
- \index{присваивание!выражение}%
- Выражение типа
- \begin{ShortCodePar}
- i = i + 2
- \end{ShortCodePar}
- \noindent в котором стоящая слева переменная повторяется и справа, можно
- написать в сжатом виде:
- \begin{ShortCodePar}
- i += 2
- \end{ShortCodePar}
- \noindent Оператор \verb|+=| называется \emph{оператором присваивания}.
- \index{оператор!присваивания \texttt{*=}}%
- \index{оператор!присваивания \texttt{\slash=}}%
- \index{оператор!присваивания \texttt{\%=}}%
- \index{оператор!присваивания \texttt{+=}}%
- \index{оператор!присваивания \texttt{\textminus=}}%
- \index{оператор!присваивания \texttt{\textless}\texttt{\textless=}}%
- \index{оператор!присваивания \texttt{\textgreater}\texttt{\textgreater=}}%
- \index{оператор!присваивания \texttt{\&=}}%
- \index{оператор!присваивания \texttt{\textasciicircum=}}%
- \index{оператор!присваивания \texttt{"|=}}%
- Большинству бинарных операторов (аналогичных \verb|+| и имеющих левый и правый
- операнды) соответствуют операторы присваивания \textit{\Verb|op|}\verb|=|, где
- \textit{\Verb|op|} -- один из операторов
- \begin{ShortCodePar}
- + - * / % << >> & ^ |
- \end{ShortCodePar}
- \noindent Если \textit{выр}$_{1}$ и \textit{выр}$_{2}$ -- выражения, то запись
- \begin{ShortCodeParWithCC}{\\\{\}}
- \textit{выр}\textsubscript{1} \textit{op}= \textit{выр}\textsubscript{2}
- \end{ShortCodeParWithCC}
- \noindent эквивалентна записи
- \begin{ShortCodeParWithCC}{\\\{\}}
- \textit{выр}\textsubscript{1} = (\textit{выр}\textsubscript{1}) \textit{op}\
- (\textit{выр}\textsubscript{2})
- \end{ShortCodeParWithCC}
- \noindent с той лишь разницей, что \textit{выр}$_{1}$ вычисляется только один
- раз. Обратите внимание на скобки вокруг \textit{выр}$_{2}$: запись
- \begin{ShortCodePar}
- x *= y + 1
- \end{ShortCodePar}
- \noindent эквивалентна записи
- \begin{ShortCodePar}
- x = x * (y + 1)
- \end{ShortCodePar}
- \noindent но не
- \begin{ShortCodePar}
- x = x * y + 1
- \end{ShortCodePar}
- \index{функция!bitcount@\texttt{bitcount}}%
- В качестве примера приведём функцию \verb|bitcount|, подсчитывающую число
- единичных битов в своём аргументе целого типа.
- \begin{LongCodePar}
- /* bitcount: подсчёт 1 в x */
- int bitcount(unsigned x)
- {
- int b;
- for (b = 0; x != 0; x >>= 1)
- if (x & 01)
- b++;
- return b;
- }
- \end{LongCodePar}
- \noindent%
- \index{unsigned@\texttt{unsigned}!тип}%
- Независимо от машины, на которой будет работать эта программа, описание
- аргумента \verb|x| как \verb|unsigned| гарантирует, что при правом сдвиге
- освобождающиеся биты будут заполняться нулями, а не знаковым битом.
- \index{программа!читаемость}%
- Помимо краткости операторы присваивания обладают тем преимуществом, что они
- более соответствуют тому, как человек мыслит. Мы говорим <<прибавить $2$ к
- \verb|i|>> или <<увеличить \verb|i| на $2$>>, а не <<взять \verb|i|, добавить
- $2$ и затем вернуть результат в \verb|i|>>, так что выражение \verb|i += 2|
- лучше, чем \verb|i = i + 2|. Кроме того, в сложных выражениях вроде
- \begin{ShortCodePar}
- yyval[yypv[p3+p4] + yypv[p1+p2]] += 2
- \end{ShortCodePar}
- \noindent благодаря оператору присваивания запись становится более лёгкой для
- понимания, так как читателю при такой записи не потребуется старательно
- сравнивать два длинных выражения или выяснять, почему они не совпадают.
- \index{эффективность}%
- Следует иметь в виду и то, что оператор присваивания может помочь компилятору
- сгенерировать более эффективный код.
- \index{инструкции!присваивания вложенные}%
- \index{оператор!присваивания \texttt{=}}%
- \index{присваивание!инструкция вложенная}%
- Мы уже видели, что присваивание вырабатывает значение и может применяться внутри
- выражения; во многих рядовых программах мы видим
- \begin{ShortCodePar}
- while ((c = getchar()) != EOF)
- ...
- \end{ShortCodePar}
- \noindent В выражениях встречаются и другие операторы присваивания (\verb|+=|,
- \verb|-=| и т.д.), хотя и реже.
- Типом и значением любого выражения присваивания являются тип и значение его
- левого операнда после завершения присваивания.
- \paragraph{Упражнение 2.9.} Применительно к числам, в представлении которых
- использован дополнительный код, выражение \verb|x &= (x-1)| уничтожает самую
- правую 1 в \verb|x|. Объясните, почему. Используйте это наблюдение при написании
- более быстрого варианта функции \verb|bitcount|.
- \section{Условные выражения}
- \index{условное выражение}%
- Инструкции
- \begin{ShortCodePar}
- if (a > b)
- z = a;
- else
- z = b;
- \end{ShortCodePar}
- \noindent пересылают в \verb|z| максимальное из двух значений, \verb|a| и
- \verb|b|. \emph{Условное выражение}, написанное с помощью тернарного оператора
- <<\verb|?:|>>, представляет собой другой способ записи этой и подобных ей
- конструкций. В выражении
- \begin{ShortCodeParWithCC}{\\\{\}}
- \textit{выр}\textsubscript{1} ? \textit{выр}\textsubscript{2} :\
- \textit{выр}\textsubscript{3}
- \end{ShortCodeParWithCC}
- \noindent первым вычисляется выражение \textit{выр}$_{1}$. Если его значение не
- нуль (истина), то вычисляется выражение \textit{выр}$_{2}$, и значение этого
- выражения становится значением всего условного выражения. В противном случае
- вычисляется выражение \textit{выр}$_{3}$, и его значение становится значением
- условного выражения. Следует отметить, что из выражений \textit{выр}$_{2}$ и
- \textit{выр}$_{3}$ вычисляется только одно из них. Таким образом, чтобы
- установить в \verb|z| наибольшее из \verb|a| и \verb|b|, можно написать
- \begin{ShortCodePar}
- z = (a > b) ? a : b; /* z = max(a, b) */
- \end{ShortCodePar}
- Следует заметить, что условное выражение и в самом деле является выражением, и
- его можно использовать в любом месте, где допускается выражение. Если
- \textit{выр}$_{2}$ и \textit{выр}$_{3}$ принадлежат разным типам, то тип
- результата определяется правилами преобразования, о которых шла речь в этой
- главе ранее. Например, если \verb|f| имеет тип \verb|float|, а \verb|n| -- тип
- \verb|int|, то типом выражения
- \begin{ShortCodePar}
- (n > 0) ? f : n
- \end{ShortCodePar}
- \noindent будет \verb|float|, вне зависимости от того, положительно значение
- \verb|n| или нет.
- Заключать в скобки первое выражение в условном выражении не обязательно, так как
- приоритет \verb|?:| очень низкий (более низкий приоритет имеет только
- присваивание), однако мы рекомендуем всегда это делать, поскольку благодаря
- обрамляющим скобкам условие в выражении лучше воспринимается.
- Условное выражение часто позволяет сократить программу. В качестве примера
- приведём цикл, обеспечивающий печать \verb|n| элементов массива по 10 на каждой
- строке с одним пробелом между колонками; каждая строка цикла, включая последнюю,
- заканчивается литерой новая-строка:
- \begin{ShortCodePar}
- for (i = 0; i < n; i++)
- printf("%6c%d", a[i], (i%10==9 || i==n-1) ? '\n' : ' ');
- \end{ShortCodePar}
- \noindent Литера новая-строка посылается после каждого десятого и после
- \verb|n|-го элемента. За всеми другими элементами следует пробел. Эта программа
- выглядит довольно замысловато, зато она более компактна, чем эквивалентная
- программа с использованием \verb|if-else|. Вот ещё один пример:
- {\setlength{\codeIndent}{\codeIndentBk-1em}
- \begin{ShortCodePar}
- printf("Вы имеете %d элемент%s.\n", n, (n%10==1 && n!=11) ? "" :
- ((n<10 || n>20) && n%10>=2 && n%10<=4) ? "а" : "ов");
- \end{ShortCodePar}
- \setlength{\codeIndent}{\codeIndentBk}}
- \paragraph{Упражнение 2.10.} Напишите функцию \verb|lower|, которая переводит
- большие буквы в малые, используя условное выражение (вместо \verb|if-else|).
- \section{Приоритет и порядок вычислений}
- \index{выражение!порядок вычисления}%
- \index{вычисление, порядок}%
- \index{оператор!приоритет}%
- \index{операторы!ассоциативные}%
- \index{порядок!вычислений}%
- \index{приоритеты операторов}%
- В табл.~\ref{tab:precedence} показаны приоритеты и порядок вычислений всех
- операторов, включая и те, которые мы ещё не рассматривали. Операторы,
- перечисленные на одной строке, имеют одинаковый приоритет; строки упорядочены по
- убыванию приоритетов; так, например, \verb|*|, \verb|/| и \verb|%| имеют
- одинаковый приоритет, который выше, чем приоритет бинарных \verb|+| и \verb|-|.
- <<Оператор>> \verb|()| обозначает вызов функции. Операторы \verb|->| и \verb|.|
- (точка) обеспечивают доступ к элементам структур; о них пойдёт речь в
- гл.~\ref{chapt:structures}, там же будет рассмотрен и оператор \verb|sizeof|
- (размер объекта). Операторы \verb|*| (адресация по указателю) и \verb|&|
- (получение адреса объекта) обсуждаются в гл.~\ref{chapt:pointers_and_arrays}.
- Оператор <<запятая>> будет рассмотрен в гл.~\ref{chapt:control_flow}.
- Заметим, что приоритеты побитовых операторов \verb|&|, \verb|^| и \verb!|! ниже,
- чем приоритет \verb|==| и \verb|!=|, из-за чего в побитовых проверках типа
- \begin{ShortCodePar}
- if ((x & MASK) == 0) ...
- \end{ShortCodePar}
- \noindent чтобы получить правильный результат, приходится использовать скобки.
- Си подобно многим языкам не фиксирует порядок вычисления операндов оператора (за
- исключением \verb|&&|, \verb!||!, \verb|?:| и <<\verb|,|>>). Например, в
- инструкции вида
- \begin{ShortCodePar}
- x = f() + g();
- \end{ShortCodePar}
- \noindent%
- \index{побочный эффект}%
- \verb|f| может быть вычислена раньше \verb|g| или наоборот. Из этого
- следует, что если одна из функций изменяет значение переменной, от которой
- зависит другая функция, то помещаемый в \verb|x| результат может зависеть от
- порядка вычислений. Чтобы обеспечить нужную последовательность вычислений,
- промежуточные результаты можно запоминать во временных переменных.
- Порядок вычисления аргументов функции также не определён, поэтому на разных
- компиляторах
- \begin{ShortCodePar}
- printf("%d %d\n", ++n, power(2, n)); /* НЕВЕРНО */
- \end{ShortCodePar}
- \begin{table}[t]
- \centering
- \captionsetup{name=ТАБЛИЦА,labelsep=period,justification=centering}
- \renewcommand\thetable{2.1}
- \caption{ПРИОРИТЕТЫ И ПОРЯДОК ВЫЧИСЛЕНИЙ ОПЕРАТОРОВ}
- \label{tab:precedence}
- \index{вычисление, порядок}%
- \index{порядок!вычислений}%
- \index{приоритеты операторов}%
- \index{таблица!операторов}%
- {%
- \SetLenVarWithWidth{\colAlen}{\Verb!= += -= *= /= \%= \&= \^= |= <<= >>=!}%
- \SetLenVarWithWidth{\colBlen}{ВЫПОЛНЯЮТСЯ}%
- \begin{tabular*}{\colAlen+\colBlen+4\tabcolsep}{p{\colAlen}|p{\colBlen}}
- \hline\hline
- \multicolumn{1}{c|}{\rule{0pt}{3ex}ОПЕРАТОРЫ\rule[-1.5ex]{0pt}{0pt}} &
- \multicolumn{1}{|c}{ВЫПОЛНЯЮТСЯ} \\
- \hline
- \rule{0pt}{3ex}\verb|() [] -> .| &\multicolumn{1}{|c}{\Verb|слева направо|}
- \\
- \verb|! ~ ++ -- + - * & (|\textit{\Verb|тип|}\verb|) sizeof|
- &\multicolumn{1}{|c}{\Verb|справа налево|} \\
- \verb|* / %| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|+ -| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|<< >>| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|< <= > >=| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|== !=| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|&| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|^| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb+|+ &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|&&| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb+||+ &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \verb|?:| &\multicolumn{1}{|c}{\Verb|справа налево|} \\
- \verb!= += -= *= /= %= &= ^= |= <<= >>=!
- &\multicolumn{1}{|c}{\Verb|справа налево|} \\
- \rule[-1.5ex]{0pt}{0pt}\verb|,| &\multicolumn{1}{|c}{\Verb|слева направо|} \\
- \hline
- \multicolumn{2}{c}{\small \rule{0pt}{3ex} Унарные операторы \Verb|+|, \Verb|-| и
- \Verb|*| имеют более высокий}\\
- \multicolumn{2}{c}{\small приоритет, чем те же операторы в бинарном варианте}
- \end{tabular*}}
- \end{table}
- \noindent может давать несовпадающие результаты. Результат вызова функции
- зависит от того, когда компилятор сгенерирует команды увеличения \verb|n| до
- или после обращения к \verb|power|. Чтобы обезопасить себя от возможного
- побочного эффекта, достаточно написать
- \begin{ShortCodePar}
- ++n;
- printf("%d %d\n", n, power(2, n));
- \end{ShortCodePar}
- \index{побочный эффект}%
- Обращения к функциям, вложенные присваивания, инкрементные и декрементные
- операторы дают <<побочный эффект>>, проявляющийся в том, что при вычислении
- выражения значения некоторых переменных изменяются. В любом выражении с побочным
- эффектом может быть скрыта трудно просматриваемая зависимость результата
- выражения от порядка изменения значений переменных, входящих в выражение. В
- такой, например, типично неприятной ситуации --
- \begin{ShortCodePar}
- a[i] = i++;
- \end{ShortCodePar}
- \noindent возникает вопрос: массив \verb|a| индексируется старым или изменённым
- значением \verb|i|? Компиляторы могут по-разному генерировать программу, что
- проявится в интерпретации данной записи. Стандарт сознательно устроен так, что
- большинство подобных вопросов оставлено на усмотрение компиляторов, так как
- лучший порядок вычислений определяется архитектурой машины. Стандартом только
- гарантируется, что все побочные эффекты при вычислении аргументов проявятся
- перед входом в функцию. Правда, в примере с \verb|printf| это нам не поможет.
- Мораль такова: писать программы, которые зависят от порядка вычислений, --
- плохая практика, какой бы язык вы ни использовали. Естественно, надо знать, чего
- следует избегать, но если вы не знаете, \emph{как} образуются побочные эффекты
- на вашей машине, то лучше и не рассчитывать на особенности реализации.
|