chapter6.tex 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599
  1. \chapter{Структуры}
  2. \label{chapt:structures}
  3. Структура -- это одна или несколько переменных (возможно, различных типов),
  4. которые для удобства работы с ними сгруппированы под одним именем. (В некоторых
  5. языках, в частности в Паскале, структуры называются записями.) Структуры
  6. помогают в организации сложных данных (особенно в больших программах), поскольку
  7. позволяют группу связанных между собой переменных трактовать не как множество
  8. отдельных элементов, а как единое целое.
  9. Распространённый пример структуры -- строка платёжной ведомости. Она содержит
  10. такие сведения о служащем, как его полное имя, адрес, номер карточки социального
  11. страхования, зарплата и т.д. Некоторые из этих характеристик сами могут быть
  12. структурами: например, полное имя состоит из нескольких компонент (фамилии‚
  13. имени и отчества); аналогично адрес, и даже зарплата. Другой пример (более
  14. типичный для Си) -- из области графики: точка есть пара координат,
  15. прямоугольник есть пара точек и т.д.
  16. Главные изменения, внесённые стандартом ANSI в отношении структур, -- это
  17. введение для них операции присваивания. Структуры могут копироваться, над ними
  18. могут выполняться операции присваивания, их можно передавать функциям в
  19. качестве аргументов, а функции могут возвращать их в качестве результатов. В
  20. большинстве компиляторов уже давно реализованы эти возможности, но теперь они
  21. точно оговорены стандартом. Для автоматических структур и массивов теперь также
  22. допускается инициализация.
  23. \section{Основные сведения о структурах}
  24. Сконструируем несколько графических структур. В качестве основного объекта
  25. выступает точка с координатами $x$ и $y$, и пусть они имеют тип \verb|int|.
  26. \begin{figure}[H]
  27. \center{\includegraphics[width=0.3841808\linewidth]{chapt6_sec1_img0.eps}}
  28. \end{figure}
  29. \noindent%
  30. \index{декларация!структуры}%
  31. \index{структура!декларация}%
  32. Указанные две компоненты можно поместить в структуру, описанную, например,
  33. следующим образом:
  34. \begin{ShortCodePar}
  35. struct point {
  36. int x;
  37. int y;
  38. };
  39. \end{ShortCodePar}
  40. Описание структуры начинается с ключевого слова \verb|struct| и содержит список
  41. деклараций, заключённый в фигурные скобки.
  42. \index{структура!тег}%
  43. \index{тег!структуры}%
  44. За словом \verb|struct| может следовать имя, называемое
  45. \emph{тегом}\footnote{От английского слова tag -- ярлык, этикетка. --
  46. \textit{Примеч. пер.}} \emph{структуры} (\verb|point| в нашем случае). Тег даёт
  47. название структуре данного вида и далее может служить кратким обозначением той
  48. части декларации, которая заключена в фигурные скобки.
  49. \index{структура!имя члена}%
  50. \index{член структуры, имя}%
  51. Перечисленные в структуре переменные называются \emph{членами}. Имена членов и
  52. тегов без каких-либо коллизий могут совпадать с именами обычных переменных (т.е.
  53. не членов), так как они всегда различимы по контексту. Более того, одни и те же
  54. имена членов могут встречаться в разных структурах, хотя, если следовать
  55. хорошему стилю программирования, лучше одинаковые имена давать только близким по
  56. смыслу объектам.
  57. \index{декларация!структуры}%
  58. \index{структура!декларация}%
  59. Декларация структуры -- это тип. За правой фигурной скобкой, закрывающей список
  60. членов, могут следовать переменные точно так же, как они могут быть указаны
  61. после названия любого базового типа. Таким образом, запись
  62. \begin{ShortCodePar}
  63. struct { ... } x, y, z;
  64. \end{ShortCodePar}
  65. \noindent с точки зрения синтаксиса аналогична записи
  66. \begin{ShortCodePar}
  67. int x, y, z;
  68. \end{ShortCodePar}
  69. \noindent в том смысле, что каждая декларирует \verb|x|, \verb|y| и \verb|z| как
  70. переменные указанного типа. Обе записи приведут к тому, что где-то будет
  71. выделена память соответствующего размера.
  72. \index{декларация!структуры}%
  73. \index{структура!декларация}%
  74. Декларация структуры, не содержащей списка переменных, не резервирует памяти:
  75. она просто описывает шаблон, или образец структуры. Однако если структура имеет
  76. тег, то этим тегом далее можно пользоваться при определении структурных
  77. объектов. Например, с помощью заданной выше декларации структуры
  78. \verb|point| строка
  79. \begin{ShortCodePar}
  80. struct point pt;
  81. \end{ShortCodePar}
  82. \noindent определяет структурную переменную \verb|pt| типа \verb|struct point|.
  83. \index{инициализация!структуры}%
  84. \index{структура!инициализация}%
  85. \index{фигурные скобки}%
  86. Структурную переменную при её определении можно инициализировать, формируя
  87. список инициализаторов её членов в виде константных выражений:
  88. \begin{ShortCodePar}
  89. struct point maxpt = { 320, 200 };
  90. \end{ShortCodePar}
  91. \noindent Инициализировать автоматические структуры можно также присваиванием
  92. или обращением к функции, возвращающей результат в виде структуры
  93. соответствующего типа.
  94. \index{оператор!доступа к члену структуры!точка@\texttt{.} (точка)}%
  95. \index{структура!оператор доступа к её члену!\texttt{.} (точка)}%
  96. Доступ к отдельному члену структуры осуществляется посредством конструкции вида:
  97. \begin{ShortCodeParWithCC}{\\\{\}}
  98. \textit{имя-структуры} . \textit{член}
  99. \end{ShortCodeParWithCC}
  100. \noindent Оператор доступа к члену структуры <<\verb|.|>> соединяет имя
  101. структуры и имя члена. Чтобы напечатать, например, координаты точки \verb|pt|,
  102. годится следующее обращение к \verb|printf|:
  103. \begin{ShortCodePar}
  104. printf("%d,%d", pt.x, pt.y);
  105. \end{ShortCodePar}
  106. \noindent Другой пример: чтобы вычислить расстояние от начала координат $(0, 0)$
  107. до \verb|pt|, можно написать
  108. \begin{ShortCodePar}
  109. double dist, sqrt(double);
  110. dist = sqrt((double)pt.x * pt.x + (double)pt.y * pt.y);
  111. \end{ShortCodePar}
  112. \index{структура!вложенная}%
  113. Структуры могут быть вложены друг в друга. Одно из возможных представлений
  114. прямоугольника -- это пара точек на углах одной из его диагоналей:
  115. \begin{figure}[H]
  116. \center{\includegraphics[width=0.5056180\linewidth]{chapt6_sec1_img1.eps}}
  117. \end{figure}
  118. \begin{ShortCodePar}
  119. struct rect {
  120. struct point pt1;
  121. struct point pt2;
  122. };
  123. \end{ShortCodePar}
  124. \noindent Структура \verb|rect| содержит две структуры \verb|point|. Если мы
  125. декларируем \verb|screen| как
  126. \begin{ShortCodePar}
  127. struct rect screen;
  128. \end{ShortCodePar}
  129. \noindent то
  130. \begin{ShortCodePar}
  131. screen.pt1.x
  132. \end{ShortCodePar}
  133. \noindent ссылается на координату $x$ точки \verb|pt1| из \verb|screen|.
  134. \section{Структуры и функции}
  135. Единственно возможные операции над структурами -- это их копирование,
  136. присваивание, взятие адреса с помощью \verb|&| и осуществление доступа к её
  137. членам. Передача структур функциям в качестве аргументов и возврат их от
  138. функций в виде результата также относятся к операциям копирования и
  139. присваивания. Структуры нельзя сравнивать. Инициализировать структуру можно
  140. списком константных значений её членов; автоматическую структуру можно
  141. инициализировать также присваиванием.
  142. Чтобы лучше познакомиться со структурами, напишем несколько функций,
  143. манипулирующих точками и прямоугольниками. Возникает вопрос: а как передавать
  144. функциям названные объекты? Существует по крайней мере три подхода: передавать
  145. компоненты по отдельности, передавать всю структуру целиком и передавать
  146. указатель на структуру. Каждый подход имеет свои плюсы и минусы.
  147. \index{функция!makepoint@\texttt{makepoint}}%
  148. Первая функция, \verb|makepoint|, получает два целых значения и возвращает
  149. структуру \verb|point|.
  150. \begin{LongCodePar}
  151. /* makepoint: формирует точку по компонентам x и y */
  152. struct point makepoint(int x, int y)
  153. {
  154. struct point temp;
  155. temp.x = x;
  156. temp.y = y;
  157. return temp;
  158. }
  159. \end{LongCodePar}
  160. \noindent Заметим: никакой путаницы из-за того, что имя аргумента совпадает с
  161. именем члена структуры не возникает; более того, одно и то же имя подчёркивает
  162. родство обозначаемых им объектов.
  163. Теперь с помощью \verb|makepoint| можно выполнять динамическую инициализацию
  164. любой структуры или формировать структурные аргументы для той или иной функции:
  165. \begin{ShortCodePar}
  166. struct rect screen;
  167. struct point middle;
  168. struct point makepoint(int, int);
  169. screen.pt1 = makepoint(0, 0);
  170. screen.pt2 = makepoint(XMAX, YMAX);
  171. middle = makepoint((screen.pt1.x + screen.pt2.x)/2,
  172. (screen.pt1.y + screen.pt2.y)/2);
  173. \end{ShortCodePar}
  174. \index{функция!addpoint@\texttt{addpoint}}%
  175. Нам может понадобиться ряд функций, реализующих различные операции над точками.
  176. В качестве примера рассмотрим следующую функцию:
  177. \begin{ShortCodePar}
  178. /* addpoint: сложение двух точек */
  179. struct point addpoint(struct point p1, struct point p2)
  180. {
  181. p1.x += p2.x;
  182. p1.y += p2.y;
  183. return p1;
  184. }
  185. \end{ShortCodePar}
  186. \noindent Здесь оба аргумента и возвращаемое значение -- структуры. Мы
  187. увеличиваем компоненты прямо в \verb|p1| и не используем для этого временной
  188. переменной, чтобы подчеркнуть, что структурные параметры передаются по значению
  189. так же, как и любые другие.
  190. \index{функция!ptinrect@\texttt{ptinrect}}%
  191. В качестве другого примера приведём функцию \verb|ptinrect|, которая проверяет:
  192. находится ли точка внутри прямоугольника, относительно которого мы принимаем
  193. соглашение, что в него входят его левая и нижняя стороны, но не входят верхняя и
  194. правая.
  195. \begin{ShortCodePar}
  196. /* ptinrect: возвращает 1, если p в r, и 0 в прот. случае */
  197. int ptinrect(struct point p, struct rect r)
  198. {
  199. return p.x >= r.pt1.x && p.x < r.pt2.x
  200. && p.y >= r.pt1.y && p.y < r.pt2.y;
  201. }
  202. \end{ShortCodePar}
  203. \noindent Здесь предполагается, что прямоугольник представлен в стандартном
  204. виде, т.е. координаты точки \verb|pt1| меньше соответствующих координат точки
  205. \verb|pt2|.
  206. \index{функция!canonrect@\texttt{canonrect}}%
  207. Следующая функция гарантирует получение прямоугольника в
  208. каноническом виде.
  209. \begin{LongCodePar}
  210. #define min(a, b) ((a) < (b) ? (a) : (b))
  211. #define max(a, b) ((a) > (b) ? (a) : (b))
  212. /* canonrect: канонизация координат прямоугольника */
  213. struct rect canonrect(struct rect r)
  214. {
  215. struct rect temp;
  216. temp.pt1.x = min(r.pt1.x, r.pt2.x);
  217. temp.pt1.y = min(r.pt1.y, r.pt2.y);
  218. temp.pt2.x = max(r.pt1.x, r.pt2.x);
  219. temp.pt2.y = max(r.pt1.y, r.pt2.y);
  220. return temp;
  221. }
  222. \end{LongCodePar}
  223. Если функции передаётся большая структура, то, чем копировать её целиком,
  224. эффективнее передать указатель на неё. Указатели на структуры ничем не
  225. отличаются от указателей на обычные переменные. Декларация
  226. \begin{ShortCodePar}
  227. struct point *pp;
  228. \end{ShortCodePar}
  229. \noindent сообщает, что \verb|pp| есть указатель на структуру типа
  230. \verb|struct point|. Если \verb|pp| ссылается на структуру \verb|point|, то
  231. \verb|*pp| есть сама структура, а \verb|(*pp).x| и \verb|(*pp).y| -- её члены.
  232. Используя указатель \verb|pp|, мы могли бы написать
  233. \begin{ShortCodePar}
  234. struct point origin, *pp;
  235. pp = &origin;
  236. printf("origin: (%d,%d)\n", (*pp).x, (*pp).y);
  237. \end{ShortCodePar}
  238. \noindent%
  239. \index{оператор!приоритет}%
  240. Скобки в \verb|(*pp).x| необходимы поскольку приоритет оператора \verb|.| выше,
  241. чем приоритет \verb|*|. Выражение \verb|*pp.x| будет проинтерпретировано как
  242. \verb|*(pp.x)|‚ что неверно, поскольку \verb|pp.x| не является указателем.
  243. \index{оператор!доступа к члену структуры!через указатель \texttt{\textminus\textgreater}}%
  244. \index{структура!оператор доступа к её члену!через указатель \texttt{\textminus\textgreater}}%
  245. Указатели на структуры используются весьма часто, поэтому для доступа к её
  246. членам была придумана ещё одна, более короткая форма записи. Если \verb|p| --
  247. указатель на структуру, то
  248. \begin{ShortCodeParWithCC}{\\\{\}}
  249. p->\textit{член-структуры}
  250. \end{ShortCodeParWithCC}
  251. \noindent есть её отдельный член. (Оператор \verb|->| состоит из знака
  252. \verb|-|, за которым сразу следует знак \verb|>|.) Поэтому \verb|printf| можно
  253. переписать в виде
  254. \begin{ShortCodePar}
  255. printf("origin: (%d,%d)\n", pp->x, pp->y);
  256. \end{ShortCodePar}
  257. \index{оператор!приоритет}%
  258. \index{приоритеты операторов}%
  259. Оба оператора \verb|.| и \verb|->| выполняются слева направо. Таким образом, при
  260. наличии декларации
  261. \begin{ShortCodePar}
  262. struct rect r, *rp = r;
  263. \end{ShortCodePar}
  264. \noindent следующие четыре выражения будут эквивалентны:
  265. \begin{ShortCodePar}
  266. r.pt1.x
  267. rp->pt1.x
  268. (r.pt1).x
  269. (rp->pt1).x
  270. \end{ShortCodePar}
  271. \index{оператор!приоритет}%
  272. \index{приоритеты операторов}%
  273. Операторы доступа к членам структуры \verb|.| и \verb|->| вместе с операторами
  274. вызова функции \verb|()| и индексации массива \verb|[]| занимают самое высокое
  275. положение в иерархии приоритетов и выполняются раньше любых других операторов.
  276. Например, если задана декларация
  277. \begin{ShortCodePar}
  278. struct {
  279. int len;
  280. char *str;
  281. } *p;
  282. \end{ShortCodePar}
  283. \noindent то
  284. \begin{ShortCodePar}
  285. ++p->len
  286. \end{ShortCodePar}
  287. \noindent увеличит на $1$ значение члена структуры \verb|len|, а не указатель
  288. \verb|p|, поскольку в этом выражении как бы неявно присутствуют скобки:
  289. \verb|++(p->len)|. Чтобы изменить порядок выполнения операций, нужны явные
  290. скобки. Так, в \verb|(++p)->len|, прежде чем взять значение \verb|len|,
  291. программа
  292. %
  293. % в оригинале слева внизу страницы ``5. Заказ № 13''
  294. %
  295. продвинет указатель \verb|p|. В \verb|(p++)->len| указатель \verb|p| увеличится
  296. после того, как будет взято значение \verb|len| (в последнем случае скобки не
  297. обязательны).
  298. \index{оператор!приоритет}%
  299. \index{приоритеты операторов}%
  300. По тем же правилам \verb|*p->str| обозначает содержимое объекта, на который
  301. ссылается \verb|str|; \verb|*p->str++| продвинет указатель \verb|str| после
  302. получения значения объекта, на который он указывал (как и в выражении вида
  303. \verb|*s++|); \verb|(*p->str)++| увеличит значение объекта, на который ссылается
  304. \verb|str|; \verb|*p++->str| продвинет \verb|p| после того, как будет получено
  305. то, на что указывает \verb|str|.
  306. \section{Массивы структур}
  307. \index{массив!структур}%
  308. \index{программа!подсчёта!ключевых слов}%
  309. Рассмотрим программу, определяющую число вхождений каждого ключевого слова в
  310. текст Си-программы. Нам нужно уметь хранить ключевые слова в виде массива
  311. стрингов и счётчики ключевых слов в виде массива целых. Один из возможных
  312. вариантов -- это иметь два параллельных массива:
  313. \begin{ShortCodePar}
  314. char *keyword[NKEYS];
  315. int keycount[NKEYS];
  316. \end{ShortCodePar}
  317. \noindent Однако именно тот факт, что они параллельны, подсказывает нам другую
  318. организацию хранения -- через массив структур. Каждое ключевое слово можно
  319. описать парой характеристик
  320. \begin{ShortCodePar}
  321. char *word;
  322. int count;
  323. \end{ShortCodePar}
  324. \noindent Такие пары составляют массив. Декларация
  325. \begin{ShortCodePar}
  326. struct key {
  327. char *word;
  328. int count;
  329. } keytab[NKEYS];
  330. \end{ShortCodePar}
  331. \noindent описывает структуру типа \verb|key| и определяет массив \verb|keytab|,
  332. каждый элемент которого есть структура этого типа и которому где-то будет
  333. выделена память. Это же можно записать и по-другому:
  334. \begin{ShortCodePar}
  335. struct key {
  336. char *word;
  337. int count;
  338. };
  339. struct key keytab[NKEYS];
  340. \end{ShortCodePar}
  341. Так как \verb|keytab| содержит постоянный набор имён, его легче всего сделать
  342. внешним массивом и инициализировать один раз в момент определения.
  343. \index{массив!структур!инициализация}%
  344. \index{инициализация!массивов структур}%
  345. Инициализация структур аналогична ранее демонстрировавшимся инициализациям -- за
  346. определением следует список инициализаторов, заключённый в фигурные скобки:
  347. \begin{LongCodePar}
  348. struct key {
  349. char *word;
  350. int count;
  351. } keytab[] = {
  352. "auto", 0,
  353. "break", 0,
  354. /* ... */
  355. "while", 0
  356. };
  357. \end{LongCodePar}
  358. \noindent%
  359. \index{массив!структур!инициализация}%
  360. \index{инициализация!массивов структур}%
  361. Инициализаторы задаются парами, чтобы соответствовать конфигурации структуры.
  362. Строго говоря, пару инициализаторов для каждой отдельной структуры следовало бы
  363. заключить в фигурные скобки, как, например, в
  364. \begin{ShortCodePar}
  365. { "auto", 0 },
  366. { "break", 0 },
  367. ...
  368. \end{ShortCodePar}
  369. \noindent Однако, когда инициализаторы -- простые константы или цепочки литер,
  370. и все они имеются в наличии, во внутренних скобках нет необходимости.
  371. \index{массив!размер по умолчанию}%
  372. \index{по умолчанию!размер массива}%
  373. Число элементов массива \verb|keytab| будет вычислено по количеству
  374. инициализаторов, поскольку они представлены полностью, а внутри квадратных
  375. скобок \verb|[]| ничего не задано.
  376. Программа подсчёта ключевых слов начинается с определения \verb|keytab|.
  377. Программа \verb|main| читает ввод, многократно обращаясь к функции
  378. \verb|getword| и получая на каждом её вызове очередное слово. Каждое слово
  379. ищется в \verb|keytab|.
  380. \index{функция!binsearch@\texttt{binsearch}}%
  381. Для этого используется функция бинарного поиска, которую
  382. мы написали в гл.~\ref{chapt:control_flow}. Список ключевых слов должен быть
  383. упорядочен в алфавитном порядке.
  384. \begin{LongCodePar}
  385. #include <stdio.h>
  386. #include <ctype.h>
  387. #include <string.h>
  388. #define MAXWORD 100
  389. int getword(char *, int);
  390. int binsearch(char *, struct key *, int);
  391. /* подсчёт ключевых слов Си */
  392. main()
  393. {
  394. int n;
  395. char word[MAXWORD];
  396. while (getword(word, MAXWORD) != EOF)
  397. if (isalpha(word[0]))
  398. if ((n = binsearch(word, keytab, NKEYS)) >= 0)
  399. keytab[n].count++;
  400. for (n = 0; n < NKEYS; n++)
  401. if (keytab[n].count > 0)
  402. printf("%4d %s\n",
  403. keytab[n].count, keytab[n].word);
  404. return 0;
  405. }
  406. /* binsearch: найти слово в tab[0]...tab[n-1] */
  407. int binsearch(char *word, struct key tab[], int n)
  408. {
  409. int cond;
  410. int low, high, mid;
  411. low = 0;
  412. high = n - 1;
  413. while (low <= high) {
  414. mid = (low+high) / 2;
  415. if ((cond = strcmp(word, tab[mid].word)) < 0)
  416. high = mid - 1;
  417. else if (cond > 0)
  418. low = mid + 1;
  419. else
  420. return mid;
  421. }
  422. return -1;
  423. }
  424. \end{LongCodePar}
  425. %
  426. % в оригинале слева внизу страницы ``5*''
  427. %
  428. \noindent Чуть позже мы рассмотрим функцию \verb|getword|, а сейчас нам
  429. достаточно знать, что при каждом её вызове получается очередное слово, которое
  430. запоминается в массиве, заданном первым аргументом.
  431. \verb|NKEYS| -- количество ключевых слов в \verb|keytab|. Хотя мы могли бы
  432. подсчитать число таких слов вручную, гораздо легче и безопасней сделать это с
  433. помощью машины, особенно если список ключевых слов может быть изменён. Одно из
  434. возможных решений -- поместить в конец списка инициализаторов пустой указатель
  435. (\verb|NULL|) и затем перебирать в цикле элементы \verb|keytab|, пока не
  436. встретится концевой элемент.
  437. Но возможно и более простое решение. Поскольку размер массива полностью
  438. определён во время компиляции и равен произведению количества элементов массива
  439. на размер его отдельного элемента, число элементов массива можно вычислить по
  440. формуле
  441. \begin{ShortCodeParWithCC}{\\\{\}}
  442. \textit{размер} keytab / \textit{размер} struct key
  443. \end{ShortCodeParWithCC}
  444. \noindent%
  445. \index{оператор!sizeof@\texttt{sizeof}}%
  446. В Си имеется унарный оператор \verb|sizeof|‚ который работает во время
  447. компиляции. Его можно применять для вычисления размера любого объекта. Выражения
  448. \begin{ShortCodeParWithCC}{\\\{\}}
  449. sizeof \textit{объект}
  450. \end{ShortCodeParWithCC}
  451. \noindent и
  452. \begin{ShortCodeParWithCC}{\\\{\}}
  453. sizeof(\textit{имя типа})
  454. \end{ShortCodeParWithCC}
  455. \noindent выдают целые значения, равные размеру указанного объекта или типа в
  456. байтах.
  457. \index{файл!головной!<stddef.h>@\texttt{<stddef.h>}}%
  458. \index{size{\_}t@\texttt{size{\_}t}}%
  459. (Строго говоря, \verb|sizeof| выдаёт беззнаковое целое, тип которого
  460. \verb|size_t| определён в головном файле \verb|<stddef.h>|.) Что касается
  461. объекта, то это может быть переменная, массив или структура. В качестве имени
  462. типа может выступать имя базового типа (\verb|int|, \verb|double|, \ldots) или
  463. имя производного типа, например, структуры или указателя.
  464. В нашем случае, чтобы вычислить количество ключевых слов, размер массива надо
  465. поделить на размер одного элемента. Указанное вычисление используется в
  466. инструкции \verb|#define| для установки значения \verb|NKEYS|:
  467. \begin{ShortCodePar}
  468. #define NKEYS (sizeof keytab / sizeof(struct key))
  469. \end{ShortCodePar}
  470. \noindent Этот же результат можно получить другим способом -- поделить размер
  471. массива на размер какого-то его конкретного элемента:
  472. \begin{ShortCodePar}
  473. #define NKEYS (sizeof keytab / sizeof keytab[0])
  474. \end{ShortCodePar}
  475. \noindent Преимущество такого рода записей в том, что их не надо корректировать
  476. при изменении типа.
  477. \index{оператор!sizeof@\texttt{sizeof}}%
  478. \index{if@\texttt{{\#}if}}%
  479. Поскольку препроцессор не обращает внимания на имена типов, оператор
  480. \verb|sizeof| нельзя применять в \verb|#if|. Но в \verb|#define| выражение
  481. препроцессором не вычисляется, так что предложенная нами запись допустима.
  482. \index{функция!getword@\texttt{getword}}%
  483. Теперь поговорим о функции \verb|getword|. Мы написали \verb|getword| в
  484. несколько более общем виде, чем требуется для нашей программы, но она от этого
  485. не стала заметно сложнее. Функция \verb|getword| берет из входного потока
  486. следующее <<слово>>. Под словом понимается цепочка букв-цифр, начинающаяся с
  487. буквы, или отдельная непробельная литера. По концу файла функция выдаёт
  488. \verb|EOF|, в остальных случаях её значением является код первой литеры слова
  489. или код отдельной литеры, если она не буква.
  490. \begin{LongCodePar}
  491. /* getword: принимает следующее слово или литеру из ввода */
  492. int getword(char *word, int lim)
  493. {
  494. int c, getch(void);
  495. void ungetch(int);
  496. char *w = word;
  497. while (isspace(c = getch()))
  498. ;
  499. if (c != EOF)
  500. *w++ = c;
  501. if (!isalpha(c)) {
  502. *w = '\0';
  503. return c;
  504. }
  505. for ( ; --lim > 0; w++)
  506. if (!isalnum(*w = getch())) {
  507. ungetch(*w);
  508. break;
  509. }
  510. *w = '\0';
  511. return word[0];
  512. }
  513. \end{LongCodePar}
  514. %
  515. % исправлена опечатка
  516. % в оригинале не хватало закрывающей скобки в
  517. % while (isspace(c = getch())
  518. %
  519. Функция \verb|getword| обращается к \verb|getch| и \verb|ungetch|, которые мы
  520. написали в гл.~\ref{chapt:functions_and_program_structure}. При завершении
  521. набора букв-цифр оказывается, что \verb|getword| взяла лишнюю литеру. Обращение
  522. к \verb|ungetch| позволяет вернуть её назад во входной поток. В \verb|getword|
  523. используются также \verb|isspace| -- для пропуска пробельных литер,
  524. \verb|isalpha| -- для идентификации букв и \verb|isalnum| -- для распознавания
  525. букв-цифр. Все они описаны в стандартном головном файле \verb|<ctype.h>|.
  526. \paragraph{Упражнение 6.1.} Наша версия \verb|getword| не обрабатывает должным
  527. образом знак подчёркивания, стринговые константы, комментарии и управляющие
  528. строки препроцессора. Напишите более совершенный вариант программы.
  529. \section{Указатели на структуры}
  530. \index{структура!указатель на неё}%
  531. \index{указатель!на структуру}%
  532. Для иллюстрации некоторых моментов, касающихся указателей на структуры и
  533. массивов структур, перепишем программу подсчёта ключевых слов, пользуясь для
  534. получения элементов массива вместо индексов указателями.
  535. \index{функция!binsearch@\texttt{binsearch}}%
  536. Внешняя декларация массива \verb|keytab| остаётся без изменения, а
  537. \verb|main| и \verb|binsearch| нужно модифицировать.
  538. \begin{LongCodePar}
  539. #include <stdio.h>
  540. #include <ctype.h>
  541. #include <string.h>
  542. #define MAXWORD 100
  543. int getword(char *, int);
  544. struct key *binsearch(char *, struct key *, int);
  545. /* подсчёт ключевых слов Си; версия с указателями */
  546. main()
  547. {
  548. char word[MAXWORD];
  549. struct key *p;
  550. while (getword(word, MAXWORD) != EOF)
  551. if (isalpha(word[0]))
  552. if ((p=binsearch(word, keytab, NKEYS)) != NULL)
  553. p->count++;
  554. for (p = keytab; p < keytab + NKEYS; p++)
  555. if (p->count > 0)
  556. printf("%4d %s\n", p->count, p->word);
  557. return 0;
  558. }
  559. /* binsearch: найти слово в tab[0]...tab[n-1] */
  560. struct key *binsearch(char *word, struct key *tab, int n)
  561. {
  562. int cond;
  563. struct key *low = &tab[0];
  564. struct key *high = &tab[n];
  565. struct key *mid;
  566. while (low < high) {
  567. mid = low + (high-low) / 2;
  568. if ((cond = strcmp(word, mid->word)) < 0)
  569. high = mid;
  570. else if (cond > 0)
  571. low = mid + 1;
  572. else
  573. return mid;
  574. }
  575. return NULL;
  576. }
  577. \end{LongCodePar}
  578. Некоторые детали этой программы требуют пояснений. Первое, описание функции
  579. \verb|binsearch| должно отражать тот факт, что она возвращает указатель на
  580. \verb|struct key|, а не целое; соответствующие изменения коснулись как прототипа
  581. функции, так и её заголовка. Если \verb|binsearch| находит слово, то она выдаёт
  582. указатель на него, в противном случае она возвращает \verb|NULL|.
  583. \index{указатели!арифметика с}%
  584. Второе, к элементам \verb|keytab| доступ осуществляется в нашей программе через
  585. указатели. Это потребовало значительных изменений в \verb|binsearch|.
  586. Инициализаторами для \verb|low| и \verb|high| теперь служат указатели на начало
  587. и на место сразу после конца массива.
  588. \index{неправильная арифметика с указателями}%
  589. \index{сравнение указателей}%
  590. \index{указатели!неправильная арифметика с}%
  591. \index{указатели!сравнение}%
  592. Вычисление положения среднего элемента с помощью формулы
  593. \begin{ShortCodePar}
  594. mid = (low+high) / 2 /* НЕВЕРНО */
  595. \end{ShortCodePar}
  596. \noindent не годится, поскольку указатели нельзя складывать.
  597. \index{вычитание из указателя}%
  598. \index{указатели!вычитание}%
  599. Однако к ним можно применить операцию вычитания, и так как \verb|high-low| есть
  600. число элементов, присваивание
  601. \begin{ShortCodePar}
  602. mid = low + (high-low) / 2
  603. \end{ShortCodePar}
  604. \noindent установит в \verb|mid| указатель на элемент, лежащий посередине между
  605. \verb|low| и \verb|high|.
  606. Самое важное при переходе на новый вариант программы -- сделать так, чтобы не
  607. генерировались неправильные указатели и не было попыток обращений за пределы
  608. массива. Проблема в том, что и \verb|&tab[-1]|, и \verb|&tab[n]| находятся вне
  609. границ массива. Первый адрес определённо неверен, нельзя также осуществить
  610. доступ и по второму адресу. По правилам языка, однако, гарантируется, что адрес
  611. ячейки памяти, следующей сразу за концом массива (т.е. \verb|&tab[n]|), в
  612. арифметике с указателями воспринимается правильно.
  613. В главной программе мы написали
  614. \begin{ShortCodePar}
  615. for (p = keytab; p < keytab + NKEYS; p++)
  616. \end{ShortCodePar}
  617. \noindent%
  618. \index{масштабирование целых в арифметике с указателями}%
  619. \index{структура!размер}%
  620. \index{указатели!коэффициент домножения целых в арифметике с}%
  621. Если \verb|p| -- указатель на структуру, то при выполнении операций с \verb|p|
  622. учитывается размер структуры. Поэтому \verb|p++| увеличит \verb|p| на такую
  623. величину, чтобы выйти на следующий структурный элемент массива, а проверка
  624. условия вовремя остановит цикл.
  625. Не следует, однако, полагать, что размер структуры равен сумме размеров её
  626. членов. Вследствие
  627. \index{выравнивание!ограничения по}%
  628. выравнивания объектов разной длины в структуре могут появляться безымянные
  629. <<дыры>>. Так, например, если переменная типа \verb|char| занимает один байт, а
  630. \verb|int| -- четыре байта, то для структуры
  631. \begin{ShortCodePar}
  632. struct {
  633. char c;
  634. int i;
  635. };
  636. \end{ShortCodePar}
  637. \noindent может потребоваться восемь байт, а не пять. Оператор \verb|sizeof|
  638. возвращает правильное значение.
  639. \index{программа!формат}%
  640. Наконец, несколько слов относительно формата программы. Если функция возвращает
  641. значение сложного типа, как, например, в нашем случае указатель на структуру:
  642. \begin{ShortCodePar}
  643. struct key *binsearch(char *word, struct key *tab, int n)
  644. \end{ShortCodePar}
  645. \noindent то имя функции <<высмотреть>> оказывается совсем не просто. В таких
  646. случаях иногда пользуются записью вида:
  647. \begin{ShortCodePar}
  648. struct key *
  649. binsearch(char *word, struct key *tab, int n)
  650. \end{ShortCodePar}
  651. \noindent Какой форме отдать предпочтение -- дело вкуса. Выберите ту, которая
  652. больше всего вам нравится.
  653. \section{Структуры со ссылками на себя}
  654. \label{sec:self_reference_structures}
  655. \index{программа!подсчёта!слов}%
  656. \index{структура!ссылающаяся на себя}%
  657. Предположим, что мы хотим решить более общую задачу -- написать программу,
  658. подсчитывающую частоту встречаемости для \emph{любых} слов входного потока. Так
  659. как список слов заранее не известен, мы не можем предварительно упорядочить его
  660. и применить бинарный поиск. Было бы неразумно пользоваться и линейным поиском
  661. каждого полученного слова, чтобы определять, встречалось оно ранее или нет -- в
  662. этом случае программа работала бы слишком медленно. (Более точная оценка: время
  663. работы такой программы пропорционально квадрату количества слов.) Как можно
  664. организовать данные, чтобы эффективно справиться со списком произвольных слов?
  665. Один из способов -- постоянно поддерживать упорядоченность уже полученных слов,
  666. помещая каждое новое слово в такое место, чтобы не нарушалась имеющаяся
  667. упорядоченность. Делать это передвижкой слов в линейном массиве не следует, --
  668. хотя бы потому, что указанная процедура тоже слишком долгая. Вместо этого мы
  669. воспользуемся структурой данных, называемой
  670. \index{бинарное дерево}%
  671. \index{дерево!бинарное}%
  672. \emph{бинарным деревом}.
  673. В дереве на каждое отдельное слово предусмотрен <<узел>>, который содержит:
  674. \begin{ShortCodeParWithCC}{\\\{\}}
  675. \textit{указатель на текст слова}
  676. \textit{счётчик числа встречаемости}
  677. \textit{указатель на левый сыновний узел}
  678. \textit{указатель на правый сыновний узел}
  679. \end{ShortCodeParWithCC}
  680. \noindent У каждого узла может быть один или два сына, или узел вообще может не
  681. иметь сыновей.
  682. Узлы в дереве располагаются так, что по отношению к любому узлу левое поддерево
  683. содержит только те слова, которые лексикографически меньше, чем слово данного
  684. узла, а правое -- слова, которые больше него. Вот как выглядит дерево,
  685. построенное для фразы <<now is the time for all good men to come to the aid of
  686. their party>> (<<настало время всем добрым людям помочь своей партии>>), по
  687. завершении процесса, в котором для каждого нового слова в него добавлялся новый
  688. узел:
  689. %% в окружении Verbatim моноширинным шрифтом выполнено дерево так как оно было в
  690. %% оригинале книги. Однако, такое представление выглядит неясным и невнятным,
  691. %% поэтому ниже представлен вариант дерева выполненный в синтаксисе пакета
  692. %% xypic. Если по какой-то причине вам ближе оригинальное представление, вы
  693. %% можете раскомментировать окружение Verbatim и закомментировать формулу xypic.
  694. % \begin{Verbatim}[samepage=true,xleftmargin=\codeIndent]
  695. % now
  696. % / \
  697. % is the
  698. % / \ / \
  699. % for men of time
  700. % / \ \ / \
  701. % all good party their to
  702. % / \
  703. % aid come
  704. % \end{Verbatim}
  705. {\small
  706. $$
  707. \xymatrix@C=.5em{
  708. &&&&&\text{now}\ar@{-}[dll]\ar@{-}[drr]&&&&\\
  709. &&&\text{is}\ar@{-}[dl]\ar@{-}[dr]&&&&\text{the}\ar@{-}[dll]\ar@{-}[dr]&&\\
  710. &&\text{for}\ar@{-}[dl]\ar@{-}[dr]&&\text{men}&
  711. \text{of}\ar@{-}[dr]&&&\text{time}\ar@{-}[dl]\ar@{-}[dr]&\\
  712. &\text{all}\ar@{-}[dl]\ar@{-}[dr]&&\text{good}&&&
  713. \text{party}&\text{their}&&\text{to}\\
  714. \text{aid}&&\text{come}&&&&&&&
  715. }
  716. $$
  717. }
  718. \noindent Чтобы определить, помещено ли уже в дерево вновь поступившее слово,
  719. начинают с корня, сравнивая это слово со словом из корневого узла. Если они
  720. совпали, то ответ на вопрос -- положительный. Если новое слово меньше слова из
  721. дерева, то поиск продолжается в левом поддереве‚ если больше‚ то -- в правом.
  722. Если же в выбранном направлении поддерева не оказалось, то этого слова в дереве
  723. нет, а пустующая ссылка, говорящая об отсутствии поддерева, как раз то место,
  724. куда нужно <<подвесить>> узел с новым словом.
  725. \index{рекурсия}%
  726. Описанный процесс по сути рекурсивен, так как поиск в любом узле использует
  727. результат поиска в одном из своих сыновних узлов. В соответствии с этим для
  728. добавления узла и печати дерева здесь наиболее естественно применить рекурсивные
  729. функции.
  730. Вернёмся к описанию узла, которое удобно представить в виде структуры с четырьмя
  731. компонентами:
  732. \begin{ShortCodePar}
  733. struct tnode { /* узел дерева */
  734. char *word; /* указатель на текст */
  735. int count; /* число вхождений */
  736. struct tnode *left; /* левый сын */
  737. struct tnode *right; /* правый сын */
  738. };
  739. \end{ShortCodePar}
  740. \noindent Приведённое рекурсивное определение узла может показаться рискованным,
  741. но оно правильное. Структура не может включать саму себя, но ведь
  742. \begin{ShortCodePar}
  743. struct tnode *left;
  744. \end{ShortCodePar}
  745. \noindent определяет \verb|left| как указатель на \verb|tnode|, а не сам
  746. \verb|tnode|.
  747. \index{структура!ссылающаяся на себя}%
  748. \index{структуры взаимно рекурсивные}%
  749. Иногда возникает потребность во взаимоссылающихся структурах: двух структурах,
  750. ссылающихся друг на друга. Приём, позволяющий справиться с этой задачей,
  751. демонстрирует следующий фрагмент:
  752. \begin{LongCodePar}
  753. struct t {
  754. ...
  755. struct s *p; /* p указывает на s */
  756. };
  757. struct s {
  758. ...
  759. struct t *q; /* q указывает на t */
  760. };
  761. \end{LongCodePar}
  762. Вся программа удивительно мала -- правда, она использует вспомогательные
  763. программы типа \verb|getword|, уже написанные нами.
  764. \index{функция!addtree@\texttt{addtree}}%
  765. Главная программа читает слова с помощью \verb|getword| и вставляет их в дерево
  766. посредством \verb|addtree|.
  767. \begin{LongCodePar}
  768. #include <stdio.h>
  769. #include <ctype.h>
  770. #include <string.h>
  771. #define MAXWORD 100
  772. struct tnode *addtree(struct tnode *, char *);
  773. void treeprint(struct tnode *);
  774. int getword(char *, int);
  775. /* подсчёт частоты встречаемости слов */
  776. main()
  777. {
  778. struct tnode *root;
  779. char word[MAXWORD];
  780. root = NULL;
  781. while (getword(word, MAXWORD) != EOF)
  782. if (isalpha(word[0]))
  783. root = addtree(root, word);
  784. treeprint(root);
  785. return 0;
  786. }
  787. \end{LongCodePar}
  788. Функция \verb|addtree| рекурсивна. Первое слово функция \verb|main| помещает на
  789. верхний уровень дерева (корень дерева). Каждое вновь поступившее слово
  790. сравнивается со словом узла и <<погружается>> или в левое, или в правое
  791. поддерево с помощью рекурсивного обращения к \verb|addtree|. Через некоторое
  792. время это слово обязательно либо совпадёт с каким-нибудь из имеющихся в дереве
  793. слов (в этом случае к счётчику будет добавлена $1$), либо программа встретит
  794. пустую ссылку, что послужит сигналом для заведения нового узла и добавления его
  795. к дереву. Создание нового узла сопровождается тем, что \verb|addtree| возвращает
  796. на него указатель, который вставляется в узел родителя.
  797. \begin{LongCodePar}
  798. struct tnode *talloc(void);
  799. char *strdup(char *);
  800. /* addtree: добавляет узел со словом w в p или ниже него */
  801. struct tnode *addtree(struct tnode *p, char *w)
  802. {
  803. int cond;
  804. if (p == NULL) { /* слово встречается впервые */
  805. p = talloc(); /* создаётся новый узел */
  806. p->word = strdup(w);
  807. p->count = 1;
  808. p->left = p->right = NULL;
  809. } else if ((cond = strcmp(w, p->word)) == 0)
  810. p->count++; /* это слово уже встречалось */
  811. else if (cond < 0) /* < корня левого поддерева */
  812. p->left = addtree(p->left, w);
  813. else /* > корня правого поддерева */
  814. p->right = addtree(p->right, w);
  815. return p;
  816. }
  817. \end{LongCodePar}
  818. Память для нового узла запрашивается с помощью программы \verb|talloc|, которая
  819. возвращает указатель на свободное пространство, достаточное для хранения одного
  820. узла дерева, а копирование нового слова в отдельное место памяти осуществляется
  821. с помощью \verb|strdup|. (Мы рассмотрим эти программы чуть позже.) В тот (и
  822. только в тот) момент, когда к дереву подвешивается новый узел, происходит
  823. инициализация счётчика и заполнение пустыми ссылками указателей на сыновей. Мы
  824. опустили (что неразумно) контроль ошибок, который должен выполняться при
  825. получении значений от \verb|strdup| и \verb|talloc|.
  826. \index{функция!treeprint@\texttt{treeprint}}%
  827. Функция \verb|treeprint| печатает дерево в лексикографическом порядке; для
  828. каждого узла она печатает его левое поддерево (все слова, которые меньше слова
  829. данного узла), затем само слово и, наконец, правое поддерево (слова, которые
  830. больше слова данного узла).
  831. \begin{LongCodePar}
  832. /* treeprint: упорядоченная печать дерева p */
  833. void treeprint(struct tnode *p)
  834. {
  835. if (p != NULL) {
  836. treeprint(p->left);
  837. printf("%4d %s\n", p->count, p->word);
  838. treeprint(p->right);
  839. }
  840. }
  841. \end{LongCodePar}
  842. \index{рекурсия}%
  843. Если вы не уверены, что досконально разобрались в том, как работает рекурсия,
  844. <<проиграйте>> действия \verb|treeprint| на дереве, приведённом выше.
  845. \index{эффективность}%
  846. Практическое замечание: если дерево <<несбалансировано>> (что бывает, когда
  847. слова поступают не в случайном порядке), то время работы программы может сильно
  848. возрасти. Худший вариант, когда слова уже упорядочены; в этом случае затраты на
  849. вычисления будут такими же, как при линейном поиске. Существуют обобщения
  850. бинарного дерева, которые не страдают этим недостатком, но здесь мы их не
  851. описываем.
  852. \index{память!распределитель}%
  853. \index{распределитель памяти}%
  854. Прежде чем окончательно оставить этот пример, стоит сделать краткое отступление
  855. от темы и поговорить о механизме запроса памяти. Очевидно, хотелось бы иметь
  856. всего лишь одну функцию, выделяющую память, даже если эта память предназначается
  857. для разного рода объектов. Но если одна и та же функция обеспечивает память,
  858. скажем, и для указателей на \verb|char|, и для указателей на
  859. \verb|struct tnode|, то возникают два вопроса. Первый, как справиться с
  860. требованием большинства машин, в которых объекты определённого типа должны быть
  861. выровнены (например, целые часто должны размещаться, начиная с чётных адресов)?
  862. И второе, как описать функцию, которая вынуждена в качестве результата выдавать
  863. указатели разных типов?
  864. \index{выравнивание!ограничения по}%
  865. Вообще говоря, требования, касающиеся выравнивания, можно легко выполнить за
  866. счёт некоторой потери памяти. Однако для этого возвращаемый указатель должен
  867. быть таким, чтобы удовлетворялись любые ограничения, связанные с выравниванием.
  868. Функция \verb|alloc|, описанная в гл.~\ref{chapt:pointers_and_arrays}, не
  869. гарантирует нам любое конкретное выравнивание, поэтому мы будем пользоваться
  870. \index{библиотечная функция!malloc@\texttt{malloc}}%
  871. функцией \verb|malloc| из стандартной библиотеки, которая это делает. В
  872. гл.~\ref{chapt:unix_system_interface} мы покажем один из способов её реализации.
  873. \index{оператор!приведения к типу}%
  874. Вопрос об описании типа таких функций, как \verb|malloc|, является камнем
  875. преткновения в любом языке с жёсткой проверкой типов. В Си вопрос решается
  876. естественным образом: \verb|malloc| объявляется как функция, которая возвращает
  877. указатель на \verb|void|.
  878. \index{преобразование!указателя}%
  879. \index{указатель!преобразование}%
  880. Полученный указатель затем явно приводится к желаемому типу. Описания
  881. \verb|malloc| и связанных с ней функций находятся в стандартном головном файле
  882. \verb|<stdlib.h>|.
  883. \index{функция!talloc@\texttt{talloc}}%
  884. Таким образом, функцию \verb|talloc| можно записать следующим образом:
  885. \begin{ShortCodePar}
  886. #include <stdlib.h>
  887. /* talloc: создаёт tnode */
  888. struct tnode *talloc(void)
  889. {
  890. return (struct tnode *) malloc(sizeof(struct tnode));
  891. }
  892. \end{ShortCodePar}
  893. \index{функция!strdup@\texttt{strdup}}%
  894. Функция \verb|strdup| просто копирует стринг, указанный в аргументе, в место,
  895. полученное с помощью \verb|malloc|:
  896. \begin{LongCodePar}
  897. char *strdup(char *s) /* дублирует s */
  898. {
  899. char *p;
  900. p = (char *) malloc(strlen(s)+1); /* +1 для '\0' */
  901. if (p != NULL)
  902. strcpy(p, s);
  903. return p;
  904. }
  905. \end{LongCodePar}
  906. \noindent Функция \verb|malloc| возвращает \verb|NULL|, если свободного
  907. пространства нет; \verb|strdup| передаёт это значение, оставляя заботу о выходе
  908. из ошибочной ситуации той программе, которая к ней обратилась.
  909. Память, полученную с помощью \verb|malloc|, можно освободить для повторного
  910. использования, обратившись к функции
  911. \verb|free|~(см.~гл.~\ref{chapt:input-output}~и~\ref{chapt:unix_system_interface}).
  912. \paragraph{Упражнение 6.2.} Напишите программу, которая читает текст
  913. Си-программы и печатает в алфавитном порядке все группы имён переменных, в
  914. которых совпадают первые 6 литер, но последующие в чём-то различаются. Не
  915. обрабатывайте внутренности стрингов и комментариев. Число 6 сделайте параметром,
  916. задаваемым в командной строке.
  917. \paragraph{Упражнение 6.3.} Напишите программу печати таблицы <<перекрёстных
  918. ссылок>>, которая будет печатать все слова документа и указывать для каждого из
  919. них номера строк, где оно встретилось. Программа должна игнорировать
  920. <<шумовые>> слова типа <<и>>, <<или>> и т.д.
  921. \paragraph{Упражнение 6.4.} Напишите программу, которая печатает весь набор
  922. различных слов, образующих входной поток, в порядке возрастания частоты их
  923. встречаемости. Перед каждым словом должно быть указано число вхождений.
  924. \section{Просмотр таблиц}
  925. \index{программа!поиска!в таблице}%
  926. В этом разделе, чтобы проиллюстрировать новые аспекты применения структур, мы
  927. напишем ядро пакета программ, осуществляющих вставку элементов в таблицы и их
  928. поиск внутри таблиц. Этот пакет -- типичный набор программ, с помощью которых
  929. работают с таблицами имён в любом макропроцессоре или компиляторе. Рассмотрим,
  930. например, инструкцию \verb|#define|. Когда встречается строка вида
  931. \begin{ShortCodePar}
  932. #define IN 1
  933. \end{ShortCodePar}
  934. \noindent имя \verb|IN| и замещающий его текст \verb|1| должны запоминаться в
  935. таблице. Если затем это имя \verb|IN| встретится в инструкции, например, в
  936. \begin{ShortCodePar}
  937. state = IN;
  938. \end{ShortCodePar}
  939. \noindent оно должно быть заменено на \verb|1|.
  940. Существуют две программы, манипулирующие с именами и замещающими их текстами.
  941. Это \verb|install(s, t)|, которая записывает имя \verb|s| и замещающий его текст
  942. \verb|t| в таблицу, где \verb|s| и \verb|t| -- стринги, и \verb|lookup(s)|‚
  943. осуществляющая поиск \verb|s| в таблице и возвращающая указатель на место, где
  944. имя \verb|s| было найдено, или \verb|NULL|, если \verb|s| в таблице не
  945. оказалось.
  946. Алгоритм основан на <<хэшировании>> (функции расстановки): поступающее имя
  947. свёртывается в неотрицательное число (хэш-код), которое затем используется в
  948. качестве индекса в массиве указателей. Каждый элемент этого массива является
  949. указателем на начало связанного ссылками списка блоков, описывающих имена с
  950. данным хэш-кодом. Если элемент массива содержит \verb|NULL|, это значит, что
  951. среди имён не встретилось ни одного с соответствующим хэш-кодом.
  952. \begin{figure}[H]
  953. \center{\includegraphics[width=0.5762712\linewidth]{chapt6_sec6_img0.eps}}
  954. \end{figure}
  955. Блок в списке -- это структура, содержащая указатели на имя, на замещающий текст
  956. и на следующий блок в списке; значение \verb|NULL| в указателе на следующий блок
  957. означает конец списка.
  958. \begin{ShortCodePar}
  959. struct nlist { /* элемент таблицы */
  960. struct nlist *next; /* ук-ль на следующий элемент */
  961. char *name; /* определяемое имя */
  962. char *defn; /* замещающий текст */
  963. };
  964. \end{ShortCodePar}
  965. %
  966. % добавлены завершающие ``;''
  967. % в оригинале их нет
  968. %
  969. \noindent А вот как записывается определение массива указателей:
  970. \begin{ShortCodePar}
  971. #define HASHSIZE 101
  972. static struct nlist *hashtab[HASHSIZE]; /* таблица ук-лей */
  973. \end{ShortCodePar}
  974. \index{функция!hash@\texttt{hash}}%
  975. Хэш-функция, используемая в \verb|lookup| и \verb|install|, суммирует коды литер
  976. имени, тем самым <<замешивая>> их, и в качестве результата выдаёт остаток от
  977. деления полученной суммы на размер массива указателей. Это не самый лучший
  978. способ получения хэш-кода, но достаточно лаконичный и эффективный.
  979. \begin{LongCodePar}
  980. /* hash: получает хэш-код по стрингу s */
  981. unsigned hash(char *s)
  982. {
  983. unsigned hashval;
  984. for (hashval = 0; *s != '\0'; s++)
  985. hashval = *s + 31 * hashval;
  986. return hashval % HASHSIZE;
  987. }
  988. \end{LongCodePar}
  989. \noindent Беззнаковая арифметика гарантирует, что хэш-код будет неотрицательным.
  990. \index{hash-таблица}%
  991. Хэширование порождает стартовый индекс для массива \verb|hashtab|; если
  992. соответствующий стринг в таблице есть, он может быть обнаружен только в списке
  993. блоков, на начало которого указывает элемент массива \verb|hashtab| с этим
  994. индексом.
  995. \index{функция!lookup@\texttt{lookup}}%
  996. Поиск осуществляется с помощью \verb|lookup|. Если \verb|lookup| находит элемент
  997. с заданным стрингом, то он возвращает указатель на него, если не находит, то
  998. возвращает \verb|NULL|.
  999. \begin{LongCodePar}
  1000. /* lookup: ищет s */
  1001. struct nlist *lookup(char *s)
  1002. {
  1003. struct nlist *np;
  1004. for (np = hashtab[hash(s)]; np != NULL; np = np->next)
  1005. if (strcmp(s, np->name) == 0)
  1006. return np; /* нашли */
  1007. return NULL; /* не нашли */
  1008. }
  1009. \end{LongCodePar}
  1010. \noindent В \verb|for|-цикле функции \verb|lookup| для просмотра списка
  1011. используется стандартная конструкция
  1012. \begin{ShortCodePar}
  1013. for (ptr = head; ptr != NULL; ptr = ptr->next)
  1014. ...
  1015. \end{ShortCodePar}
  1016. \index{функция!install@\texttt{install}}%
  1017. Функция \verb|install| обращается к \verb|lookup|, чтобы определить, имеется ли
  1018. в наличии вставляемый стринг. Если это так, то старое определение будет заменено
  1019. новым. В противном случае будет образован новый элемент. Если запрос памяти для
  1020. нового элемента не может быть удовлетворён, функция \verb|install| выдаёт
  1021. \verb|NULL|.
  1022. \begin{LongCodePar}
  1023. struct nlist *lookup(char *);
  1024. char *strdup(char *);
  1025. /* install: заносит (name, defn) в таблицу */
  1026. struct nlist *install(char *name, char *defn)
  1027. {
  1028. struct nlist *np;
  1029. unsigned hashval;
  1030. if ((np = (lookup(name))) == NULL) { /* не найден */
  1031. np = (struct nlist *) malloc(sizeof(*np));
  1032. if (np == NULL || (np->name = strdup(name)) == NULL)
  1033. return NULL;
  1034. hashval = hash(name);
  1035. np->next = hashtab[hashval];
  1036. hashtab[hashval] = np;
  1037. } else /* уже имеется */
  1038. free((void *) np->defn); /* освобождаем прежн.defn */
  1039. if ((np->defn = strdup(defn)) == NULL)
  1040. return NULL;
  1041. return np;
  1042. }
  1043. \end{LongCodePar}
  1044. %
  1045. % исправлена опечатка в оригинале не хватает закрывающей скобки
  1046. % if ((np = (lookup(name)) == NULL) { /* не найден */
  1047. %
  1048. \paragraph{Упражнение 6.5.} Напишите функцию \verb|undef|, удаляющую имя и
  1049. определение из таблицы, организация которой поддерживается функциями
  1050. \verb|lookup| и \verb|install|.
  1051. \paragraph{Упражнение 6.6.} Реализуйте простую версию \verb|#define|-процессора
  1052. (без аргументов), которая использовала бы программы этого раздела и годилась бы
  1053. для Си-программ. Вам могут помочь программы \verb|getch| и \verb|ungetch|.
  1054. \section{Средство \texorpdfstring{\protect\Verb|typedef|}{typedef}}
  1055. \label{sec:typedef}
  1056. \index{декларация!typedef@\texttt{typedef}}%
  1057. \index{typedef-декларация@\texttt{typedef}-декларация}%
  1058. Язык Си предоставляет средство, называемое \verb|typedef|, позволяющее давать
  1059. новые имена типам данных. Например, декларация
  1060. \begin{ShortCodePar}
  1061. typedef int Length
  1062. \end{ShortCodePar}
  1063. \noindent делает имя \verb|Length| синонимом \verb|int|. С этого момента тип
  1064. \verb|Length| можно применять в декларациях, в операторе приведения и т.д. точно
  1065. так же, как тип \verb|int|:
  1066. \begin{ShortCodePar}
  1067. Length len, maxlen;
  1068. Length *lengths[];
  1069. \end{ShortCodePar}
  1070. \noindent Аналогично декларация
  1071. \begin{ShortCodePar}
  1072. typedef char *String
  1073. \end{ShortCodePar}
  1074. \noindent делает \verb|String| синонимом \verb|char *|, т.е. указателем на
  1075. \verb|char|, и правомерным будет, например, следующее его использование:
  1076. \begin{ShortCodePar}
  1077. String p, lineptr[MAXLINES], alloc(int);
  1078. int strcmp(String, String);
  1079. p = (String) malloc(100);
  1080. \end{ShortCodePar}
  1081. Заметим, что объявляемый в \verb|typedef| тип стоит на месте имени переменной в
  1082. обычной декларации, а не сразу за словом \verb|typedef|. С точки зрения
  1083. синтаксиса слово \verb|typedef| занимает место, где обычно располагается
  1084. спецификатор класса памяти -- \verb|extern|, \verb|static| и т.д. Имена типов
  1085. записаны с заглавных букв для того, чтобы они выделялись.
  1086. Для демонстрации более сложных примеров применения \verb|typedef| воспользуемся
  1087. этим средством при задании узлов деревьев, с которыми мы уже встречались в
  1088. данной главе.
  1089. \begin{ShortCodePar}
  1090. typedef struct tnode *Treeptr;
  1091. typedef struct node { /* узел дерева: */
  1092. char *word; /* указатель на текст */
  1093. int count; /* число вхождений */
  1094. Treeptr left; /* левый сын */
  1095. Treeptr right; /* правый сын */
  1096. } Treenode;
  1097. \end{ShortCodePar}
  1098. \noindent В результате создаются два новых названия типов: \verb|Treenode|
  1099. (структура) и \verb|Treeptr| (указатель на структуру).
  1100. \index{функция!talloc@\texttt{talloc}}%
  1101. Теперь программу \verb|talloc| можно записать в следующем виде:
  1102. \begin{ShortCodePar}
  1103. Treeptr talloc(void)
  1104. {
  1105. return (Treeptr) malloc(sizeof(Treenode));
  1106. }
  1107. \end{ShortCodePar}
  1108. Следует подчеркнуть, что декларация \verb|typedef| не создаёт новый тип, она
  1109. лишь сообщает новое имя уже существующего типа. Никакого нового смысла эти новые
  1110. имена не несут, они декларируют переменные в точности с теми же свойствами, как
  1111. если бы они были объявлены напрямую без переименования типа. Фактически
  1112. \verb|typedef| аналогичен \verb|#define| с тем лишь отличием, что, будучи
  1113. интерпретируемым компилятором, он может справиться с такой текстовой
  1114. подстановкой, которая не может быть обработана препроцессором.
  1115. \index{указатель!на функцию}%
  1116. \index{функция!указатель на}%
  1117. Например,
  1118. \begin{ShortCodePar}
  1119. typedef int (*PHI)(char *, char *);
  1120. \end{ShortCodePar}
  1121. \noindent определяет тип \verb|PHI| как <<указатель на функцию (двух аргументов
  1122. типа \verb|char *|), возвращающую \verb|int|>>, который, например, в программе
  1123. сортировки, описанной в гл.~\ref{chapt:pointers_and_arrays}, можно использовать
  1124. в таком контексте:
  1125. \begin{ShortCodePar}
  1126. PHI strcmp, numcmp;
  1127. \end{ShortCodePar}
  1128. \index{переносимость}%
  1129. Помимо просто эстетических соображений, для привлечения \verb|typedef|
  1130. существуют две важные причины. Первая -- параметризация программы, связанная с
  1131. проблемой переносимости. Если с помощью \verb|typedef| объявить типы данных,
  1132. которые, возможно, являются машинно-зависимыми, то при переносе программы на
  1133. другую машину потребуется внести изменения только в определения \verb|typedef|.
  1134. Одна из распространённых ситуаций -- использование \verb|typedef|-имён для
  1135. варьирования целыми величинами.
  1136. \index{size{\_}t@\texttt{size{\_}t}}%
  1137. Для каждой конкретной машины это предполагает соответствующие установки
  1138. \verb|short|, \verb|int| или \verb|long|, которые делаются аналогично установкам
  1139. стандартных типов, например, \verb|size_t| и \verb|ptrdiff_t|.
  1140. \index{программа!читаемость}%
  1141. Вторая причина, побуждающая к применению \verb|typedef|, -- желание сделать
  1142. более ясным текст программы. Тип, названный \verb|Treeptr| (от английских слов
  1143. tree -- дерево и pointer -- указатель) более понятен, чем тот же тип, записанный
  1144. как указатель на некоторую сложную структуру.
  1145. \section{Объединения}
  1146. \index{декларация!union@\texttt{union}}%
  1147. \index{union@\texttt{union}!декларация}%
  1148. \emph{Объединение} -- это переменная, которая может содержать (в разные моменты
  1149. времени) объекты различных типов и размеров. Все требования относительно
  1150. размеров и
  1151. \index{выравнивание!ограничения по}%
  1152. выравнивания выполняет компилятор.
  1153. \index{переносимость}%
  1154. Объединения позволяют хранить разнородные данные в одной и той же области памяти
  1155. без включения в программу машинно-зависимой информации. Эти средства аналогичны
  1156. вариантным записям в Паскале.
  1157. Примером использования объединений мог бы послужить сам компилятор, заведующий
  1158. таблицей символов, если предположить, что константы могут иметь тип \verb|int|,
  1159. \verb|float| или являться указателем на стринговый литерал и иметь тип
  1160. \verb|char *|. Значение каждой конкретной константы должно храниться в
  1161. переменной соответствующего этой константе типа. Работать с таблицей символов
  1162. всегда удобнее, если значения занимают одинаковую по объёму память и
  1163. запоминаются в одном и том же месте независимо от своего типа. Цель введения в
  1164. программу объединения -- иметь переменную, которая бы на законных основаниях
  1165. хранила в себе значения нескольких типов. Синтаксис объединений аналогичен
  1166. синтаксису структур. Приведём пример объединения.
  1167. \begin{ShortCodePar}
  1168. union u_tag {
  1169. int ival;
  1170. float fval;
  1171. char *sval;
  1172. } u;
  1173. \end{ShortCodePar}
  1174. Переменная \verb|u| будет достаточно большой, чтобы в ней поместилась любая
  1175. переменная из указанных трёх типов; точный её размер зависит от реализации.
  1176. Значение одного из этих трёх типов может быть присвоено переменной \verb|u| и
  1177. далее использовано в выражениях, если это правомерно, т.е. если тип взятого ею
  1178. значения совпадает с типом последнего присвоенного ей значения. Выполнение этого
  1179. требования в каждый текущий момент -- целиком на совести программиста. В случае
  1180. <<рассогласованности>> типов результат зависит от реализации.
  1181. Синтаксис доступа к членам объединения следующий:
  1182. \begin{ShortCodeParWithCC}{\\\{\}}
  1183. \textit{имя-объединения} . \textit{член}
  1184. \end{ShortCodeParWithCC}
  1185. \noindent или
  1186. \begin{ShortCodeParWithCC}{\\\{\}}
  1187. \textit{указатель-на-объединение} -> \textit{член}
  1188. \end{ShortCodeParWithCC}
  1189. \noindent т.е. в точности такой, как в структурах. Если для хранения типа
  1190. текущего значения \verb|u| использовать, скажем, переменную \verb|utype|, то
  1191. можно написать такой фрагмент программы:
  1192. \begin{LongCodePar}
  1193. if (utype == INT)
  1194. printf("%d\n", u.ival);
  1195. else if (utype == FLOAT)
  1196. printf("%f\n", u.fval);
  1197. else if (utype == STRING)
  1198. printf("%s\n", u.sval);
  1199. else
  1200. printf(”неверный тип %d в utype\n”, utype);
  1201. \end{LongCodePar}
  1202. Объединения могут входить в структуры и массивы, и наоборот. Запись доступа к
  1203. члену объединения, находящегося в структуре (как и структуры, находящейся в
  1204. объединении), такая же, как и для вложенных структур. Например, в массиве
  1205. структур
  1206. \begin{LongCodePar}
  1207. struct {
  1208. char *name;
  1209. int flags;
  1210. int utype;
  1211. union {
  1212. int ival;
  1213. float fval;
  1214. char *sval;
  1215. } u;
  1216. } symtab[NSYM];
  1217. \end{LongCodePar}
  1218. \noindent на \verb|ival| ссылаются следующим образом:
  1219. \begin{ShortCodePar}
  1220. symtab[i].u.ival
  1221. \end{ShortCodePar}
  1222. \noindent а к первой литере стринга \verb|sval| можно обратиться любым из
  1223. следующих двух способов:
  1224. \begin{ShortCodePar}
  1225. *symtab[i].u.sval
  1226. symtab[i].u.sval[0]
  1227. \end{ShortCodePar}
  1228. Фактически объединение -- это структура, все члены которой имеют нулевое
  1229. смещение относительно её базового адреса, размера, который позволяет поместиться
  1230. в ней самому большому её члену, и
  1231. \index{выравнивание!при помощи \texttt{union}}%
  1232. \index{union@\texttt{union}!выравнивание при помощи}%
  1233. выравнивание которой удовлетворяет всем типам объединения.
  1234. \index{операции над!объединениями}%
  1235. Операции, применимые к структурам, годятся и для объединений, т.е. законны
  1236. присваивание объединения и копирование его как единого целого, взятие адреса от
  1237. объединения и доступ к отдельным его членам.
  1238. Инициализировать объединение можно только значением, имеющим тип его первого
  1239. члена; таким образом, упомянутую выше переменную \verb|u| можно инициализировать
  1240. лишь значением типа \verb|int|.
  1241. В гл.~\ref{chapt:unix_system_interface} (на примере программы, заведующей
  1242. выделением памяти) мы покажем, как, применяя объединение, можно добиться, чтобы
  1243. расположение переменной было выровнено по соответствующей границе в памяти.
  1244. \section{Поля битов}
  1245. \index{битовое поле}%
  1246. \index{биты, образцы манипулирования}%
  1247. При дефиците памяти может возникнуть необходимость запаковать несколько объектов
  1248. в одно слово машины. Одна из обычных ситуаций, встречающаяся в задачах обработки
  1249. таблиц символов для компиляторов, -- это объединение групп однобитовых флажков.
  1250. Форматы некоторых данных могут от нас вообще не зависеть и диктоваться,
  1251. например, интерфейсами с аппаратурой внешних устройств; здесь также возникает
  1252. потребность адресоваться к частям слова.
  1253. Вообразим себе фрагмент компилятора, который заведует таблицей символов. Каждый
  1254. идентификатор программы имеет некоторую связанную с ним информацию, которая
  1255. сообщает, например, представляет ли он собой ключевое слово и к какому классу,
  1256. если это переменная, она принадлежит: внешняя и/или статическая и т.д. Самый
  1257. компактный способ кодирования такой информации -- расположить однобитовые флажки
  1258. в одном слове типа \verb|char| или \verb|int|.
  1259. \index{define@\texttt{{\#}define}!вместо \texttt{enum}}%
  1260. \index{enum@\texttt{enum}!а не \texttt{{\#}define}}%
  1261. Один из распространённых приёмов работы с битами основан на определении набора
  1262. <<масок>>, соответствующих позициям этих битов, как, например, в
  1263. \begin{ShortCodePar}
  1264. #define KEYWORD 01 /* ключевое слово */
  1265. #define EXTERNAL 02 /* внешний */
  1266. #define STATIC 04 /* статический */
  1267. \end{ShortCodePar}
  1268. \noindent или в
  1269. \begin{ShortCodePar}
  1270. enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04 };
  1271. \end{ShortCodePar}
  1272. \noindent Числа должны быть степенями двойки. Тогда доступ к битам становится
  1273. делом <<побитовых операций>>, описанных в
  1274. гл.~\ref{chapt:types-operators-expressions} (сдвиг, маскирование, взятие
  1275. дополнения).
  1276. Некоторые виды записи выражений встречаются довольно часто. Так,
  1277. \begin{ShortCodePar}
  1278. flags |= EXTERNAL | STATIC;
  1279. \end{ShortCodePar}
  1280. \noindent устанавливает 1 в соответствующих битах переменной \verb|flags|,
  1281. \begin{ShortCodePar}
  1282. flags &= ~(EXTERNAL | STATIC);
  1283. \end{ShortCodePar}
  1284. \noindent обнуляет их, а
  1285. \begin{ShortCodePar}
  1286. if ((flags & (EXTERNAL | STATIC)) == 0) ...
  1287. \end{ShortCodePar}
  1288. \noindent оценивает условие как истинное, если оба бита нулевые.
  1289. \index{биты, образцы манипулирования}%
  1290. Хотя научиться писать такого рода выражения не составляет труда, вместо
  1291. побитовых логических операций можно пользоваться предоставляемым Си другим
  1292. способом прямого определения и доступа к полям внутри слова.
  1293. \index{битовое поле}%
  1294. \emph{Поле-битов} (или для краткости просто \emph{поле}) -- это некоторое
  1295. множество битов, лежащих рядом внутри одной, зависящей от реализации, единице
  1296. памяти, которую мы будем называть <<словом>>.
  1297. \index{битовое поле!декларация}%
  1298. \index{декларация!поля битов}%
  1299. Синтаксис определения полей и доступа к ним базируется на синтаксисе структур.
  1300. Например, строки \verb|#define|, фигурировавшие выше при задании таблицы
  1301. символов, можно заменить на определение трёх полей:
  1302. \begin{ShortCodePar}
  1303. struct {
  1304. unsigned int is_keyword : 1;
  1305. unsigned int is_extern : 1;
  1306. unsigned int is_static : 1;
  1307. } flags;
  1308. \end{ShortCodePar}
  1309. \noindent%
  1310. \index{выравнивание!битового поля}%
  1311. Эта запись определяет переменную \verb|flags|, которая содержит три однобитовых
  1312. поля. Число, следующее за двоеточием, задаёт ширину поля. Поля декларированы как
  1313. \verb|unsigned int|, чтобы они воспринимались как беззнаковые величины.
  1314. На отдельные поля ссылаются так же, как и на члены обычных структур:
  1315. \verb|flags.is_keyword|, \verb|flags.is_extern|, и т.д. Поля <<ведут себя>> как
  1316. малые целые и могут участвовать в арифметических выражениях точно так же, как и
  1317. другие целые. Таким образом, предыдущие примеры можно написать более
  1318. естественным образом:
  1319. \begin{ShortCodePar}
  1320. flags.is_extern = flags.is_static = 1;
  1321. \end{ShortCodePar}
  1322. \noindent устанавливает 1 в соответствующие биты;
  1323. \begin{ShortCodePar}
  1324. flags.is_extern = flags.is_static = 0;
  1325. \end{ShortCodePar}
  1326. \noindent их обнуляет, а
  1327. \begin{ShortCodePar}
  1328. if (flags.is_extern == 0 && flags.is_static == 0)
  1329. \end{ShortCodePar}
  1330. \noindent проверяет их.
  1331. Почти все технические детали, касающиеся полей, в частности, может ли поле
  1332. перейти границу слова, зависят от реализации. Поля могут не иметь имени;
  1333. \index{битовое поле!выравнивание}%
  1334. с помощью безымянного поля (задаваемого только двоеточием и шириной)
  1335. организуется пропуск нужного количества разрядов. Особая ширина, равная нулю,
  1336. используется, когда требуется выйти на границу следующего слова.
  1337. На одних машинах поля размещаются слева направо, на других -- справа налево. Это
  1338. значит, что при всей полезности работы с ними, если формат данных, с которыми мы
  1339. имеем дело, дан нам свыше, то необходимо самым тщательным образом исследовать
  1340. порядок расположения полей; программы, зависящие от такого рода вещей, не
  1341. переносимы. Поля можно определять только с типом \verb|int|, а для того, чтобы
  1342. обеспечить переносимость, явно указывая \verb|signed| или \verb|unsigned|. Они
  1343. не могут быть массивами и не имеют адресов, и, следовательно, оператор \verb|&|
  1344. к ним не применим.