chapter5.tex 124 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208
  1. \chapter{Указатели и массивы}
  2. \label{chapt:pointers_and_arrays}
  3. \index{указатели!и индексирование}%
  4. Указатель -- это переменная, содержащая адрес переменной. Указатели широко
  5. применяются в Си -- отчасти потому, что в некоторых случаях без них просто не
  6. обойтись, а отчасти потому, что программы с ними обычно короче и эффективнее.
  7. Указатели и массивы тесно связаны друг с другом; в этой главе мы рассмотрим эту
  8. зависимость и покажем, как ею пользоваться.
  9. Наряду с \verb|goto| указатели когда-то были объявлены лучшим средством для
  10. написания малопонятных программ. Так оно и есть, если ими пользоваться бездумно.
  11. Ведь очень легко получить указатель, ссылающийся на что-нибудь совсем
  12. нежелательное. При соблюдении же определённой дисциплины с помощью указателей
  13. можно достичь ясности и простоты. Мы попытаемся убедить вас в этом.
  14. Изменения, внесённые стандартом ANSI, связаны в основном с формулированием
  15. точных правил работы с указателями. Он узаконил накопленный положительный опыт
  16. программистов и удачные нововведения разработчиков компиляторов.
  17. \index{указатель!void *@\texttt{void *}}%
  18. \index{void *, указатель@\texttt{void *}, указатель}%
  19. Кроме того, взамен \verb|char *| в качестве типа обобщённого указателя
  20. предлагается тип \verb|void *| (указатель на \verb|void|).
  21. \section{Указатели и адреса}
  22. \index{переменная!адрес}%
  23. Начнём с того, что рассмотрим упрощённую схему организации памяти. Память
  24. типичной машины представляет собой массив последовательно пронумерованных и
  25. проадресованных ячеек, с которыми можно работать по отдельности или связными
  26. кусками. Применительно к любой машине верны следующие утверждения: один байт
  27. может хранить значение типа \verb|char|, двухбайтовые ячейки могут
  28. рассматриваться как целое типа \verb|short|, а четырёхбайтовые -- как целые
  29. типа \verb|long|. Указатель -- это группа ячеек (как правило, две или четыре),
  30. в которых может храниться адрес. Так, если \verb|c| имеет тип \verb|char|, а
  31. \verb|p| -- указатель, ссылающийся на \verb|c|, то ситуация выглядит следующим
  32. образом:
  33. \begin{figure}[H]
  34. \center{\includegraphics[width=0.7831461\linewidth]{chapt5_sec1_img0.eps}}
  35. \end{figure}
  36. \noindent Унарный оператор \verb|&| выдаёт адрес объекта, так что инструкция
  37. \begin{ShortCodePar}
  38. p = &c;
  39. \end{ShortCodePar}
  40. \noindent присваивает адрес ячейки \verb|c| переменной \verb|p| (говорят, что
  41. \verb|p| указывает на \verb|c| или, что то же, \verb|p| ссылается на \verb|c|).
  42. Оператор \verb|&| применяется только к объектам, расположенным в памяти: к
  43. переменным и элементам массивов. Его операндом не может быть ни выражение, ни
  44. константа, ни регистровая переменная.
  45. \index{оператор!косвенности \texttt{*}}%
  46. Унарный оператор \verb|*| есть оператор \emph{раскрытия ссылки}. Применённый к
  47. указателю он выдаёт объект, на который данный указатель ссылается. Предположим,
  48. что \verb|x| и \verb|y| -- целые, а \verb|ip| -- указатель на \verb|int|.
  49. Следующие несколько строк придуманы специально для того, чтобы показать, каким
  50. образом декларируются указатели и используются операторы \verb|&| и \verb|*|.
  51. \begin{ShortCodePar}
  52. int x = 1, y = 2, z[10];
  53. int *ip; /* ip - указатель на int */
  54. ip = &x; /* теперь ip указывает на x */
  55. y = *ip; /* y теперь равен 1 */
  56. *ip = 0; /* x теперь равен 0 */
  57. ip = &z[0]; /* ip теперь указывает на z[0] */
  58. \end{ShortCodePar}
  59. \noindent Декларации \verb|x|, \verb|y| и \verb|z| нам уже знакомы.
  60. \index{декларация!указателя}%
  61. \index{указатель!декларация}%
  62. Декларацию указателя \verb|ip|
  63. \begin{ShortCodePar}
  64. int *ip;
  65. \end{ShortCodePar}
  66. мы стремились сделать мнемоничной -- она гласит: <<выражение \verb|*ip| есть
  67. нечто типа \verb|int|>>. Синтаксис декларации переменной <<подстраивается>> под
  68. синтаксис выражений, в которых эта переменная может встретиться. Указанный
  69. принцип применим и в отношении описаний функций. Например, запись
  70. \begin{ShortCodePar}
  71. double *dp, atof(char *);
  72. \end{ShortCodePar}
  73. \noindent означает, что выражение \verb|*dp| и \verb|atof(s)| имеют тип
  74. \verb|double|‚ а аргумент функции \verb|atof| есть указатель на \verb|char|.
  75. Вы, наверное, заметили, что указателю разрешено ссылаться только на объекты
  76. заданного типа. (Существует одно исключение: <<указатель на \verb|void|>> может
  77. ссылаться на объекты любого типа, но к такому указателю нельзя применять
  78. оператор раскрытия ссылки. Мы вернёмся к этому в
  79. разд.~\ref{sec:function_pointers}.)
  80. Если \verb|ip| ссылается на \verb|x| целого типа, то \verb|*ip| можно
  81. использовать в любом месте, где допустимо применение \verb|x|; например,
  82. \begin{ShortCodePar}
  83. *ip = *ip + 10;
  84. \end{ShortCodePar}
  85. \noindent увеличивает \verb|*ip| на $10$.
  86. \index{вычисление, порядок}%
  87. \index{оператор!приоритет}%
  88. \index{порядок!вычислений}%
  89. \index{приоритеты операторов}%
  90. Унарные операторы \verb|*| и \verb|&| имеют более высокий приоритет, чем
  91. арифметические операторы, так что присваивание
  92. \begin{ShortCodePar}
  93. y = *ip + 1
  94. \end{ShortCodePar}
  95. \noindent берет то, на что указывает \verb|ip|, и добавляет к нему $1$, а
  96. результат присваивает переменной \verb|y|. Аналогично
  97. \begin{ShortCodePar}
  98. *ip += 1
  99. \end{ShortCodePar}
  100. \noindent увеличивает на единицу то, на что ссылается \verb|ip|; те же действия
  101. выполняют
  102. \begin{ShortCodePar}
  103. ++*ip
  104. \end{ShortCodePar}
  105. \noindent и
  106. \begin{ShortCodePar}
  107. (*ip)++
  108. \end{ShortCodePar}
  109. \noindent%
  110. \index{указатели!арифметика с}%
  111. В последней записи скобки необходимы, поскольку, если их не будет,
  112. увеличится значение самого указателя, а не то, на что он ссылается. Это
  113. обусловлено тем, что унарные операторы \verb|*| и \verb|++| имеют одинаковые
  114. приоритет и порядок выполнения -- справа налево.
  115. И наконец, так как указатели сами являются переменными, в тексте они могут
  116. встречаться и без оператора раскрытия ссылки. Например, если \verb|iq| есть
  117. другой указатель на \verb|int|, то
  118. \begin{ShortCodePar}
  119. iq = ip
  120. \end{ShortCodePar}
  121. \noindent копирует содержимое \verb|ip| в \verb|iq|, чтобы \verb|ip| и \verb|iq|
  122. ссылались на один и тот же объект.
  123. \section{Указатели и аргументы функций}
  124. \index{вызов!по значению}%
  125. Поскольку функции в Си в качестве своих аргументов получают значения параметров,
  126. прямой возможности, находясь в вызванной функции, изменить переменную вызывающей
  127. функции нет. В программе сортировки нам понадобилась функция \verb|swap|,
  128. переставляющая местами два неупорядоченных элемента. Однако недостаточно
  129. написать
  130. \begin{ShortCodePar}
  131. swap(a, b);
  132. \end{ShortCodePar}
  133. \noindent где функция \verb|swap| определена следующим образом:
  134. \begin{LongCodePar}
  135. void swap(int x, int y) /* НЕВЕРНО */
  136. {
  137. int temp;
  138. temp = x;
  139. x = y;
  140. y = temp;
  141. }
  142. \end{LongCodePar}
  143. \noindent Поскольку \verb|swap| получает лишь \emph{копии} значений переменных
  144. \verb|a| и \verb|b|, она не может повлиять на переменные \verb|a| и \verb|b| той
  145. программы, которая к ней обратилась.
  146. \index{вызов!по ссылке}%
  147. \index{указатель!аргумент}%
  148. Чтобы получить желаемый эффект, надо вызывающей программе передать
  149. \emph{указатели} на те значения, которые должны быть изменены:
  150. \begin{ShortCodePar}
  151. swap(&a, &b);
  152. \end{ShortCodePar}
  153. \noindent Так как оператор \verb|&| получает адрес переменной, \verb|&a| есть
  154. указатель на \verb|a|.
  155. \index{функция!swap@\texttt{swap}}%
  156. В самой функции \verb|swap| параметры должны быть описаны как указатели, при
  157. этом доступ к значениям параметров будет осуществляться через них косвенно.
  158. \begin{LongCodePar}
  159. void swap(int *px, int *py) /* перестановка *px и *py */
  160. {
  161. int temp;
  162. temp = *px;
  163. *px = *py;
  164. *py = temp;
  165. }
  166. \end{LongCodePar}
  167. \noindent Графически это можно изобразить следующим образом:
  168. \begin{figure}[H]
  169. \center{\includegraphics[width=0.3389831\linewidth]{chapt5_sec2_img0.eps}}
  170. \end{figure}
  171. Аргументы-указатели позволяют функции осуществлять доступ к объектам вызвавшей
  172. её программы и дают ей возможность изменить эти объекты.
  173. \index{функция!getint@\texttt{getint}}%
  174. Рассмотрим, например, функцию \verb|getint|, которая осуществляет ввод в
  175. свободном формате одного целого числа и его перевод из текстового представления
  176. в значение типа \verb|int|. Функция \verb|getint| должна возвращать значение
  177. полученного числа или сигнализировать значением \verb|EOF| о конце файла, если
  178. входной поток исчерпан. Эти значения должны возвращаться по разным каналам, так
  179. как нельзя рассчитывать на то, что полученное в результате перевода число
  180. никогда не совпадёт с \verb|EOF|.
  181. Одно из решений состоит в том, чтобы \verb|getint| выдавала характеристику
  182. состояния файла (исчерпан или не исчерпан) в качестве результата, а значение
  183. самого числа помещала согласно указателю, переданному ей в виде аргумента.
  184. Похожая схема действует в программе \verb|scanf|, которую мы рассмотрим в
  185. разд.~\ref{sec:formatted-input}.
  186. Показанный ниже цикл заполняет некоторый массив целыми числами, полученными с
  187. помощью \verb|getint|.
  188. \begin{ShortCodePar}
  189. int n, array[SIZE], getint(int *);
  190. for (n = 0; n < SIZE && getint(&array[n]) != EOF; n++)
  191. ;
  192. \end{ShortCodePar}
  193. \noindent Результат каждого очередного обращения к \verb|getint| посылается в
  194. \verb|array[n]|, и \verb|n| увеличивается на единицу. Заметим, что существенным
  195. здесь является то, что функции \verb|getint| передаётся адрес элемента
  196. \verb|array[n]|. Если этого не сделать, у \verb|getint| не будет способа вернуть
  197. в вызывающую программу переведённое целое число.
  198. В предлагаемом нами варианте функция \verb|getint| выдаёт \verb|EOF| по концу
  199. файла; нуль, если следующие вводимые литеры не представляют собою числа; и
  200. положительное значение, если введённые литеры есть правильное число.
  201. \begin{LongCodePar}
  202. #include <ctype.h>
  203. int getch(void);
  204. void ungetch(int);
  205. /* getint: читает следующее целое из ввода в *pn */
  206. int getint(int *pn)
  207. {
  208. int c, sign;
  209. while (isspace(c = getch()))
  210. ; /* пропуск пробельных литер */
  211. if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
  212. ungetch(c); /* не число */
  213. return 0;
  214. }
  215. sign = (c == '-') ? -1 : 1;
  216. if (c == '+' || c == '-')
  217. c = getch();
  218. for (*pn = 0; isdigit(c); c = getch())
  219. *pn = 10 * *pn + (c - '0');
  220. *pn *= sign;
  221. if (c != EOF)
  222. ungetch(c);
  223. return c;
  224. }
  225. \end{LongCodePar}
  226. %
  227. % в оригинале слева в нижнем колонтитуле надпись ``4. Заказ № 13''
  228. %
  229. \noindent Везде в \verb|getint| под комбинацией \verb|*pn| подразумевается
  230. обычная переменная типа \verb|int|. Функция \verb|ungetch| вместе с \verb|getch|
  231. (разд.~\ref{sec:external_variables}) включена в программу, чтобы обеспечить
  232. возможность отослать назад лишнюю прочитанную литеру.
  233. %
  234. % исправлена опечатка. В оригинале ``влючена в программу''
  235. %
  236. \paragraph{Упражнение 5.1.} Функция \verb|getint| написана так, что знаки
  237. \verb|-| или \verb|+|, за которыми не следует цифра, она понимает как
  238. <<правильное>> представление нуля. Скорректируйте программу таким образом, чтобы
  239. она в подобных случаях возвращала прочитанный знак назад во ввод.
  240. \paragraph{Упражнение 5.2.} Напишите функцию \verb|getfloat| -- аналог
  241. \verb|getint| для чисел с плавающей точкой. Какой тип будет иметь результирующее
  242. значение, выдаваемое функцией \verb|getfloat|?
  243. \section{Указатели и массивы}
  244. \index{индексирование массива!и указатели|(}%
  245. \index{массив!а не указатель|(}%
  246. \index{указатель!а не массив|(}%
  247. В Си существует связь между указателями и массивами, и связь эта настолько
  248. тесная, что эти средства лучше рассматривать вместе.
  249. \index{индексирование массива}%
  250. \index{массив!индексирование}%
  251. Любой доступ к элементу массива, осуществляемый операцией индексирования, может
  252. быть выполнен при помощи указателя. Вариант с указателями в общем случае
  253. работает быстрее, но разобраться в нём, особенно непосвящённому, довольно
  254. трудно.
  255. Декларация
  256. \begin{ShortCodePar}
  257. int a[10];
  258. \end{ShortCodePar}
  259. \noindent определяет массив \verb|a| размера $10$, т.е. блок из $10$
  260. последовательных объектов с именами \verb|a[0]|‚ \verb|a[1]|‚ \ldots ‚
  261. \verb|a[9]|.
  262. \begin{figure}[H]
  263. \center{\includegraphics[width=0.7865169\linewidth]{chapt5_sec3_img0.eps}}
  264. \end{figure}
  265. \noindent Запись \verb|a[i]| отсылает нас к \verb|i|-му элементу массива. Если
  266. \verb|pa| есть указатель на \verb|int|, т.е. определён как
  267. \begin{ShortCodePar}
  268. int *pa;
  269. \end{ShortCodePar}
  270. \noindent то в результате присваивания
  271. \begin{ShortCodePar}
  272. pa = &a[0];
  273. \end{ShortCodePar}
  274. \noindent \verb|pa| будет указывать на нулевой элемент \verb|a|; иначе говоря,
  275. \verb|pa| будет содержать адрес элемента \verb|a[0]|.
  276. \begin{figure}[H]
  277. \center{\includegraphics[width=0.8651685\linewidth]{chapt5_sec3_img1.eps}}
  278. \end{figure}
  279. \noindent Теперь присваивание
  280. \begin{ShortCodePar}
  281. x = *pa;
  282. \end{ShortCodePar}
  283. \noindent будет копировать содержимое \verb|a[0]| в \verb|x|.
  284. \index{указатели!арифметика с}%
  285. Если \verb|pa| указывает на некоторый элемент массива, то \verb|pa+1| по
  286. определению указывает на следующий элемент, \verb|pa+i| -- на \verb|i|-й
  287. элемент после \verb|pa|, а \verb|pa-i| -- на \verb|i|-й элемент перед
  288. \verb|pa|. Таким образом, если \verb|pa| указывает на \verb|a[0]|, то
  289. \begin{ShortCodePar}
  290. *(pa+1)
  291. \end{ShortCodePar}
  292. \noindent есть содержимое \verb|a[1]|, \verb|pa+i| -- адрес \verb|a[i]|, а
  293. \verb|*(pa+i)| -- содержимое \verb|a[i]|.
  294. \begin{figure}[H]
  295. \center{\includegraphics[width=0.8651685\linewidth]{chapt5_sec3_img2.eps}}
  296. \end{figure}
  297. Сделанные замечания верны безотносительно к типу и размеру элементов массива
  298. \verb|a|. Смысл слов <<добавить $1$ к указателю>>, как и смысл любой арифметики
  299. с указателями, в том, чтобы \verb|pa+1| указывал на следующий объект, а
  300. \verb|pa+i| -- на \verb|i|-й после \verb|pa|.
  301. Между индексированием и арифметикой с указателями существует очень тесная связь.
  302. По определению значение переменной или выражения типа массив есть адрес нулевого
  303. элемента массива. После присваивания
  304. \begin{ShortCodePar}
  305. pa = &a[0];
  306. \end{ShortCodePar}
  307. \noindent \verb|pa| и \verb|a| имеют одно и то же значение. Поскольку имя
  308. массива есть не что иное, как адрес его начального элемента, присваивание
  309. \verb|pa=&a[0];| можно также записать в следующем виде:
  310. \begin{ShortCodePar}
  311. pa = a;
  312. \end{ShortCodePar}
  313. Ещё более удивительно (по крайней мере на первый взгляд) то, что \verb|a[i]|
  314. можно записать как \verb|*(a+i)|.
  315. \index{массив!преобразование имени}%
  316. \index{преобразование!имени массива}%
  317. Встречая запись \verb|a[i]|, компилятор сразу преобразует её в \verb|*(a+i)|;
  318. указанные две формы записи эквивалентны. Из этого следует, что полученные в
  319. результате применения оператора \verb|&| записи \verb|&a[i]| и \verb|a+i| также
  320. будут эквивалентными, т.е. и в том и в другом случае это адрес \verb|i|-го
  321. элемента после \verb|a|. С другой стороны, если \verb|pa| -- указатель, то в
  322. выражениях его можно использовать с индексом, т.е. запись \verb|pa[i]|
  323. эквивалентна записи \verb|*(pa+i)|. Элемент массива одинаково разрешается
  324. изображать и в виде указателя со смещением и в виде имени массива с индексом.
  325. Между именем массива и указателем, выступающим в роли имени массива, существует
  326. одно различие. Указатель -- это переменная, поэтому можно написать
  327. \verb|pa = a| или \verb|pa++|. Но имя массива не является переменной, и записи
  328. типа \verb|a = pa| или \verb|a++| не допускаются.
  329. \index{массив!имя в роли аргумента}%
  330. \index{указатель!аргумент}%
  331. Если имя массива передаётся функции, то последняя получает в качестве аргумента
  332. адрес его начального элемента. Внутри вызываемой функции этот аргумент является
  333. локальной переменной, содержащей адрес.
  334. \index{стринг!длина}%
  335. \index{функция!strlen@\texttt{strlen}}%
  336. Мы можем воспользоваться отмеченным фактом и написать ещё одну версию функции
  337. \verb|strlen|, вычисляющей длину стринга.
  338. \begin{LongCodePar}
  339. /* strlen: возвращает длину стринга */
  340. int strlen(char *s)
  341. {
  342. int n;
  343. for (n = 0; *s != '\0'; s++)
  344. n++;
  345. return n;
  346. }
  347. \end{LongCodePar}
  348. \noindent Так как переменная \verb|s| -- указатель, к нему применима операция
  349. \verb|++|; \verb|s++| никакого влияния на стринг литер функции, которая
  350. обратилась к \verb|strlen|‚ не оказывает. Просто увеличивается на $1$ некоторая
  351. копия указателя, находящаяся в личном пользовании функции \verb|strlen|. Это
  352. значит, что все вызовы типа:
  353. \begin{ShortCodePar}
  354. strlen("Здравствуй, мир"); /* стринговая константа */
  355. strlen(array); /* char array[100]; */
  356. strlen(ptr); /* char *ptr; */
  357. \end{ShortCodePar}
  358. \noindent законны.
  359. \index{декларация!указателя}%
  360. \index{параметр}%
  361. \index{указатель!декларация}%
  362. Записи
  363. \begin{ShortCodePar}
  364. char s[];
  365. \end{ShortCodePar}
  366. \noindent и
  367. \begin{ShortCodePar}
  368. char *s;
  369. \end{ShortCodePar}
  370. \noindent в определении функции в качестве формальных параметров эквивалентны.
  371. Мы отдаём предпочтение последней, поскольку она более явно сообщает, что
  372. \verb|s| есть указатель.
  373. \index{массив!имя в роли аргумента}%
  374. \index{указатель!аргумент}%
  375. Если функции в качестве аргумента передаётся имя массива, то она может
  376. рассматривать его так, как ей удобно -- либо как имя массива, либо как
  377. указатель, и поступать с ним соответственно. Она может даже использовать оба
  378. вида записи, если это покажется ей уместным.
  379. \index{подмассив-аргумент}%
  380. \index{указатель!аргумент}%
  381. Функции можно передать часть массива, для этого аргумент должен указывать на
  382. начало подмассива. Например, если \verb|a| -- массив, то в записях
  383. \begin{ShortCodePar}
  384. f(&a[2])
  385. \end{ShortCodePar}
  386. \noindent или
  387. \begin{ShortCodePar}
  388. f(a+2)
  389. \end{ShortCodePar}
  390. \noindent функции \verb|f| передаётся адрес подмассива, начинающегося с элемента
  391. \verb|a[2]|. Внутри функции \verb|f| описание параметров может выглядеть как
  392. \begin{ShortCodePar}
  393. f(int arr[]) { ... }
  394. \end{ShortCodePar}
  395. \noindent или
  396. \begin{ShortCodePar}
  397. f(int *arr) { ... }
  398. \end{ShortCodePar}
  399. \noindent Следовательно, для \verb|f| тот факт, что параметр ссылается на часть
  400. массива, а не на весь массив, не имеет значения.
  401. \index{индекс отрицательный}%
  402. \index{отрицательные индексы}%
  403. \index{указатели!и индексирование}%
  404. Если есть уверенность, что элементы массива существуют, то возможно
  405. индексирование и в <<обратную>> сторону по отношению к нулевому элементу;
  406. выражения \verb|p[-1]|, \verb|p[-2]| и т.д. не противоречат синтаксису языка и
  407. ссылаются на элементы, стоящие непосредственно перед \verb|p[0]|. Разумеется,
  408. нельзя <<выходить>> за границы массива и тем самым ссылаться на несуществующие
  409. <<объекты>>.
  410. \index{указатель!а не массив|)}%
  411. \index{массив!а не указатель|)}%
  412. \index{индексирование массива!и указатели|)}%
  413. \section{Адресная арифметика}
  414. \index{указатели!арифметика с|(}%
  415. Если \verb|p| есть указатель на некоторый элемент массива, то \verb|p++|
  416. продвигает \verb|p| так, чтобы он указывал на следующий элемент, а \verb|p += i|
  417. увеличивает его, чтобы он указывал на \verb|i|-й элемент после того, на который
  418. он указывал ранее. Эти и подобные конструкции -- самые простые примеры
  419. арифметики над указателями, называемой также адресной арифметикой.
  420. Си последователен и единообразен в своём подходе к адресной арифметике. Это
  421. соединение в одном языке указателей, массивов и адресной арифметики -- одна из
  422. сильных его сторон. Проиллюстрируем сказанное построением простого
  423. распределителя памяти, состоящего из двух программ.
  424. \index{функция!alloc@\texttt{alloc}}%
  425. Первая, \verb|alloc(n)|‚ возвращает указатель \verb|p| на \verb|n|
  426. последовательно расположенных ячеек типа \verb|char|; программой, обращающейся к
  427. \verb|alloc|, эти ячейки могут быть использованы для запоминания литер.
  428. \index{функция!afree@\texttt{afree}}%
  429. Вторая, \verb|afree(p)|‚ освобождает память для, возможно, повторной её
  430. утилизации. Простота алгоритма обусловлена предположением, что обращения к
  431. \verb|afree| делаются в обратном порядке по отношению к соответствующим
  432. обращениям к \verb|alloc|. Таким образом, память, с которой работают
  433. \verb|alloc| и \verb|afree|, является стеком (списком, в основе которого лежит
  434. принцип <<последним вошёл, первым ушёл>>). В стандартной библиотеке имеются
  435. функции \verb|malloc| и \verb|free|, которые делают то же самое, только без
  436. упомянутых ограничений; в разд.~\ref{sec:memory_allocator} мы покажем, как они
  437. выглядят.
  438. Функцию \verb|alloc| легче всего реализовать, если условиться, что она будет
  439. выдавать куски некоторого большого массива типа \verb|char|, который мы назовём
  440. \verb|allocbuf|. Этот массив отдадим в личное пользование функциям \verb|alloc|
  441. и \verb|afree|. Так как они имеют дело с указателями, а не с индексами массива,
  442. то другим программам знать его имя не нужно. Кроме того, этот массив можно
  443. определить в том же исходном файле, что и \verb|alloc| и \verb|afree|, объявив
  444. его с классификатором \verb|static|, благодаря чему он станет невидимым вне
  445. этого файла. На практике такой массив может и вовсе не иметь имени, поскольку
  446. его можно запросить с помощью \verb|malloc| у операционной системы и получить
  447. указатель на некоторый безымянный блок памяти.
  448. Естественно, нам нужно знать, сколько элементов массива \verb|allocbuf| уже
  449. занято. Мы введём указатель \verb|allocp|, который будет указывать на первый
  450. свободный элемент. Если запрашивается память для \verb|n| литер, то \verb|alloc|
  451. возвращает текущее значение \verb|allocp| (т.е. адрес начала свободного блока) и
  452. затем увеличивает его на \verb|n|, чтобы указатель \verb|allocp| ссылался на
  453. следующую свободную область. Если же пространства нет, то \verb|alloc| выдаёт
  454. нуль. Функция \verb|afree(p)| просто устанавливает в \verb|allocp| значение
  455. \verb|p|, если оно не выходит за пределы массива \verb|allocbuf|.
  456. \begin{figure}[H]
  457. \center{\includegraphics[width=0.8926554\linewidth]{chapt5_sec4_img0.eps}}
  458. \end{figure}
  459. \begin{LongCodePar}
  460. #define ALLOCSIZE 10000 /* размер доступного пространства */
  461. static char allocbuf[ALLOCSIZE]; /* память для alloc */
  462. static char *allocp = allocbuf; /* ук-ль на своб. место */
  463. char *alloc(int n) /* возвращает указатель на n литер */
  464. {
  465. if (allocbuf + ALLOCSIZE - allocp >= n) {
  466. allocp += n; /* пространство есть */
  467. return allocp - n; /* старое p */
  468. } else /* пространства нет */
  469. return 0;
  470. }
  471. void afree(char *p) /* освобождается память по ук-лю p */
  472. {
  473. if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
  474. allocp = p;
  475. }
  476. \end{LongCodePar}
  477. \index{инициализация!указателя}%
  478. \index{указатель!инициализация}%
  479. \index{указатель!пустой}%
  480. Как и любую другую переменную, указатель можно инициализировать, но только
  481. такими осмысленными для него значениями, как нуль или выражение, приводящее к
  482. некоторому адресу ранее определённых данных соответствующего типа. Декларация
  483. \begin{ShortCodePar}
  484. static char *allocp = allocbuf;
  485. \end{ShortCodePar}
  486. \noindent определяет \verb|allocp| как указатель на \verb|char| и инициализирует
  487. его адресом массива \verb|allocbuf|, поскольку перед началом работы программы
  488. массив \verb|allocbuf| пуст. Указанная декларация могла бы иметь и такой вид:
  489. \begin{ShortCodePar}
  490. static char *allocp = &allocbuf[0];
  491. \end{ShortCodePar}
  492. \noindent поскольку имя массива и есть адрес его нулевого элемента.
  493. Проверка
  494. \begin{ShortCodePar}
  495. if (allocbuf + ALLOCSIZE - allocp >= n) {
  496. \end{ShortCodePar}
  497. \noindent контролирует, достаточно ли пространства, чтобы удовлетворить запрос
  498. на \verb|n| литер. Если памяти достаточно, то новое значение для \verb|allocp|
  499. должно указывать не далее чем на следующую позицию за последним элементом
  500. \verb|allocbuf|. При выполнении этого требования \verb|alloc| выдаёт указатель
  501. на начало выделенного блока литер (обратите внимание на описание типа самой
  502. функции). Если требование не выполняется, функция \verb|alloc| должна выдать
  503. какой-то сигнал о том, что памяти не хватает. Си гарантирует, что нуль никогда
  504. не будет правильной ссылкой на данные, поэтому мы будем использовать его в
  505. качестве признака аварийного события, в нашем случае нехватки памяти.
  506. \index{неправильная арифметика с указателями}%
  507. \index{указатели!неправильная арифметика с}%
  508. \index{указатели!операция над}%
  509. \index{указатели!сравнение}%
  510. Указатели и целые не являются взаимозаменяемыми объектами.
  511. \index{указатель!пустой}%
  512. \index{null-указатель}%
  513. Константа нуль -- единственное исключение из этого правила: её можно присвоить
  514. указателю, и указатель можно сравнить с нулевой константой.
  515. \index{файл!головной!<stdio.h>@\texttt{<stdio.h>}}%
  516. \index{NULL@\texttt{NULL}}%
  517. \index{stdio.h@\texttt{<stdio.h>}}%
  518. Чтобы показать, что нуль -- это специальное значение для указателя, вместо цифры
  519. нуль, как правило, записывают \verb|NULL| -- константу, определённую в файле
  520. \verb|<stdio.h>|. С этого момента и мы будем ею пользоваться.
  521. Проверки
  522. \begin{ShortCodePar}
  523. if (allocbuf + ALLOCSIZE - allocp >= n) {
  524. \end{ShortCodePar}
  525. \noindent и
  526. \begin{ShortCodePar}
  527. if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
  528. \end{ShortCodePar}
  529. \noindent демонстрируют несколько важных свойств арифметики с указателями.
  530. \index{операции над!указателями}%
  531. \index{сравнение указателей}%
  532. Во-первых, при соблюдении некоторых правил указатели можно сравнивать. Если
  533. \verb|p| и \verb|q| указывают на элементы одного массива, то к ним можно
  534. применять операторы отношения \verb|==|, \verb|!=|, \verb|<|, \verb|>=| и т.д.
  535. Например, отношение вида
  536. \begin{ShortCodePar}
  537. p < q
  538. \end{ShortCodePar}
  539. \noindent истинно, если \verb|p| указывает на более ранний элемент массива, чем
  540. \verb|q|. Любой указатель всегда можно сравнить на равенство и неравенство с
  541. нулём. А вот для указателей, ссылающихся на элементы разных массивов, результат
  542. арифметических операций или сравнений не определён. (Существует одно исключение:
  543. в арифметике с указателями можно использовать адрес несуществующего <<следующего
  544. за массивом>> элемента, т.е. адрес того <<элемента>>, который станет последним,
  545. если в массив добавить ещё один элемент.)
  546. Во-вторых, как вы уже, наверное, заметили, указатели и целые можно складывать и
  547. вычитать. Запись вида
  548. \begin{ShortCodePar}
  549. p + n
  550. \end{ShortCodePar}
  551. \noindent означает адрес объекта, занимающего \verb|n|-е место после объекта, на
  552. который указывает \verb|p|.
  553. \index{масштабирование целых в арифметике с указателями}%
  554. \index{указатели!коэффициент домножения целых в арифметике с}%
  555. Это справедливо безотносительно к типу объекта, на который ссылается \verb|p|;
  556. \verb|n| автоматически домножается на коэффициент, соответствующий размеру
  557. объекта. Информация о размере неявно присутствует в описании \verb|p|. Если, к
  558. примеру, \verb|int| занимает четыре байта, то коэффициент умножения будет равен
  559. четырём.
  560. \index{вычитание из указателя}%
  561. \index{указатели!вычитание}%
  562. Допускается также вычитание указателей. Например, если \verb|p| и \verb|q|
  563. ссылаются на элементы одного массива и \verb|p < q|, то \verb|q - p + 1| есть
  564. число элементов от \verb|p| до \verb|q| включительно.
  565. \index{стринг!длина}%
  566. \index{функция!strlen@\texttt{strlen}}%
  567. Этим фактом можно воспользоваться при написании ещё одной версии \verb|strlen|:
  568. \begin{LongCodePar}
  569. /* strlen: возвращает длину стринга s */
  570. int strlen(char *s)
  571. {
  572. char *p = s;
  573. while (*p != '\0')
  574. p++;
  575. return p - s;
  576. }
  577. \end{LongCodePar}
  578. \noindent В своём определении \verb|p| инициализируется значением \verb|s|, т.е.
  579. вначале \verb|p| указывает на первую литеру стринга. На каждом шаге цикла
  580. \verb|while| проверяется очередная литера; цикл продолжается до тех пор, пока не
  581. встретится \verb|'\0'|. Каждое продвижение указателя \verb|p| на следующую
  582. литеру выполняется инструкцией \verb|p++|, и разность \verb|p-s| даёт число
  583. пройденных литер, т.е. длину стринга. (Число литер в стринге может быть слишком
  584. большим, чтобы хранить его в переменной типа \verb|int|.
  585. \index{файл!головной!<stddef.h>@\texttt{<stddef.h>}}%
  586. \index{ptrdiff{\_}t@\texttt{ptrdiff{\_}t}}%
  587. Тип \verb|ptrdiff_t|, достаточный для хранения разности (со знаком) двух
  588. указателей, определён в головном файле \verb|<stddef.h>|.
  589. \index{size{\_}t@\texttt{size{\_}t}}%
  590. Однако, если быть очень осторожными, нам следовало бы для возвращаемого
  591. результата использовать тип \verb|size_t|‚ в этом случае наша программа
  592. соответствовала бы стандартной библиотечной версии.
  593. \index{оператор!sizeof@\texttt{sizeof}}%
  594. Тип \verb|size_t| есть тип беззнакового целого, возвращаемого оператором
  595. \verb|sizeof|.)
  596. \index{неправильная арифметика с указателями}%
  597. \index{указатели!коэффициент домножения целых в арифметике с}%
  598. \index{указатели!неправильная арифметика с}%
  599. Арифметика с указателями учитывает тип: если она имеет дело со значениями
  600. \verb|float|, занимающими больше памяти, чем \verb|char|, и \verb|p| --
  601. указатель на \verb|float|, то \verb|p++| продвинет \verb|p| на следующее
  602. значение \verb|float|. Это значит, что другую версию \verb|alloc|, которая имеет
  603. дело с элементами типа \verb|float|, а не \verb|char|, можно получить простой
  604. заменой в \verb|alloc| и \verb|afree| всех \verb|char| на \verb|float|. Все
  605. операции с указателями будут автоматически откорректированы в соответствии с
  606. размером объектов, на которые ссылаются указатели.
  607. \index{неправильная арифметика с указателями}%
  608. \index{указатели!неправильная арифметика с}%
  609. Допускаются следующие операции с указателями: присваивание значения указателя
  610. другому указателю того же типа, сложение и вычитание указателя и целого,
  611. вычитание и сравнение двух указателей, ссылающихся на элементы одного и того же
  612. массива, а также присваивание указателю нуля и сравнение указателя с нулём. Все
  613. другие операции с указателями не допускаются. Нельзя складывать два указателя,
  614. перемножать их, делить, сдвигать, выделять разряды; указатель нельзя складывать
  615. со значением типа \verb|float| или \verb|double|;
  616. \index{указатель!void *@\texttt{void *}}%
  617. \index{void *, указатель@\texttt{void *}, указатель}%
  618. указателю одного типа нельзя даже присвоить указатель другого типа, не выполнив
  619. предварительно операции приведения (исключение составляют лишь указатели типа
  620. \verb|void *|).
  621. \index{указатели!арифметика с|)}%
  622. \section{Литерные указатели и функции}
  623. \index{массив!литер}%
  624. \index{константа!стринговая}%
  625. \emph{Стринговая константа}, написанная в виде
  626. \begin{ShortCodePar}
  627. "I am a string"
  628. \end{ShortCodePar}
  629. \noindent есть массив литер. Во внутреннем представлении этот массив
  630. заканчивается <<пустой>> литерой \verb|'\0'|, по которой программа может найти
  631. конец стринга. Число занятых ячеек памяти на одну больше, чем количество литер,
  632. помещённых между двойными кавычками.
  633. Чаще всего стринговые константы используются в качестве аргументов функций, как,
  634. например, в
  635. \begin{ShortCodePar}
  636. printf("здравствуй, мир\n");
  637. \end{ShortCodePar}
  638. %
  639. % в оригинале опечатка ``здраствуй''
  640. %
  641. \noindent Когда такой литерный стринг появляется в программе, доступ к нему
  642. осуществляется через литерный указатель; \verb|printf| получает указатель на
  643. начало массива литер. Точнее, доступ к стринговой константе осуществляется через
  644. указатель на её первый элемент.
  645. Стринговые константы нужны не только в качестве аргументов функций. Если,
  646. например, переменную \verb|pmessage| описать как
  647. \begin{ShortCodePar}
  648. char *pmessage
  649. \end{ShortCodePar}
  650. \noindent то присваивание
  651. \begin{ShortCodePar}
  652. pmessage = "now is the time";
  653. \end{ShortCodePar}
  654. \noindent поместит в неё указатель на литерный массив, при этом сам стринг
  655. \emph{не} копируется, копируется лишь указатель на него. Операции для работы со
  656. стрингом как с единым целым в Си не предусмотрены.
  657. \index{массив!а не указатель}%
  658. \index{указатель!а не массив}%
  659. Существует важное различие между следующими определениями:
  660. \begin{ShortCodePar}
  661. char amessage[] = "now is the time"; /* массив */
  662. char *pmessage = "now is the time"; /* указатель */
  663. \end{ShortCodePar}
  664. \noindent \verb|amessage| -- это массив, имеющий такой объем, что в нём как раз
  665. помещается указанная последовательность литер и \verb|'\0'|. Отдельные литеры
  666. внутри массива могут изменяться, но \verb|amessage| всегда ссылается на одно и
  667. то же место памяти. В противоположность ему \verb|pmessage| есть указатель,
  668. инициализированный ссылкой на стринговую константу. А значение указателя можно
  669. изменить, и тогда последний будет ссылаться на что-либо другое. Кроме того,
  670. результат будет неопределён, если вы попытаетесь изменить содержимое константы.
  671. \begin{figure}[H]
  672. \center{\includegraphics[width=0.6629213\linewidth]{chapt5_sec5_img0.eps}}
  673. \end{figure}
  674. %
  675. % в картинке исправлены опечатки - неверное экранирование символа \'0'
  676. %
  677. \index{массив!а не указатель}%
  678. \index{указатель!а не массив}%
  679. Дополнительные моменты, связанные с указателями и массивами, проиллюстрируем на
  680. несколько видоизменённых вариантах двух полезных программ, взятых нами из
  681. стандартной библиотеки. Первая из них, функция \verb|strcpy(s,t)|, копирует
  682. стринг \verb|t| в стринг \verb|s|. Хотелось бы написать прямо \verb|s=t|‚ но
  683. такой оператор копирует указатель, а не литеры. Чтобы копировать литеры, нам
  684. нужно организовать цикл.
  685. \index{функция!strcpy@\texttt{strcpy}}%
  686. Первый вариант \verb|strcpy|, с использованием массива, имеет следующий вид:
  687. \begin{LongCodePar}
  688. /* strcpy: копирует t в s; вариант с индексируемым массивом */
  689. void strcpy(char *s, char *t)
  690. {
  691. int i;
  692. i = 0;
  693. while ((s[i] = t[i]) != '\0')
  694. i++;
  695. }
  696. \end{LongCodePar}
  697. \noindent%
  698. \index{функция!strcpy@\texttt{strcpy}}%
  699. Для сравнения приведём версию \verb|strcpy| с указателями:
  700. \begin{LongCodePar}
  701. /* strcpy: копирует t в s; версия 1 (с указателями) */
  702. void strcpy(char *s, char *t)
  703. {
  704. while ((*s = *t) != '\0') {
  705. s++;
  706. t++;
  707. }
  708. }
  709. \end{LongCodePar}
  710. \noindent Поскольку передаются лишь копии значений аргументов, \verb|strcpy|
  711. может свободно пользоваться параметрами \verb|s| и \verb|t| как своими
  712. локальными переменными. Они должным образом инициализированы указателями,
  713. которые продвигаются каждый раз на следующую литеру в каждом из массивов до тех
  714. пор, пока в копируемом стринге \verb|t| не встретится \verb|'\0'|.
  715. \index{функция!strcpy@\texttt{strcpy}}%
  716. На практике \verb|strcpy| так не пишут. Опытный программист предпочтёт более
  717. короткую запись:
  718. \begin{ShortCodePar}
  719. /* strcpy: копирует t в s; версия 2 (с указателями) */
  720. void strcpy(char *s, char *t)
  721. {
  722. while ((*s++ = *t++) != '\0')
  723. ;
  724. }
  725. \end{ShortCodePar}
  726. \noindent Продвижение \verb|s| и \verb|t| здесь осуществляется в управляющей
  727. части цикла.
  728. \index{операторы!постфиксные \texttt{++} и \texttt{\textminus\textminus}}%
  729. Значением \verb|*t++| является литера, на которую указывает переменная \verb|t|
  730. перед тем, как её значение будет продвинуто; постфиксный оператор \verb|++| не
  731. изменяет указатель \verb|t|, пока не будет взята литера, на которую он
  732. указывает. То же в отношении \verb|s|, сначала литера запомнится в позиции, на
  733. которую указывает старое значение \verb|s|, и лишь после этого значение
  734. переменной \verb|s| увеличится. Пересылаемая литера является одновременно и
  735. значением, которое сравнивается с \verb|'\0'|. В итоге копируются все литеры, включая и заключительную литеру \verb|'\0'|.
  736. \index{нуль, опущенная проверка на него}%
  737. \index{функция!strcpy@\texttt{strcpy}}%
  738. Заметив, что сравнение с \verb|'\0'| здесь лишнее (поскольку в Си ненулевое
  739. значение выражения в условии трактуется и как его истинность), мы можем сделать
  740. ещё одно и последнее сокращение текста программы:
  741. \begin{ShortCodePar}
  742. /* strcpy: копирует t в s; версия 3 (с указателями) */
  743. void strcpy(char *s, char *t)
  744. {
  745. while (*s++ = *t++)
  746. ;
  747. }
  748. \end{ShortCodePar}
  749. \noindent Хотя на первый взгляд то, что мы получили, выглядит как криптограмма,
  750. всё же такая запись значительно удобнее, и следует освоить её, поскольку в
  751. Си-программах вы будете с ней часто встречаться.
  752. \index{файл!головной!<string.h>@\texttt{<string.h>}}%
  753. Что касается функции \verb|strcpy| из стандартной библиотеки \verb|<string.h>|‚
  754. то она возвращает в качестве своего результата ещё и ссылку на новую копию
  755. стринга.
  756. \index{функция!strcmp@\texttt{strcmp}}%
  757. Вторая программа, которую мы здесь рассмотрим, это \verb|strcmp(s,t)|. Она
  758. сравнивает литеры стрингов \verb|s| и \verb|t| и возвращает отрицательное,
  759. нулевое или положительное значение, если стринг \verb|s| соответственно
  760. лексикографически меньше, равен или больше, чем стринг \verb|t|. Результат
  761. получается вычитанием первых несовпадающих литер из \verb|s| и \verb|t|.
  762. \begin{LongCodePar}
  763. /* strcmp: выдаёт <0 при s<t, 0 при s==t, >0 при s>t */
  764. int strcmp(char *s, char *t)
  765. {
  766. int i;
  767. for (i = 0; s[i] == t[i]; i++)
  768. if (s[i] == '\0')
  769. return 0;
  770. return s[i] - t[i];
  771. }
  772. \end{LongCodePar}
  773. \noindent Та же программа с использованием указателей записывается так:
  774. \begin{LongCodePar}
  775. /* strcmp: выдаёт <0 при s<t, 0 при s==t, >0 при s>t */
  776. int strcmp(char *s, char *t)
  777. {
  778. for ( ; *s == *t; s++, t++)
  779. if (*s == '\0')
  780. return 0;
  781. return *s - *t;
  782. }
  783. \end{LongCodePar}
  784. \index{оператор!инкрементации \texttt{++}}%
  785. \index{оператор!декрементации \texttt{\textminus\textminus}}%
  786. \index{операторы!постфиксные \texttt{++} и \texttt{\textminus\textminus}}%
  787. \index{операторы!префиксные \texttt{++} и \texttt{\textminus\textminus}}%
  788. Поскольку операторы \verb|++| и \verb|--| могут быть и префиксными, и
  789. постфиксными, возможны (хотя встречаются и не так часто) другие их сочетания с
  790. оператором \verb|*|. Например,
  791. \begin{ShortCodePar}
  792. *--p
  793. \end{ShortCodePar}
  794. \noindent уменьшит \verb|p| прежде, чем по этому указателю будет получена
  795. литера. Например, следующие два выражения:
  796. \begin{ShortCodePar}
  797. *p++ = val; /* поместить val в стек */
  798. val = *--p; /* взять из стека значение и поместить в val */
  799. \end{ShortCodePar}
  800. \noindent являются стандартными записями для посылки в стек и взятия из
  801. стека.~(См. разд.~\ref{sec:external_variables})
  802. \index{файл!головной!<string.h>@\texttt{<string.h>}}%
  803. Описания функций, упомянутых в этом разделе, а также ряда других стандартных
  804. функций, работающих со стрингами, содержатся в головном файле \verb|<string.h>|.
  805. \paragraph{Упражнение 5.3.} Используя указатели, напишите функцию
  806. \verb|strcat|, которую мы рассматривали в
  807. гл.~\ref{chapt:types-operators-expressions} (функция \verb|strcat(s,t)| копирует
  808. стринг \verb|t| в конец стринга \verb|s|).
  809. \paragraph{Упражнение 5.4.} Напишите функцию \verb|strend(s,t)|, которая выдаёт
  810. $1$, если стринг \verb|t| расположен в конце стринга \verb|s|, и нуль в
  811. противном случае.
  812. \paragraph{Упражнение 5.5.} Напишите варианты библиотечных функций
  813. \verb|strncpy|, \verb|strncat| и \verb|strncmp|, которые оперируют с первыми
  814. литерами своих аргументов, число которых не превышает \verb|n|. Например,
  815. \verb|strncpy(t,s,n)| копирует не более \verb|n| литер \verb|t| в \verb|s|.
  816. Полные описания этих функций содержатся в приложении~\ref{apx:stdlib}.
  817. \paragraph{Упражнение 5.6.} Отберите подходящие программы из предыдущих глав и
  818. упражнений и перепишите их, используя вместо индексирования указатели. Подойдут,
  819. в частности, программы
  820. \verb|getline|~(гл.~\ref{chapt:tutorial_intro}~и~\ref{chapt:functions_and_program_structure}),
  821. \verb|atoi|, \verb|itoa| и их
  822. варианты~(гл.~\ref{chapt:types-operators-expressions},~\ref{chapt:control_flow}~и~\ref{chapt:functions_and_program_structure}),
  823. \verb|reverse|~(гл.~\ref{chapt:control_flow}), а также \verb|strindex| и
  824. \verb|getop|~(гл.~\ref{chapt:functions_and_program_structure}).
  825. \section{Массивы указателей, указатели на указатели}
  826. \label{sec:arrays_of_pointers}
  827. \index{массив!указателей}%
  828. \index{указатели!массив из}%
  829. Как и любые другие переменные, указатели можно группировать в массивы.
  830. \index{программа!сортировки}%
  831. \index{сортировка!текстовых строк}%
  832. Для иллюстрации этого напишем программу, сортирующую в алфавитном порядке
  833. текстовые строки; это будет упрощённый вариант программы \verb|sort| системы
  834. UNIX.
  835. В гл.~\ref{chapt:control_flow} мы привели функцию сортировки по Шеллу, которая
  836. упорядочивает массив целых, а в гл.~\ref{chapt:functions_and_program_structure}
  837. улучшили её, повысив быстродействие.
  838. Те же алгоритмы используются и здесь,
  839. однако теперь они будут обрабатывать текстовые строки, которые могут иметь
  840. разную длину и сравнение или перемещение которых невозможно выполнить за одну
  841. операцию. Нам необходимо выбрать некоторое представление данных, которое бы
  842. позволило удобно и эффективно работать с текстовыми строками произвольной длины.
  843. \index{сортировка!лексикографическая}%
  844. Для этого воспользуемся массивом указателей на начала строк. Поскольку строки в
  845. памяти расположены вплотную друг к другу, к каждой отдельной строке доступ
  846. просто осуществлять через указатель на её первую литеру. Сами указатели можно
  847. организовать в виде массива. Одна из возможностей сравнить две строки --
  848. передать указатели на них функции \verb|strcmp|. Чтобы поменять местами строки,
  849. достаточно будет поменять местами в массиве их указатели (а не сами строки).
  850. \begin{figure}[H]
  851. \center{\includegraphics[width=0.8651685\linewidth]{chapt5_sec6_img0.eps}}
  852. \end{figure}
  853. \noindent Здесь снимаются сразу две проблемы: одна -- связанная со сложностью
  854. управления памятью, а вторая -- с большими накладными расходами при
  855. перестановках самих строк.
  856. \index{модульность}%
  857. Процесс сортировки распадается на три этапа:
  858. \begin{ShortCodeParWithCC}{\\\{\}}
  859. \textit{чтение всех строк из ввода}
  860. \textit{сортировка введённых строк}
  861. \textit{печать их по порядку}
  862. \end{ShortCodeParWithCC}
  863. \noindent Как обычно, выделим функции, соответствующие естественному делению
  864. задачи, и напишем главную программу, управляющую этими функциями. Отложим на
  865. время реализацию этапа сортировки и сосредоточимся на структуре данных и
  866. вводе-выводе.
  867. Программа ввода должна прочитать и запомнить литеры всех строк, а также
  868. построить массив указателей на строки. Она, кроме того, должна подсчитать число
  869. введённых строк -- эта информация понадобится для сортировки и печати. Так как
  870. функция ввода может работать только с конечным числом строк, то, если их введено
  871. слишком много, она будет выдавать некоторое значение, которое никогда не
  872. совпадёт ни с каким количеством строк, например, $-1$.
  873. Программа вывода занимается только тем, что печатает строки, причём в том
  874. порядке, в котором в массиве указателей на них расположены ссылки.
  875. \index{функция!readlines@\texttt{readlines}}%
  876. \index{функция!writelines@\texttt{writelines}}%
  877. \begin{LongCodePar}
  878. #include <stdio.h>
  879. #include <string.h>
  880. #define MAXLINES 5000 /* максимальное число строк */
  881. char *lineptr[MAXLINES]; /* указатели на строки */
  882. int readlines(char *lineptr[], int nlines);
  883. void writelines(char *lineptr[], int nlines);
  884. void qsort(char *lineptr[], int left, int right);
  885. /* сортировка строк */
  886. main()
  887. {
  888. int nlines; /* количество прочитанных строк */
  889. if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
  890. qsort(lineptr, 0, nlines-1);
  891. writelines(lineptr, nlines);
  892. return 0;
  893. } else {
  894. printf("ошибка: слишком много строк\n");
  895. return 1;
  896. }
  897. }
  898. #define MAXLEN 1000 /* максимальная длина строки */
  899. int getline(char *, int);
  900. char *alloc(int);
  901. /* readlines: чтение строк */
  902. int readlines(char *lineptr[], int maxlines)
  903. {
  904. int len, nlines;
  905. char *p, line[MAXLEN];
  906. nlines = 0;
  907. while ((len = getline(line, MAXLEN)) > 0)
  908. if (nlines >= maxlines || (p = alloc(len)) == NULL)
  909. return -1;
  910. else {
  911. line[len-1] = '\0'; /* убираем литеру \n */
  912. strcpy(p, line);
  913. lineptr[nlines++] = p;
  914. }
  915. return nlines;
  916. }
  917. /* writelines: печать строк */
  918. void writelines(char *lineptr[], int nlines)
  919. {
  920. int i;
  921. for (i = 0; i < nlines; i++)
  922. printf("%s\n", lineptr[i]);
  923. }
  924. \end{LongCodePar}
  925. \noindent Функция \verb|getline| взята из разд.~\ref{sec:char_arrays}.
  926. Основное новшество здесь -- декларация \verb|lineptr|:
  927. \begin{ShortCodePar}
  928. char *lineptr[MAXLINES];
  929. \end{ShortCodePar}
  930. \noindent в которой сообщается, что \verb|lineptr| есть массив из
  931. \verb|MAXFILES| элементов, каждый из которых представляет собой указатель на
  932. \verb|char|. Иначе говоря, \verb|lineptr[i]| -- указатель на литеру, а
  933. \verb|*lineptr[i]| -- литера, на которую он указывает (первая литера \verb|i|-й
  934. строки текста).
  935. \index{функция!writelines@\texttt{writelines}}%
  936. Так как \verb|lineptr| -- имя массива, его можно трактовать как указатель, т.е.
  937. так же, как мы это делали в предыдущих примерах, и \verb|writelines| переписать
  938. следующим образом:
  939. \begin{ShortCodePar}
  940. /* writelines: печать строк */
  941. void writelines(char *lineptr[], int nlines)
  942. {
  943. while (nlines-- > 0)
  944. printf("%s\n", *lineptr++);
  945. }
  946. \end{ShortCodePar}
  947. \noindent Вначале \verb|*lineptr| ссылается на первую строку; каждое приращение
  948. указателя приводит к тому, что \verb|*lineptr| ссылается на следующую строку, и
  949. делается это до тех пор, пока \verb|nlines| не станет нулём.
  950. Теперь, когда мы разобрались с вводом и выводом, можно приступить к сортировке.
  951. \index{быстрая сортировка}%
  952. \index{функция!qsort@\texttt{qsort}}%
  953. Быструю сортировку, описанную в гл.~\ref{chapt:functions_and_program_structure},
  954. надо несколько модифицировать: нужно изменить декларации, а операцию сравнения
  955. заменить обращением к \verb|strcmp|. Алгоритм остался тем же, и это даёт нам
  956. определённую уверенность в его правильности.
  957. \begin{LongCodePar}
  958. /* qsort: сортирует v[left]...v[right] по возрастанию */
  959. void qsort(char *v[], int left, int right)
  960. {
  961. int i, last;
  962. void swap(char *v[], int i, int j);
  963. if (left >= right) /* ничего не делается, если */
  964. return; /* в массиве менее двух элементов */
  965. swap(v, left, (left + right)/2);
  966. last = left;
  967. for (i = left+1; i <= right; i++)
  968. if (strcmp(v[i], v[left]) < 0)
  969. swap(v, ++last, i);
  970. swap(v, left, last);
  971. qsort(v, left, last-1);
  972. qsort(v, last+1, right);
  973. }
  974. \end{LongCodePar}
  975. \noindent%
  976. \index{функция!swap@\texttt{swap}}%
  977. Небольшие поправки требуются и в программе перестановки.
  978. \begin{LongCodePar}
  979. /* swap: переставить v[i] и v[j] между собой */
  980. void swap(char *v[], int i, int j)
  981. {
  982. char *temp;
  983. temp = v[i];
  984. v[i] = v[j];
  985. v[j] = temp;
  986. }
  987. \end{LongCodePar}
  988. \noindent Так как каждый элемент массива \verb|v| (т.е. \verb|lineptr|) является
  989. указателем на литеру, \verb|temp| должен иметь тот же тип, что и \verb|v| --
  990. тогда можно будет осуществлять пересылки между \verb|temp| и элементами
  991. \verb|v|.
  992. \paragraph{Упражнение 5.7.} Напишите новую версию \verb|readlines|, которая
  993. запоминала бы строки в массиве, определённом в \verb|main|, а не запрашивала
  994. память посредством программы \verb|alloc|. Насколько быстрее эта программа?
  995. \section{Многомерные массивы}
  996. \index{массив!многомерный}%
  997. \index{массив!двумерный}%
  998. \index{декларация!массива}%
  999. \index{инициализация!двухмерных массивов}%
  1000. В Си имеется возможность задавать прямоугольные многомерные массивы, правда, на
  1001. практике по сравнению с массивами указателей они используются значительно реже.
  1002. В этом разделе мы продемонстрируем некоторые их свойства.
  1003. \index{преобразование!даты}%
  1004. Рассмотрим задачу перевода даты <<день-месяц>> в <<день года>> и обратно.
  1005. Например,
  1006. \index{високосный год, вычисление}%
  1007. 1 марта -- это 60-й день невисокосного или 61-й день високосного года. Определим
  1008. две функции для этих преобразований:
  1009. \index{функция!day{\_}of{\_}year@\texttt{day{\_}of{\_}year}}%
  1010. функция \verb|day_of_year| будет преобразовывать месяц-день в день года‚ а
  1011. \index{функция!month{\_}day@\texttt{month{\_}day}}%
  1012. \verb|month_day| -- день года в месяц-день. Поскольку последняя функция
  1013. вычисляет два значения, аргументы месяц и день будут указателями. Так,
  1014. \begin{ShortCodePar}
  1015. month_day(1988, 60, &m, &d)
  1016. \end{ShortCodePar}
  1017. \noindent установит в \verb|m| число $2$, а в \verb|d| -- значение $29$ (29
  1018. февраля).
  1019. Нашим функциям нужна одна и та же информация, а именно таблица, содержащая числа
  1020. дней каждого месяца. Так как для високосного и невисокосного годов эти таблицы
  1021. будут различаться, проще иметь две отдельные строки в двумерном массиве, чем во
  1022. время вычислений отслеживать особый случай с февралём. Массив и функции,
  1023. выполняющие преобразования, имеют следующий вид:
  1024. \begin{LongCodePar}
  1025. static char daytab[2][13] = {
  1026. {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  1027. {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
  1028. };
  1029. /* day_of_year: определяет день года по месяцу и дню */
  1030. int day_of_year(int year, int month, int day)
  1031. {
  1032. int i, leap;
  1033. leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
  1034. for (i = 1; i < month; i++)
  1035. day += daytab[leap][i];
  1036. return day;
  1037. }
  1038. /* month_day: определяет месяц и день по дню года */
  1039. void month_day(int year, int yearday, int *pmonth, int *pday)
  1040. {
  1041. int i, leap;
  1042. leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
  1043. for (i = 1; yearday > daytab[leap][i]; i++)
  1044. yearday -= daytab[leap][i];
  1045. *pmonth = i;
  1046. *pday = yearday;
  1047. }
  1048. \end{LongCodePar}
  1049. %%
  1050. %% исправлена опечатка
  1051. %% в оригинале "day_year"
  1052. %%
  1053. \noindent Напоминаем, что арифметическое значение логического выражения
  1054. (например, выражения, с помощью которого вычислялось \verb|leap|) равно нулю
  1055. (ложь) или единице (истина), так что мы можем использовать его как индекс в
  1056. массиве \verb|daytab|.
  1057. Массив \verb|daytab| должен быть внешним по отношению к обеим функциям
  1058. \verb|day_of_year| и \verb|month_day|, так как он нужен и той и другой. Мы
  1059. сделали его типа \verb|char|, чтобы проиллюстрировать законность применения типа
  1060. \verb|char| для малых целых.
  1061. Массив \verb|daytab| -- это первый массив из числа двумерных, с которыми мы ещё
  1062. не имели дела. Строго говоря, в Си двумерный массив рассматривается как
  1063. одномерный массив, каждый элемент которого -- также массив. Поэтому
  1064. индексирование изображается как
  1065. \begin{ShortCodePar}
  1066. daytab[i][j] /* [строка][столбец] */
  1067. \end{ShortCodePar}
  1068. \noindent а не как
  1069. \begin{ShortCodePar}
  1070. daytab[i,j] /* НЕВЕРНО */
  1071. \end{ShortCodePar}
  1072. \noindent Особенность двумерного массива в Си заключается лишь в форме записи, в
  1073. остальном его можно трактовать почти так же, как в других языках.
  1074. \index{массив!порядок элементов в памяти}%
  1075. Элементы запоминаются строками, следовательно, при переборе их в том порядке,
  1076. как они расположены в памяти, чаще будет изменяться самый правый индекс.
  1077. Массив инициализируется списком начальных значений, заключённым в фигурные
  1078. скобки; каждая строка двумерного массива инициализируется соответствующим
  1079. подсписком. Нулевой столбец добавлен в начало \verb|daytab| лишь для того, чтобы
  1080. индексы, которыми мы будем пользоваться, совпадали с естественными номерами
  1081. месяцев от 1 до 12. Экономить пару ячеек памяти здесь нет никакого смысла, а
  1082. программа, в которой уже не надо корректировать индекс, выглядит более ясной.
  1083. \index{массив!имя в роли аргумента}%
  1084. \index{массив!декларация}%
  1085. \index{массив!а не указатель}%
  1086. \index{указатель!а не массив}%
  1087. Если двумерный массив передаётся функции в качестве аргумента, то декларация
  1088. соответствующего ему параметра должна содержать количество столбцов; количество
  1089. строк в данном случае несущественно, поскольку, как и прежде, функции будет
  1090. передана ссылка на массив строк, каждая из которых есть массив из 13 значений
  1091. типа \verb|char|. В нашем частном случае имеем указатель на объекты, являющиеся
  1092. массивами из 13 значений типа \verb|char|. Таким образом, если массив
  1093. \verb|daytab| передаётся некоторой функции \verb|f|, то эту функцию можно было
  1094. бы определить следующим образом:
  1095. \begin{ShortCodePar}
  1096. f(char daytab[2][13]) { ... }
  1097. \end{ShortCodePar}
  1098. \noindent Вместо этого можно записать
  1099. \begin{ShortCodePar}
  1100. f(char daytab[][13]) { ... }
  1101. \end{ShortCodePar}
  1102. \noindent поскольку число строк здесь не имеет значения, или
  1103. \begin{ShortCodePar}
  1104. f(char (*daytab)[13]) { ... }
  1105. \end{ShortCodePar}
  1106. \noindent последняя запись декларирует, что параметр есть указатель на массив из
  1107. 13 значений типа \verb|char|. Скобки здесь необходимы, так как квадратные скобки
  1108. \verb|[]| имеют более высокий приоритет, чем \verb|*|. Без скобок декларация
  1109. \begin{ShortCodePar}
  1110. char *daytab[13]
  1111. \end{ShortCodePar}
  1112. \noindent определяет массив из 13 указателей на \verb|char|.
  1113. \index{по умолчанию!размер массива}%
  1114. В более общем случае только первое измерение (соответствующее первому индексу)
  1115. можно не задавать, все другие специфицировать необходимо.
  1116. В разд.~\ref{sec:complex_declarations} мы продолжим рассмотрение сложных
  1117. деклараций.
  1118. \paragraph{Упражнение 5.8.} В функциях \verb|day_of_year| и \verb|month_day| нет
  1119. никаких проверок правильности вводимых дат. Устраните этот недостаток.
  1120. \section{Инициализация массивов указателей}
  1121. \index{инициализация!массива}%
  1122. \index{функция!month{\_}name@\texttt{month{\_}name}}%
  1123. Напишем функцию \verb|month_name(n)|, которая возвращает ссылку на стринг литер,
  1124. содержащий название \verb|n|-го месяца. Эта функция идеальна для демонстрации
  1125. использования статического массива. Функция \verb|month_name| имеет в своём
  1126. личном распоряжении массив стрингов, на один из которых она и возвращает ссылку.
  1127. Ниже покажем, как инициализируется этот массив имён.
  1128. Синтаксис задания начальных значений аналогичен синтаксису предыдущих
  1129. инициализаций:
  1130. \begin{LongCodePar}
  1131. /* month_name: возвращает имя n-го месяца */
  1132. char *month_name(int n)
  1133. {
  1134. static char *name[] = {
  1135. "Неверный месяц",
  1136. "Январь", "Февраль", "Март",
  1137. "Апрель", "Май", "Июнь",
  1138. "Июль", "Август", "Сентябрь",
  1139. "Октябрь", "Ноябрь", "Декабрь"
  1140. };
  1141. return (n < 1 || n > 12) ? name[0] : name [n];
  1142. }
  1143. \end{LongCodePar}
  1144. \noindent Декларация, определяющая \verb|name| как массив указателей на литеры,
  1145. такая же, как и декларация \verb|lineptr| в программе сортировки.
  1146. Инициализатором служит список стрингов, каждому из которых соответствует
  1147. определённое место в массиве. Литеры \verb|i|-го стринга где-то размещены, и
  1148. указатель на них запоминается в \verb|name[i]|.
  1149. \index{массив!размер по умолчанию}%
  1150. Так как размер массива \verb|name| не специфицирован, компилятор вычислит его по
  1151. количеству заданных начальных значений.
  1152. \section{Указатели вместо многомерных массивов}
  1153. \index{массив!двумерный}%
  1154. \index{массив!а не указатель}%
  1155. \index{указатель!а не массив}%
  1156. Начинающие программировать на Си иногда не понимают, в чём разница между
  1157. двумерным массивом и массивом указателей типа \verb|name| из приведённого
  1158. примера. Для двух следующих определений:
  1159. \begin{ShortCodePar}
  1160. int a[10][20];
  1161. int *b[10];
  1162. \end{ShortCodePar}
  1163. \noindent записи \verb|a[3][4]| и \verb|b[3][4]| будут синтаксически правильными
  1164. ссылками на некоторое значение типа \verb|int|. Однако только \verb|a| является
  1165. истинно двумерным массивом: для двухсот элементов типа \verb|int| будет выделена
  1166. память, а вычисление смещения элемента
  1167. \verb|a[|\textit{\Verb|строка|}\verb|,|\textit{\Verb|столбец|}\verb|]| от начала
  1168. массива будет вестись по формуле
  1169. $20\times\text{\textit{строка}}+\text{\textit{столбец}}$, учитывающей его
  1170. прямоугольную природу. Для \verb|b| же определяются только 10 указателей‚ причём
  1171. без инициализации. Инициализация должна задаваться явно -- либо статически,
  1172. либо в процессе счёта. Предположим, что каждый элемент \verb|b| ссылается на
  1173. двадцатиэлементный массив, в результате где-то будут выделены пространство, в
  1174. котором разместятся 200 значений типа \verb|int|, и ещё 10 ячеек для
  1175. указателей. Важное преимущество массива указателей в том, что строки такого
  1176. массива могут иметь разные длины. Таким образом, каждый элемент \verb|b| не
  1177. обязательно ссылается на двадцатиэлементный вектор; один может ссылаться на два
  1178. элемента, другой -- на пятьдесят, а некоторые и вовсе могут ни на что не
  1179. ссылаться.
  1180. Наши рассуждения здесь велись в отношении целых значений, однако чаще массивы
  1181. указателей используются для работы со стрингами литер, различающимися по длине,
  1182. как это было в функции \verb|month_name|. Сравните определение массива
  1183. указателей и соответствующий ему рисунок:
  1184. \begin{ShortCodePar}
  1185. char *name[] = {"Неправильный месяц", "Янв", "Февр", "Март"};
  1186. \end{ShortCodePar}
  1187. \begin{figure}[H]
  1188. \center{\includegraphics[width=0.5617978\linewidth]{chapt5_sec9_img0.eps}}
  1189. \end{figure}
  1190. %
  1191. % исправлена опечатка:
  1192. % в оригинале все терминаторы написаны как \o
  1193. %
  1194. \noindent с определением и рисунком для двумерного массива:
  1195. \begin{ShortCodePar}
  1196. char aname[][15] = {"Неправ. месяц", "Янв", "Февр", "Март"};
  1197. \end{ShortCodePar}
  1198. \begin{figure}[H]
  1199. \center{\includegraphics[width=0.9831461\linewidth]{chapt5_sec9_img1.eps}}
  1200. \end{figure}
  1201. %
  1202. % исправлена опечатка:
  1203. % в оригинале все терминаторы написаны как \o
  1204. %
  1205. \paragraph{Упражнение 5.9.} Перепишите программы \verb|day_of_year| и
  1206. \verb|month_of_year|‚ используя вместо индексов указатели.
  1207. \section{Аргументы в командной строке}
  1208. \index{аргументы командной строки|(}%
  1209. \index{командная строка аргументы|(}%
  1210. В операционной среде, обеспечивающей поддержку Си, имеется возможность передать
  1211. аргументы или параметры запускаемой программе при помощи командной строки. В
  1212. момент вызова \verb|main| получает два аргумента.
  1213. \index{argc@\texttt{argc}}%
  1214. В первом, обычно называемом \verb|argc| (сокращение от argument count), стоит
  1215. количество аргументов, задаваемых в командной строке.
  1216. \index{argv@\texttt{argv}|(}%
  1217. Второй, \verb|argv| (от argument vector), является указателем на массив литерных
  1218. стрингов, содержащих сами аргументы. Для работы с этими стрингами обычно
  1219. используются указатели нескольких уровней.
  1220. \index{программа!echo@\texttt{echo}}%
  1221. Простейший пример -- программа \verb|echo| (<<эхо>>), которая печатает аргументы
  1222. своей командной строки в одной строчке, отделяя их друг от друга пробелами. Так,
  1223. команда
  1224. \begin{ShortCodePar}
  1225. echo Здравствуй, мир!
  1226. \end{ShortCodePar}
  1227. \noindent напечатает
  1228. \begin{ShortCodePar}
  1229. Здравствуй, мир!
  1230. \end{ShortCodePar}
  1231. \noindent По соглашению \verb|argv[0]| есть имя вызываемой программы, так что
  1232. значение \verb|argc| никогда не бывает меньше $1$. Если \verb|argc| равен $1$,
  1233. то в командной строке после имени программы никаких аргументов нет. В нашем
  1234. примере \verb|argc| равен $3$, и соответственно \verb|argv[0]|‚ \verb|argv[1]| и
  1235. \verb|argv[2]| есть стринги \verb|"echo"|, \verb|"Здравствуй,"| и \verb|"мир!"|.
  1236. Первый необязательный аргумент -- это \verb|argv[1]|‚ последний --
  1237. \verb|argv[argc-1]|. Кроме того, стандарт требует, чтобы \verb|argv[argc]|
  1238. всегда был пустым указателем.
  1239. \begin{figure}[H]
  1240. \center{\includegraphics[width=0.5786517\linewidth]{chapt5_sec10_img0.eps}}
  1241. \end{figure}
  1242. \noindent%
  1243. \index{программа!echo@\texttt{echo}}%
  1244. Первая версия программы \verb|echo| трактует \verb|argv| как массив литерных
  1245. указателей.
  1246. \begin{LongCodePar}
  1247. #include <stdio.h>
  1248. /* эхо аргументов командной строки; версия 1 */
  1249. main(int argc, char *argv[])
  1250. {
  1251. int i;
  1252. for (i = 1; i < argc; i++)
  1253. printf("%s%s", argv[i], (i < argc-1) ? " " : "");
  1254. printf("\n");
  1255. return 0;
  1256. }
  1257. \end{LongCodePar}
  1258. \noindent%
  1259. \index{программа!echo@\texttt{echo}}%
  1260. Так как \verb|argv| есть указатель на массив указателей, мы можем работать с ним
  1261. как с указателем, а не как с индексируемым массивом. Следующая программа
  1262. основана на продвижении \verb|argv|, он продвигается так, что его значение в
  1263. каждый отдельный момент ссылается на очередной указатель на \verb|char|; перебор
  1264. указателей заканчивается, когда исчерпан \verb|argc|.
  1265. \begin{LongCodePar}
  1266. #include <stdio.h>
  1267. /* эхо аргументов командной строки; версия 2 */
  1268. main(int argc, char *argv[])
  1269. {
  1270. while (--argc > 0)
  1271. printf("%s%s", *++argv, (argc > 1) ? " " : "");
  1272. printf("\n");
  1273. return 0;
  1274. }
  1275. \end{LongCodePar}
  1276. \noindent Аргумент \verb|argv| -- указатель на начало массива аргументных
  1277. стрингов. Использование в \verb|++argv| префиксного оператора \verb|++| приведёт
  1278. к тому, что первым будет напечатан \verb|argv[1]|, а не \verb|argv[0]|. Каждое
  1279. очередное продвижение указателя даёт нам следующий аргумент, на который
  1280. ссылается \verb|*argv|. В это же время значение \verb|argc| уменьшается на $1$,
  1281. и, когда оно станет нулём, все аргументы будут напечатаны.
  1282. Инструкцию \verb|printf| можно было бы написать и так:
  1283. \begin{ShortCodePar}
  1284. printf((argc > 1) ? "%s " : "%s", *++argv);
  1285. \end{ShortCodePar}
  1286. \noindent Как видим, формат в \verb|printf| может быть выражением.
  1287. \index{программа!поиска!по образцу}%
  1288. В качестве второго примера возьмём программу поиска образца, рассмотренную в
  1289. разд.~\ref{sec:functions_basics}, и несколько усовершенствуем её. Если вы
  1290. помните, образец для поиска мы <<вмонтировали>> глубоко в программу, а это,
  1291. очевидно, не лучшее решение. Построим нашу программу по аналогии с \verb|grep|
  1292. из UNIXа‚ т.е. так, чтобы образец для поиска задавался первым
  1293. \index{аргументы командной строки}%
  1294. аргументом в командной строке.
  1295. \begin{LongCodePar}
  1296. #include <stdio.h>
  1297. #include <string.h>
  1298. #define MAXLINE 1000
  1299. int getline(char *line, int max);
  1300. /* find: печать строк с образцом заданным 1-м аргументом */
  1301. main(int argc, char *argv[])
  1302. {
  1303. char line[MAXLINE];
  1304. int found = 0;
  1305. if (argc != 2)
  1306. printf("Используйте в find образец\n");
  1307. else
  1308. while (getline(line, MAXLINE) > 0)
  1309. if (strstr(line, argv[1]) != NULL) {
  1310. printf("%s", line);
  1311. found++;
  1312. }
  1313. return found;
  1314. }
  1315. \end{LongCodePar}
  1316. \noindent Стандартная функция \verb|strstr(s,t)| выдаёт указатель на первый
  1317. найденный стринг \verb|t| в стринге \verb|s| или \verb|NULL|, если такого в
  1318. \verb|s| не оказалось. Её описание хранится в головном файле
  1319. \verb|<string.h>|.
  1320. Эту модель можно развивать и дальше, чтобы проиллюстрировать другие конструкции
  1321. с указателями. Предположим, что мы вводим ещё два необязательных аргумента. Один
  1322. из них предписывает печатать все строки, \emph{кроме} тех, в которых встречается
  1323. образец; второй -- перед каждой выводимой строкой печатать её порядковый номер.
  1324. По общему соглашению для Си-программ в системе UNIX знак минус перед аргументом
  1325. может иногда играть роль необязательного признака или дополнительного параметра.
  1326. Так, \verb|-x| служит признаком слова <<кроме>>, которое изменяет задание на
  1327. противоположное, а \verb|-n| указывает на потребность в нумерации строк. Тогда, например, команда
  1328. \begin{ShortCodeParWithCC}{\\\{\}}
  1329. find -x -n \textit{образец}
  1330. \end{ShortCodeParWithCC}
  1331. \noindent напечатает все строки, в которых не найден указанный образец, и,
  1332. кроме того, перед каждой строкой укажет её номер.
  1333. Необязательные аргументы разрешается располагать в любом порядке, при этом
  1334. лучше, чтобы остальная часть программы не зависела от числа представленных
  1335. аргументов. Кроме того, пользователю было бы удобно, если бы он мог
  1336. комбинировать необязательные аргументы, например, так:
  1337. \begin{ShortCodeParWithCC}{\\\{\}}
  1338. find -nx \textit{образец}
  1339. \end{ShortCodeParWithCC}
  1340. \noindent А теперь запишем нашу программу.
  1341. \begin{LongCodePar}
  1342. #include <stdio.h>
  1343. #include <string.h>
  1344. #define MAXLINE 1000
  1345. int getline(char *line, int max);
  1346. /* find: печать строк по образцу из 1-го аргумента */
  1347. main(int argc, char *argv[])
  1348. {
  1349. char line[MAXLINE];
  1350. long lineno = 0;
  1351. int c, except = 0, number = 0, found = 0;
  1352. while (--argc > 0 && (*++argv)[0] == '-')
  1353. while (c = *++argv[0])
  1354. switch (c) {
  1355. case 'x':
  1356. except = 1;
  1357. break;
  1358. case 'n':
  1359. number = 1;
  1360. break;
  1361. default:
  1362. printf("find: неверный парам. %c\n", c);
  1363. argc = 0;
  1364. found = -1;
  1365. break;
  1366. }
  1367. if (argc != 1)
  1368. printf("Используйте: find -x -n образец\n");
  1369. else
  1370. while (getline(line, MAXLINE) > 0) {
  1371. lineno++;
  1372. if ((strstr(line, *argv) != NULL) != except) {
  1373. if (number)
  1374. printf("%ld:", lineno);
  1375. printf("%s", line);
  1376. found++;
  1377. }
  1378. }
  1379. return found;
  1380. }
  1381. \end{LongCodePar}
  1382. %
  1383. % исправлена ошибка наборщика с отступами в оригинале
  1384. %
  1385. \index{указатели!арифметика с}%
  1386. Перед получением очередного аргумента \verb|argc| уменьшается на $1$, а
  1387. \verb|argv| <<продвигается>> на следующий аргумент. После завершения цикла при
  1388. отсутствии ошибок \verb|argc| содержит количество ещё не обработанных
  1389. аргументов, а \verb|argv| указывает на первый из них. Таким образом, \verb|argc|
  1390. должен быть равен $1$, а \verb|*argv| указывать на образец. Заметим, что
  1391. \verb|*++argv| является указателем на аргумент-стринг, а \verb|(*++argv)[0]| --
  1392. его первой литерой, на которую можно сослаться и другим способом:
  1393. \verb|**++argv|. Поскольку оператор индексации \verb|[]| имеет более высокий
  1394. приоритет, чем \verb|*| и \verb|++|, круглые скобки здесь обязательны, без них
  1395. запись трактовалась бы так же, как \verb|*++(argv[0])|. Именно эту запись мы
  1396. применим во внутреннем цикле, где просматриваются литеры конкретного аргумента.
  1397. Во внутреннем цикле выражение \verb|*++argv[0]| продвигает указатель
  1398. \verb|argv[0]|.
  1399. Потребность в более сложных указательных выражениях возникает не так уж часто.
  1400. Но если такое случится, то, разбивая процесс вычисления указателя на два или три
  1401. шага, вы облегчите восприятие этого выражения
  1402. \index{argv@\texttt{argv}|)}%
  1403. \index{командная строка аргументы|)}%
  1404. \index{аргументы командной строки|)}%
  1405. \paragraph{Упражнение 5.10.} Напишите программу \verb|expr|, интерпретирующую
  1406. обратную польскую запись выражения, задаваемую командной строкой, в которой
  1407. каждый оператор и операнд представлен отдельным аргументом. Например,
  1408. \begin{ShortCodePar}
  1409. expr 2 3 4 + *
  1410. \end{ShortCodePar}
  1411. \noindent вычисляется так же, как выражение $2\times(3+4)$.
  1412. \paragraph{Упражнение 5.11.} Усовершенствуйте программы \verb|entab| и
  1413. \verb|detab|~(см.~упражнения~\hyperref[parag:exc1.20]{1.20}~и~\hyperref[parag:exc1.21]{1.21}) таким образом, чтобы через аргументы
  1414. можно было задавать список <<стопов>> табуляции.
  1415. \paragraph{Упражнение 5.12.} Расширьте возможности \verb|entab| и \verb|detab|
  1416. таким образом, чтобы при обращении вида
  1417. \begin{ShortCodeParWithCC}{\\\{\}}
  1418. entab \textit{-m} \textit{+n}
  1419. \end{ShortCodeParWithCC}
  1420. \noindent <<стопы>> табуляции начинались $m$-й позиции и выполнялись через
  1421. каждые $n$ позиций. Разработайте удобный для пользователя вариант поведения
  1422. программы по умолчанию (когда нет никаких аргументов).
  1423. \paragraph{Упражнение 5.13.} Напишите программу \verb|tail|, печатающую $n$
  1424. последних введённых строк. По умолчанию значение $n$ равно $10$, но при желании
  1425. $n$ можно задать с помощью аргумента. Обращение вида
  1426. \begin{ShortCodeParWithCC}{\\\{\}}
  1427. tail -\textit{n}
  1428. \end{ShortCodeParWithCC}
  1429. \noindent печатает $n$ последних строк. Программа должна вести себя осмысленно
  1430. при любых входных данных и любом значении $n$. Напишите программу так, чтобы
  1431. наилучшим образом использовать память; запоминание строк организуйте, как в
  1432. программе сортировки, описанной в разд.~\ref{sec:arrays_of_pointers}, а не на
  1433. основе двумерного массива с фиксированным размером строки.
  1434. \section{Указатели на функции}
  1435. \label{sec:function_pointers}
  1436. \index{указатель!на функцию}%
  1437. \index{функция!указатель на}%
  1438. В Си сама функция не является переменной, но можно определить указатель на
  1439. функцию и работать с ним, как с обычной переменной: присваивать, размещать в
  1440. массиве, передавать в качестве параметра функции, возвращать как результат из
  1441. функции и т.д.
  1442. \index{программа!сортировки}%
  1443. \index{сортировка!текстовых строк}%
  1444. Для иллюстрации этих возможностей воспользуемся программой сортировки, которая
  1445. уже встречалась в настоящей главе. Изменим её так, чтобы при задании
  1446. необязательного аргумента \verb|-n| вводимые строки упорядочивались по их
  1447. числовому значению, а не в лексикографическом порядке.
  1448. Сортировка, как правило, распадается на три части: на сравнение, определяющее
  1449. упорядоченность пары объектов; перестановку, меняющую порядок пары объектов на
  1450. обратный, и сортирующий алгоритм, который осуществляет сравнения и перестановки
  1451. до тех пор, пока все объекты не будут упорядочены. Алгоритм сортировки не
  1452. зависит от операций сравнения и перестановки, так что, передавая ему различные
  1453. функции сравнения и перестановки в качестве параметров, его можно настроить на
  1454. различные критерии сортировки.
  1455. \index{лексикографическая сортировка}%
  1456. \index{сортировка!лексикографическая}%
  1457. Лексикографическое сравнение двух строк выполняется функцией \verb|strcmp| (мы
  1458. уже использовали эту функцию в ранее рассмотренной программе сортировки); нам
  1459. также потребуется программа \verb|numcmp|, сравнивающая две строки как числовые
  1460. значения и возвращающая результат сравнения в том же виде, в каком его выдаёт
  1461. \verb|strcmp|. Эти функции описываются перед \verb|main|, а указатель на одну из
  1462. них передаётся функции \verb|qsort|. Чтобы сосредоточиться на главном, мы
  1463. упростили себе задачу, отказавшись от анализа возможных ошибок при задании
  1464. аргументов.
  1465. \begin{LongCodePar}
  1466. #include <stdio.h>
  1467. #include <string.h>
  1468. #define MAXLINES 5000 /* максимальное число строк */
  1469. char *lineptr[MAXLINES]; /* указатели на строки текста */
  1470. int readlines(char *lineptr[], int nlines);
  1471. void writelines(char *lineptr[], int nlines);
  1472. void qsort(void *lineptr[], int left, int right,
  1473. int (*comp)(void *, void *));
  1474. int numcmp(char *, char *);
  1475. /* сортировка строк */
  1476. main(int argc, char *argv[])
  1477. {
  1478. int nlines; /* количество прочитанных строк */
  1479. int numeric = 0; /* 1, если сорт. по числ. знач. */
  1480. if (argc > 1 && strcmp(argv[1], "-n") == 0)
  1481. numeric = 1;
  1482. if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
  1483. qsort((void **) lineptr, 0, nlines-1,
  1484. (int (*)(void*,void*))(numeric ? numcmp : strcmp));
  1485. writelines(lineptr, nlines);
  1486. return 0;
  1487. } else {
  1488. printf("введено слишком много строк\n");
  1489. return 1;
  1490. }
  1491. }
  1492. \end{LongCodePar}
  1493. \noindent В обращениях к функциям \verb|qsort|, \verb|strcmp| и \verb|numcmp| их
  1494. имена трактуются как адреса этих функций. Поэтому оператор \verb|&| перед ними
  1495. не нужен, как он был не нужен и перед именем массива.
  1496. \index{функция!qsort@\texttt{qsort}}%
  1497. Мы написали \verb|qsort| так, чтобы она могла обрабатывать данные любого типа, а
  1498. не только стринги литер. Как видно из прототипа, функция \verb|qsort| в качестве
  1499. своих аргументов ожидает массив ссылок, два целых значения и функцию с двумя
  1500. аргументами-указателями.
  1501. \index{указатель!void *@\texttt{void *}|(}%
  1502. \index{void *, указатель@\texttt{void *}, указатель|(}%
  1503. В качестве указателей-аргументов заданы указатели обобщённого типа
  1504. \verb|void *|. Любой указатель можно привести к типу \verb|void *| и обратно без
  1505. потери информации. Поэтому мы можем обратиться к \verb|qsort|, предварительно
  1506. преобразовав аргументы в \verb|void *|. Внутри функции сравнения её аргументы
  1507. будут приведены к нужному ей типу. На самом деле эти преобразования никакого
  1508. влияния на представления аргументов не оказывают, они лишь обеспечивают
  1509. согласованность типов для компилятора.
  1510. \begin{LongCodePar}
  1511. /* qsort: сортирует v[left]...v[right] по возрастанию */
  1512. void qsort(void *v[], int left, int right,
  1513. int (*comp)(void *, void *))
  1514. {
  1515. int i, last;
  1516. void swap(void *v[], int, int);
  1517. if (left >= right) /* ничего не делается, если */
  1518. return; /* в массиве менее двух элементов */
  1519. swap(v, left, (left + right)/2);
  1520. last = left;
  1521. for (i = left+1; i <= right; i++)
  1522. if ((*comp)(v[i], v[left]) < 0)
  1523. swap(v, ++last, i);
  1524. swap(v, left, last);
  1525. qsort(v, left, last-1, comp);
  1526. qsort(v, last+1, right, comp);
  1527. }
  1528. \end{LongCodePar}
  1529. \noindent%
  1530. \index{прототип функции}%
  1531. \index{указатель!void *@\texttt{void *}}%
  1532. \index{функция!прототип}%
  1533. \index{void *, указатель@\texttt{void *}, указатель}%
  1534. Повнимательней приглядимся к декларациям. Четвёртый параметр функции
  1535. \verb|qsort|:
  1536. \begin{ShortCodePar}
  1537. int (*comp)(void *, void *)
  1538. \end{ShortCodePar}
  1539. \noindent сообщает, что \verb|comp| есть указатель на функцию, которая имеет два
  1540. аргумента-указателя и выдаёт результат типа \verb|int|.
  1541. Использование \verb|comp| в строке
  1542. \begin{ShortCodePar}
  1543. if ((*comp)(v[i], v[left]) < 0)
  1544. \end{ShortCodePar}
  1545. \noindent согласуется с декларацией <<\verb|comp| -- это указатель на
  1546. функцию>>, и, следовательно, \verb|*comp| есть функция, а
  1547. \begin{ShortCodePar}
  1548. (*comp)(v[i], v[left])
  1549. \end{ShortCodePar}
  1550. \noindent обращение к ней. Скобки здесь нужны, чтобы обеспечить правильную
  1551. трактовку декларации; без них декларация
  1552. \begin{ShortCodePar}
  1553. int *comp(void *, void *) /* НЕВЕРНО */
  1554. \end{ShortCodePar}
  1555. \noindent описывала бы \verb|comp|, как функцию, возвращающую ссылку на
  1556. \verb|int|, а это совсем не то, что требуется.
  1557. \index{void *, указатель@\texttt{void *}, указатель|)}%
  1558. \index{указатель!void *@\texttt{void *}|)}%
  1559. Мы уже рассматривали функцию \verb|strcmp|, сравнивающую два стринга.
  1560. \index{функция!numcmp@\texttt{numcmp}}%
  1561. Ниже приведена функция \verb|numcmp|, которая сравнивает два стринга,
  1562. рассматривая их как числа; предварительно они переводятся в числовые значения
  1563. функцией \verb|atof|.
  1564. \begin{LongCodePar}
  1565. #include <stdlib.h>
  1566. /* numcmp: сравнивает s1 и s2 как числа */
  1567. int numcmp(char *s1, char *s2)
  1568. {
  1569. double v1, v2;
  1570. v1 = atof(s1);
  1571. v2 = atof(s2);
  1572. if (v1 < v2)
  1573. return -1;
  1574. else if (v1 > v2)
  1575. return 1;
  1576. else
  1577. return 0;
  1578. }
  1579. \end{LongCodePar}
  1580. Программу сортировки можно пополнить и другими возможностями; реализовать
  1581. некоторые из них предлагается в качестве упражнений.
  1582. \paragraph{Упражнение 5.14.} Модифицируйте программу сортировки, чтобы она
  1583. реагировала на параметр \verb|-r|, указывающий, что объекты нужно сортировать в
  1584. обратном порядке, т.е. в порядке убывания. Обеспечьте, чтобы \verb|-r| работал и
  1585. вместе с \verb|-n|.
  1586. \paragraph{Упражнение 5.15.} Введите в программу необязательный параметр
  1587. \verb|-f|‚ задание которого делало бы неразличимыми литеры нижнего и верхнего
  1588. регистров (например, \verb|a| и \verb|A| должны оказаться при сравнении
  1589. равными).
  1590. \paragraph{Упражнение 5.16.} Предусмотрите в программе необязательный параметр
  1591. \verb|-d|‚ который заставит программу при сравнении учитывать только буквы,
  1592. цифры и пробелы. Организуйте программу таким образом, чтобы этот параметр мог
  1593. работать вместе с параметром \verb|-f|.
  1594. \paragraph{Упражнение 5.17.} Реализуйте в программе возможность работы с полями:
  1595. возможность сортировки по полям внутри строк. Для каждого поля предусмотрите
  1596. свой набор параметров. Предметный указатель этой книги\footnote{Здесь имеется в
  1597. виду оригинал книги на английском языке. -- \textit{Примеч. пер.}}
  1598. упорядочивался с параметрами: \verb|-df| для терминов и \verb|-n| для номеров
  1599. страниц.
  1600. \section{Сложные декларации}
  1601. \label{sec:complex_declarations}
  1602. Иногда Си ругают за синтаксис деклараций, особенно тех, которые содержат в себе
  1603. указатели на функции. Таким синтаксис получился в результате нашей попытки
  1604. сделать похожими записи объектов при их описании и использовании. В простых
  1605. случаях этот синтаксис хорош, однако в сложных ситуациях он вызывает
  1606. затруднения, поскольку декларации перенасыщены скобками и их невозможно читать
  1607. слева направо. Проблему иллюстрирует различие следующих двух деклараций:
  1608. \begin{ShortCodePar}
  1609. int *f(); /* f: фу-ция, возвращающая ук-ль на int */
  1610. int (*pf)(); /* pf: ук-ль на ф-цию, возвращающую int */
  1611. \end{ShortCodePar}
  1612. \noindent Приоритет префиксного оператора \verb|*| ниже, чем приоритет
  1613. \verb|()|, поэтому во втором случае скобки необходимы.
  1614. Хотя на практике по-настоящему сложные декларации встречаются редко, всё же
  1615. важно знать‚ как их понимать, а если потребуется, и как их конструировать.
  1616. Укажем хороший способ: декларации можно синтезировать, двигаясь небольшими
  1617. шагами с помощью \verb|typedef|; этот способ рассмотрен в
  1618. разд.~\ref{sec:typedef}. В настоящем разделе на примере двух программ,
  1619. осуществляющих преобразования правильных Си-деклараций в соответствующие им
  1620. словесные описания и обратно, мы демонстрируем иной способ конструирования
  1621. деклараций. Словесное описание читается слева направо.
  1622. \index{программа!dcl@\texttt{dcl}}%
  1623. Первая программа, \verb|dcl|, -- более сложная. Она преобразует Си-декларации в
  1624. словесные описания так, как показано в следующих примерах:
  1625. \begin{LongCodePar}
  1626. char **argv
  1627. argv: указ. на указ. на char
  1628. int (*daytab)[13]
  1629. daytab: указ. на массив[13] из int
  1630. void *comp()
  1631. comp: функц. возвр. указ. на void
  1632. void (*comp)()
  1633. comp: указ. на функц. возвр. void
  1634. char (*(*x())[])()
  1635. x: функц. возвр. указ. на массив[] из указ. на функц.
  1636. возвр. char
  1637. char (*(*x[3])())[5]
  1638. x: массив[3] из указ. на функц. возвр. указ.
  1639. на массив[5] из char
  1640. \end{LongCodePar}
  1641. Функция \verb|dcl| в своей работе использует грамматику, специфицирующую
  1642. декларатор. Эта грамматика строго изложена в разд.~\ref{apx:subsec:declarators}
  1643. приложения~\ref{apx:ref_manual}, а в упрощённом виде записывается так:
  1644. \begin{ShortCodeParWithCC}{\\\{\}}
  1645. \textit{dcl:} \textit{optional} *\textit{'s} \textit{direct-dcl}
  1646. \textit{direct-dcl:} \textit{name}
  1647. (\textit{dcl})
  1648. \textit{direct-dcl}()
  1649. \textit{direct-dcl}[\textit{optional size}]
  1650. \end{ShortCodeParWithCC}
  1651. \noindent Говоря простым языком, \textit{dcl} есть \textit{direct-dcl}, перед
  1652. которым может стоять \verb|*|\textit{'s} (т.е. одна или несколько звёздочек),
  1653. где \textit{direct-dcl} есть \textit{name} (имя), или \textit{dcl} в скобках, или
  1654. \textit{direct-dcl} с последующей парой скобок, или \textit{direct-dcl} с
  1655. последующей парой квадратных скобок, внутри которых может быть помещён размер
  1656. (\textit{size}).
  1657. Эту грамматику можно использовать для грамматического разбора деклараций.
  1658. Рассмотрим, например, такой декларатор:
  1659. \begin{ShortCodePar}
  1660. (*pfa[])()
  1661. \end{ShortCodePar}
  1662. \noindent Имя \verb|pfa| будет классифицировано как \textit{name} и,
  1663. следовательно, как \textit{direct-dcl}. Затем \verb|pfa[]| будет распознано как
  1664. \textit{direct-dcl}, а \verb|*pfa[]| -- как \textit{dcl} и, следовательно,
  1665. \verb|(*pfa[])| есть \textit{direct-dcl}. Далее \verb|(*pfa[])()| есть
  1666. \textit{direct-dcl} и, таким образом, \textit{dcl}. Этот грамматический разбор
  1667. можно проиллюстрировать
  1668. \index{дерево!разбора}%
  1669. деревом разбора (здесь \textit{direct-dcl} обозначено более коротко, а именно
  1670. \textit{dir-dcl}):
  1671. \begin{figure}[H]
  1672. \center{\includegraphics[width=0.7627119\linewidth]{chapt5_sec12_img0.eps}}
  1673. \end{figure}
  1674. %
  1675. % TODO: проверить правильность синтаксического дерева
  1676. %
  1677. \index{грамматический разбор методом рекурсивного спуска}%
  1678. \index{функция!dcl@\texttt{dcl}}%
  1679. \index{функция!dirdcl@\texttt{dirdcl}}%
  1680. Сердцевиной программы обработки декларатора является пара функций \verb|dcl| и
  1681. \verb|dirdcl|‚ осуществляющих грамматический разбор декларации согласно
  1682. приведённой грамматике. Поскольку грамматика определена рекурсивно, эти функции
  1683. обращаются друг к другу рекурсивно, по мере распознавания отдельных частей
  1684. декларации.
  1685. \index{рекурсивный спуск в грамматическом разборе}%
  1686. Метод, применённый в обсуждаемой программе для грамматического
  1687. разбора, называется рекурсивным спуском.
  1688. \begin{LongCodePar}
  1689. /* dcl: разбор декларатора */
  1690. void dcl(void)
  1691. {
  1692. int ns;
  1693. for (ns = 0; gettoken() == '*'; ) /* подсчёт звёздочек */
  1694. ns++;
  1695. dirdcl();
  1696. while (ns-- > 0)
  1697. strcat(out, " указ. на");
  1698. }
  1699. /* dirdcl: разбор непосредственного декларатора */
  1700. void dirdcl(void)
  1701. {
  1702. int type;
  1703. if (tokentype == '(') { /* ( dcl ) */
  1704. dcl();
  1705. if (tokentype != ')')
  1706. printf("ошибка: пропущена )\n");
  1707. } else if (tokentype == NAME) /* имя переменной */
  1708. strcpy(name, token);
  1709. else
  1710. printf("ошибка: должно быть name или (dcl)\n");
  1711. while ((type=gettoken()) == PARENS || type == BRACKETS)
  1712. if (type == PARENS)
  1713. strcat(out, " функц. возвр.");
  1714. else {
  1715. strcat(out, " массив");
  1716. strcat(out, token);
  1717. strcat(out, " из");
  1718. }
  1719. }
  1720. \end{LongCodePar}
  1721. Приведённые программы служат только иллюстративным целям и не вполне надёжны.
  1722. Что касается \verb|dcl|, то её возможности существенно ограничены. Она может
  1723. работать только с простыми типами вроде \verb|char| и \verb|int| и не
  1724. справляется с типами аргументов в функциях и с квалификаторами подобными
  1725. \verb|const|. Лишние пробелы для неё опасны. Она не предпринимает никаких мер по
  1726. выходу из ошибочной ситуации, и поэтому неправильные декларации также
  1727. противопоказаны ей. Устранение этих недостатков мы оставляем до упражнений.
  1728. Ниже приведены глобальные переменные и главная программа.
  1729. \begin{LongCodePar}
  1730. #include <stdio.h>
  1731. #include <string.h>
  1732. #include <ctype.h>
  1733. #define MAXTOKEN 100
  1734. enum { NAME, PARENS, BRACKETS };
  1735. void dcl(void);
  1736. void dirdcl(void);
  1737. int gettoken(void);
  1738. int tokentype; /* тип последней лексемы */
  1739. char token[MAXTOKEN]; /* текст последней лексемы */
  1740. char name[MAXTOKEN]; /* имя */
  1741. char datatype[MAXTOKEN]; /* тип = char, int и т.д. */
  1742. char out[1000]; /* выдаваемый текст */
  1743. main() /* преобразование декларации в словесное описание */
  1744. {
  1745. while (gettoken() != EOF) { /* 1-я лексема в строке */
  1746. strcpy(datatype, token); /* это тип данных */
  1747. out[0] = '\0';
  1748. dcl(); /* разбор остальной части строки */
  1749. if (tokentype != '\n')
  1750. printf("синтаксическая ошибка\n");
  1751. printf("%s: %s %s\n", name, out, datatype);
  1752. }
  1753. return 0;
  1754. }
  1755. \end{LongCodePar}
  1756. \index{функция!gettoken@\texttt{gettoken}}%
  1757. Функция \verb|gettoken| пропускает пробелы и табуляции и затем получает
  1758. следующую лексему из ввода; <<лексема>> -- это имя, или пара круглых скобок,
  1759. или пара квадратных скобок (быть может, с помещённым в них числом), или любая
  1760. другая единичная литера.
  1761. \begin{LongCodePar}
  1762. int gettoken(void) /* возвращает следующую лексему */
  1763. {
  1764. int c, getch(void);
  1765. void ungetch(int);
  1766. char *p = token;
  1767. while ((c = getch()) == ' ' || c == '\t')
  1768. ;
  1769. if (c == '(') {
  1770. if ((c = getch()) == ')') {
  1771. strcpy(token, "()");
  1772. return tokentype = PARENS;
  1773. } else {
  1774. ungetch(c);
  1775. return tokentype = '(';
  1776. }
  1777. } else if (c == '[') {
  1778. for (*p++ = c; (*p++ = getch()) != ']'; )
  1779. ;
  1780. *p = '\0';
  1781. return tokentype = BRACKETS;
  1782. } else if (isalpha(c)) {
  1783. for (*p++ = c; isalnum(c = getch()); )
  1784. *p++ = c;
  1785. *p = '\0';
  1786. ungetch(c);
  1787. return tokentype = NAME;
  1788. } else
  1789. return tokentype = c;
  1790. }
  1791. \end{LongCodePar}
  1792. \noindent Функции \verb|getch| и \verb|ungetch| были рассмотрены в
  1793. гл.~\ref{chapt:functions_and_program_structure}.
  1794. Обратное преобразование реализуется легче, особенно если не придавать значения
  1795. тому, что будут генерироваться лишние скобки.
  1796. \index{программа!undcl@\texttt{undcl}}%
  1797. Программа \verb|undcl| превращает фразу типа <<\verb|x| есть функция,
  1798. возвращающая указатель на массив указателей на функции, возвращающие
  1799. \verb|char|>>, которую мы будем представлять в виде
  1800. \begin{ShortCodePar}
  1801. x () * [] () char
  1802. \end{ShortCodePar}
  1803. \noindent в декларацию
  1804. \begin{ShortCodePar}
  1805. char (*(*x())[])()
  1806. \end{ShortCodePar}
  1807. \noindent Сокращённая запись словесного описания позволяет воспользоваться
  1808. функцией \verb|gettoken|. Функция \verb|undcl| использует те же самые внешние
  1809. переменные, что и \verb|dcl|.
  1810. \begin{LongCodePar}
  1811. /* undcl: преобразует словесное описание в декларацию */
  1812. main()
  1813. {
  1814. int type;
  1815. char temp[MAXTOKEN];
  1816. while (gettoken() != EOF) {
  1817. strcpy(out, token);
  1818. while ((type = gettoken()) != '\n')
  1819. if (type == PARENS || type == BRACKETS)
  1820. strcat(out, token);
  1821. else if (type == '*') {
  1822. sprintf(temp, "(*%s)", out);
  1823. strcpy(out, temp);
  1824. } else if (type == NAME) {
  1825. sprintf(temp, "%s %s", token, out);
  1826. strcpy(out, temp);
  1827. } else
  1828. printf("неверный элемент %s в фразе\n",token);
  1829. printf("%s\n", out);
  1830. }
  1831. return 0;
  1832. }
  1833. \end{LongCodePar}
  1834. %
  1835. % исправлена опечатка
  1836. % отсутствовали ;
  1837. %
  1838. % в целях выравнивания
  1839. % строка "printf("неверный элемент %s в фразе\n", token);"
  1840. % заменена на "printf("неверный элемент %s в фразе\n",token);",
  1841. % что не влияет на смысл кода
  1842. %
  1843. \paragraph{Упражнение 5.18.} Видоизмените \verb|dcl| таким образом, чтобы она
  1844. нормально обрабатывала ошибки во входной информации.
  1845. \paragraph{Упражнение 5.19.} Модифицируйте \verb|undcl| так, чтобы она не
  1846. генерировала лишних скобок.
  1847. \paragraph{Упражнение 5.20.} Расширьте возможности \verb|dcl|: функция должна
  1848. справляться с описателями типов её аргументов, квалификаторами \verb|const| и
  1849. т.д.