Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Aliens Vs Predator |#10| Human company final
Aliens Vs Predator |#9| Unidentified xenomorph
Aliens Vs Predator |#8| Tequila Rescue
Aliens Vs Predator |#7| Fighting vs Predator

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Страустрап Б. Весь текст 579.17 Kb

Язык С++

Предыдущая страница Следующая страница
1 ... 14 15 16 17 18 19 20  21 22 23 24 25 26 27 ... 50
Однано для  компоновщиков старого  образца слишком трудно проверять
тождественность нетривиальных  констант и убирать ненужные повторы.
Кроме того,  простые случаи  гораздо более  обиходны и потому более
важны для генерации хорошего кода.

     4.3.1 Один Заголовочный Файл

  Проще всего  решить проблему  разбиения  программы  на  несколько
файлов поместив  функции и  определения данных  в подходящее  число
исходных файлов и описав типы, необходимые для их взаимодействия, в
одном заголовочном  файле,  который  включается  во  все  остальные

                             - стр 114 -

файлы. Для  программы калькулятора  можно  использовать  четыре  .c
файла: lex.c,  syn.c, table.c  и main.c,  и заголовочный файл dc.h,
содержащий описания  всех имен,  которые используются  более чем  в
одном .c файле:

  // dc.h: общие описания для калькулятора

  enum token_value {
      NAME,    NUMBER,    END,
      PLUS='+',    MINUS='-',    MUL='*',    DIV='/',
      PRINT=';',    ASSIGN='=',    LP='(',    RP=')'
  };

  extern int no_of_errors;
  extern double error(char* s);
  extern token_value get_token();
  extern token_value curr_tok;
  extern double number_value;
  extern char name_string[256];

  extern double expr();
  extern double term();
  extern double prim();

  struct name {
      char* string;
      name* next;
      double value;
  };

  extern name* look(char* p, int ins = 0);
  inline name* insert(char* s) { return look(s,1); }

  Если опустить  фактический код, то lex.c будет выглядеть примерно
так:

  // lex.c: ввод и лексический анализ

  #include "dc.h"
  #include

  token_value curr_tok;
  double number_value;
  char name_string[256];

  token_value get_token() { /* ... */ }

Заметьте, что  такое использование заголовочных файлов гарантирует,
что каждое  описание в  заголовочном файле  объекта,  определенного
пользователем, будет  в какой-то  момент включено  в файл,  где  он
определяется. Например,  при  компиляции  lex.c  компилятору  будет
передано:

  extern token_value get_token();
  // ...
  token_value get_token() { /* ... */ }

                             - стр 115 -

Это   обеспечивает    то,   что    компилятор    обнаружит    любую
несогласованность в  типах, указанных  для имени. Например, если бы
get_token() была  описана как возвращающая token_value, но при этом
определена как  возвращающая int, компиляция lex.c не прошла бы из-
за ошибки несоответствия типов.
  Файл syn.c будет выглядеть примерно так:

  // syn.c: синтаксический анализ и вычисление

  #include "dc.h"

  double prim() { /* ... */ }
  double term() { /* ... */ }
  double expr() { /* ... */ }

  Файл table.c будет выглядеть примерно так:

  // table.c: таблица имен и просмотр

  #include "dc.h"

  extern char* strcmp(const char*, const char*);
  extern char* strcpy(char*, const char*);
  extern int strlen(const char*);

  const TBLSZ = 23;
  name* table[TBLSZ];

  name* look(char* p; int ins) { /* ... */ }

  Заметьте, что  table.c  сам  описывает  стандартные  функции  для
работы со  строками, поэтому  никакой проверки согласованности этих
описаний нет.  Почти всегда  лучше включать  заголовочный файл, чем
описывать имя  в .c  файле как  extern. При  этом может  включаться
"слишком много",  но это  обычно не оказывает серьезного влияния на
время, необходимое  для компиляции,  и как  правило экономит  время
программиста. В  качестве примера  этого, обратите  внимание на то,
как strlen() заново описывается в main() (ниже). Это лишние нажатия
клавиш и  возможный источник неприятностей, поскольку компилятор не
может проверить  согласованность этих  двух определений.  На  самом
деле, этой  сложности можно  было бы  избежать, будь  все  описания
extern  помещены   в  dc.h,   как  и   предлагалось  сделать.   Эта
"небрежность" сохранена  в программе,  поскольку это  очень типично
для C  программ, очень  соблазнительно  для  программиста,  и  чаще
приводит, чем  не приводит, к ошибкам, которые трудно обнаружить, и
к программам, с которыми тяжело работать. Вас предупредили!
  И main.c, наконец, выглядит так:

                             - стр 116 -

  // main.c: инициализация, главный цикл и обработка ошибок

  #include "dc.h"

  int no_of_errors;

  double error(char* s) { /* ... */ }

  extern int strlen(const char*);

  main(int argc, char* argv[]) { /* ... */ }

  Важный  случай,   когда  размер  заголовочных  файлов  становится
серьезной помехой.  Набор заголовочных  файлов и  библиотеку  можно
использовать для  расширения языка  множеством обще-  и специально-
прикладных типов  (см. Главы  5-8).  В  таких  случаях  не  принято
осуществлять чтение тысяч строк заголовочных файлов в начале каждой
компиляции. Содержание этих файлов обычно "заморожено" и изменяется
очень нечасто.  Наиболее полезным  может оказаться  метод  затравки
компилятора содержанием этих заголовочных фалов. По сути, создается
язык специльного  назначения  со  своим  собственным  компилятором.
Никакого  стандартного   метода  создания   такого  компилятора   с
затравкой не принято.

     4.3.2 Множественные Заголовочные Файлы

  Стиль разбиения  программы с  одним заголовочным  файлом наиболее
пригоден в  тех случаях,  когда программа  невелика и  ее части  не
предполагается использовать  отдельно. Поэтому  то, что  невозможно
установить, какие  описания зачем  помещены  в  заголовочный  файл,
несущественно. Помочь  могут комментарии.  Другой способ  - сделать
так, чтобы  каждая часть  программы имела свой заголовочный файл, в
котором определяются  предоставляемые этой  частью средства.  Тогда
каждый .c  файл имеет  соответствующий .h  файл, и  каждый .c  файл
включает свой  собственный (специфицирующий то, что в нем задается)
.h файл  и, возможно, некоторые другие .h файлы (специфицирущие то,
что ему нужно).
  Рассматривая организацию  калькулятора, мы  замечаем, что error()
используется почти  каждой функцией  программы, а  сама  использует
только . Это обычная для функции ошибок ситуация, поэтому
error() следует отделить от main():

                             - стр 117 -

  // error.h: обработка ошибок

  extern int no_errors;

  extern double error(char* s);

  // error.c

  #include
  #include "error.h"

  int no_of_errors;

  double error(char* s) { /* ... */ }

  При таком  стиле использования  заголовочных  файлов  .h  файл  и
связанный с  ним .c  файл можно рассматривать как модуль, в котором
.h файл задает интерфейс, а .c файл задает реализацию.
  Таблица символов  не зависит  от остальной  части калькулятора за
исключеним использования функции ошибок. Это можно сделать явным:

  // table.h: описания таблицы имен

  struct name {
      char* string;
      name* next;
      double value;
  };

  extern name* look(char* p, int ins = 0);
  inline name* insert(char* s) { return look(s,1); }

  // table.c: определения таблицы имен

  #include "error.h"
  #include
  #include "table.h"

  const TBLSZ = 23;
  name* table[TBLSZ];

  name* look(char* p; int ins) { /* ... */ }

  Заметьте,  что   описания  функций   работы  со  строками  теперь
включаются из . Это исключает еще один возможный источник
ошибок.

                             - стр 118 -

  // lex.h: описания для ввода и лексического анализа

  enum token_value {
      NAME,        NUMBER,        END,
      PLUS='+',    MINUS='-',     MUL='*',    DIV='/',
      PRINT=';',   ASSIGN='=',    LP='(',     RP=')'
  };

  extern token_value curr_tok;
  extern double number_value;
  extern char name_string[256];

  extern token_value get_token();

  Этот интерфейс  лексического анализатора достаточно беспорядочен.
Недостаток  в   надлежащем  типе   лексемы  обнаруживает   себя   в
необходимости   давать    пользователю   get_token()    фактические
лексические буферы number_value и name_string.

  // lex.c: определения для ввода и лексического анализа

  #include
  #include
  #include "error.h"
  #include "lex.h"

  token_value curr_tok;
  double number_value;
  char name_string[256];

  token_value get_token() { /* ... */ }

  Интефейс синтаксического анализатора совершенно прозрачен:

  // syn.c: описания для синтаксического анализа и вычисления

  extern double expr();
  extern double term();
  extern double prim();

  // syn.c: определения для синтаксического анализа и вычисления

  #include "error.h"
  #include "lex.h"
  #include "syn.h"

  double prim() { /* ... */ }
  double term() { /* ... */ }
  double expr() { /* ... */ }

  Главная программа, как всегда, тривиальна:

                             - стр 119 -

  // main.c: главная программа

  #include
  #include "error.h"
  #include "lex.h"
  #include "syn.h"
  #include "table.h"
  #include

  main(int argc, char* argv[]) { /* ... */ }

  Сколько заголовочных  файлов использовать в программе, зависит от
многих факторов. Многие из этих факторов сильнее связаны с тем, как
ваша система  работает  с  заголовочными  файлами,  нежели  с  C++.
Например,  если   в  вашем   редакторе  нет   средств,  позволяющих
одновременно видеть  несколько файлов, использование большого числа
файлов   становится   менее   привлекательным.   Аналогично,   если
открывание и  чтение 10 файлов по 50 строк в каждом требует заметно
больше времени,  чем чтение  одного файла  в 500  строк, вы  можете
дважды подумать,  прежде чем использовать в небольшом проекте стиль
множественных заголовочных  файлов. Слово предостережения: набор из
десяти заголовочных  файлов  плюс  стандартные  заголовочные  файлы
обычно легче  поддаются  управлению.  С  другой  стороны,  если  вы
разбили описания  в большой  программе на  логически минимальные по
размеру заголовочные  файлы (помещая  каждое описание  структуры  в
свой  отдельный   файл  и  т.д.),  у  вас  легко  может  получиться
неразбериха из сотен файлов.

     4.3.3 Скрытие Данных

  Используя заголовочные  файлы пользователь может определять явный
интерфейс, чтобы  обеспечить согласованное  использование  типов  в
программе. С  другой стороны,  пользователь может обойти интерфейс,
задаваемый заголовочным файлом, вводя в .c файлы описания extern.
  Заметьте, что такой стиль компоновки не рекомендуется:

  // file1.c:                // "extern" не используется
      int a = 7;
      const c = 8;
      void f(long) { /* ... */ }

  // file2.c:                // "extern" в .c файле
      extern int a;
      extern const c;
      extern f(int);
      int g() { return f(a+c); }

Поскольку  описания   extern  в  file2.c  не  включаются  вместе  с
определениями  в  файле  file1.c,  компилятор  не  может  проверить
согласованность  этой   программы.   Следовательно,   если   только
загрузчик не  окажется гораздо сообразительнее среднего, две ошибки
в этой программе останутся, и их придется искать программисту.
  Пользователь может  защитить файл  от такой  недисциплинированной
компоновки, описав  имена,  которые  не  предназначены  для  общего

                             - стр 120 -

пользования, как  static, чтобы  их областью  видимости был файл, и
они были скрыты от остальных частей программы. Например:

  // table.c: определения таблицы имен

  #include "error.h"
  #include
  #include "table.h"

  const TBLSZ = 23;
  static name* table[TBLSZ];

  name* look(char* p; int ins) { /* ... */ }

  Это гарантирует,  что любой  доступ к  table действительно  будет
осуществляться именно  через look().  "Прятать" константу  TBLSZ не
обязательно.

     4.4 Файлы как Модули

  В предыдущем  разделе .c  и  .h  файлы  вместе  определяли  часть
программы. Файл  .h является интерфейсом, который используют другие
части программы;  .c файл  задает реализацию.  Такой  объект  часто
называют модулем.  Доступными делаются  только  те  имена,  которые
Предыдущая страница Следующая страница
1 ... 14 15 16 17 18 19 20  21 22 23 24 25 26 27 ... 50
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (4)

Реклама