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

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


    Прохождения игр    
Demon's Souls |#15| Dragon God
Demon's Souls |#14| Flamelurker
Demon's Souls |#13| Storm King
Demon's Souls |#12| Old Monk & Old Hero

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


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

Язык С++

Предыдущая страница Следующая страница
1 ... 7 8 9 10 11 12 13  14 15 16 17 18 19 20 ... 50
несколько таких  крошечных переменных  вместе в  виде полей struct.
Член определяется  как поле  путем указания  после его  имени числа
битов, которые  он занимает.  Допустимы неименованные  поля; они не
влияют на  смысл  именованных  полей,  но  неким  машинно-зависимым
образом могут улучшить размещение:

  struct sreg {
      unsigned enable : 1;
      unsigned page : 3;
      unsigned : 1;        // неиспользуемое
      unsigned mode : 2;
      unsigned : 4:        // неиспользуемое
      unsigned access : 1;
      unsigned length : 1;
      unsigned non_resident : 1;
  }

Получилось  размещение   регистра  0   сосояния  DEC   PDP11/45  (в
предположении, что  поля в  слове размещаются  слева направо). Этот
пример  также   иллюстрирует  другое   основное  применение  полей:
именовать части  внешне предписанного  размещения. Поле должно быть
целого типа  и используется  как другие целые, за исключением того,
что невозможно  взять адрес поля. В ядре операционной системы или в
отладчике тип sreg можно было бы использовать так:

  sreg* sr0 = (sreg*)0777572;
  //...
  if (sr->access) {        // нарушение доступа
      // чистит массив
      sr->access = 0;
  }

  Однако применение полей для упаковки нескольких переменных в один
байт   не   обязательно   экономит   пространство.   Оно   экономит
пространство, занимаемое  данными, но  объем кода, необходимого для
манипуляции этими  переменными, на  большинстве  машин  возрастает.
Известны программы,  которые значительно  сжимались, когда двоичные

                             - стр 74 -

переменные преобразовывались  из полей  бит в  символы! Кроме того,
доступ к  char или  int обычно  намного быстрее, чем доступ к полю.
Поля  -   это  просто  удобная  и  краткая  запись  для  применения
логических операций  с целью  извлечения информации  из части слова
или введения информации в нее.

     2.5.2 Объединения

  Рассмотрим проектирование  символьной таблицы,  в которой  каждый
элемент содержит  имя  и  значение,  и  значение  может  быть  либо
строкой, либо целым:

  struct entry {
      char* name;
      char  type;
      char* string_value;        // используется если type == 's'
      int   int_value;           // используется если type == 'i'
  };

  void print_entry(entry* p)
  {
      switch p->type {
      case 's':
          cout << p->string_value;
          break;
      case 'i':
          cout << p->int_value;
          break;
      default:
          cerr << "испорчен type\n";
          break;
      }
  }

  Поскольку   string_value    и   int_value    никогда   не   могут
использоваться  одновременно,   ясно,  что  пространство  пропадает
впустую. Это можно легко исправить, указав, что оба они должны быть
членами union (объединения); например, так:

  struct entry {
      char* name;
      char  type;
      union {
          char* string_value;    // используется если type == 's'
          int   int_value;       // используется если type == 'i'
      };
  };

  Это  оставляет  всю  часть  программы,  использующую  entry,  без
изменений, но обеспечивает, что при размещении entry string_value и
int_value имеют  один и тот же адрес. Отсюда следует, что все члены
объединения вместе  занимают лишь  столько памяти, сколько занимает
наибольший член.
  Использование  объединений   таким  образом,   чтобы  при  чтении
значения всегда  применялся тот  член, с  применением которого  оно

                             - стр 75 -

записывалось,  совершенно   оптимально.  Но  в  больших  программах
непросто гарантировать,  что объединения  используются только таким
образом,  и  из-за  неправильного  использования  могут  появляться
трудно уловимые  ошибки. Можно  капсулизировать  объединение  таким
образом, чтобы  соответствие между  полем типа и типами членов было
гарантированно правильным (#5.4.6).
  Объединения иногда  испольуют  для  "преобразования  типов"  (это
делают главным  образом программисты,  воспитанные  на  языках,  не
обладающих  средствами   преобразования  типов,   где  жульничество
является необходимым).  Например, это  "преобразует" на VAX'е int в
int*, просто предполагая побитовую эквивалентность:

  struct fudge {
      union {
          int  i;
          int* p;
      };
  };

  fudge a;
  a.i = 4096;
  int* p = a.p;    // плохое использование

  Но на  самом деле  это совсем  не  преобразование:  на  некоторых
машинах   int и  int* занимают неодинаковое количество памяти, а на
других  никакое   целое  не   может  иметь  нечетный  адрес.  Такое
применение объединений  непереносимо, а  есть явный  способ указать
преобразование типа (#3.2.5).
  Изредка   объединения   умышленно   применяют,   чтобы   избежать
преобразования типов.  Можно, например,  использовать fudge,  чтобы
узнать представление указателя 0:

  fudge.p = 0;
  int i = fudge.i;    // i не обязательно должно быть 0

  Можно  также   дать  объединению   имя,  то   есть  сделать   его
полноправным типом. Например, fudge можно было бы описать так:

  union fudge {
          int  i;
          int* p;
  };

и использовать (неправильно) в точности как раньше. Имеются также и
оправданные применения именованных объединений; см. #5.4.6.

     2.6 Упражнения

  1. (*1) Заставьте работать программу с "Hello, world" (1.1.1).
  2. (*1) Для  каждого описания  в #2.1  сделайте  следующее:  Если
     описание  не   является  определением,   напишите   для   него
     определение. Если описание является определением, напишите для
     него описание, которое при этом не является определением.
  3. (*1) Напишите описания для: указателя на символ; вектора из 10
     целых; ссылки  на вектор  из 10  целых; указателя на вектор из

                             - стр 76 -

     символьных  строк;   указателя   на   указатель   на   символ;
     константного  целого;   указателя  на   константное  целое;  и
     константного   указателя    на   целое.    Каждый    из    них
     инициализируйте.
  4. (*1.5) Напишите программу, которая печатает размеры основных и
     указательных типов. Используйте операцию sizeof.
  5. (*1.5) Напишите  программу, которая печатает буквы 'a'...'z' и
     цифры '0'...'9'  и их  числовые значения.  Сделайте то  же для
     остальных печатаемых  символов. Сделайте  то же,  но используя
     шестнадцатиричную запись.
  6. (*1) Напечатайте набор битов, которым представляется указатель
     0 на вашей системе. Подсказка: #2.5.2.
  7. (*1.5)  Напишите   функцию,  печатающую   порядок  и  мантиссу
     параметра типа double.
  8. (*2)  Каковы   наибольшие  и  наименьшие  значения,  на  вашей
     системе, следующих  типов:  char,  short,  int,  long,  float,
     double,  unsigned,   char*,   int*   и   void*?   Имеются   ли
     дополнительные ограничения  на принимаемые ими значения? Может
     ли,  например,   int*   принимать   нечетное   значение?   Как
     выравниваются в памяти объекты этих типов? Может ли, например,
     int иметь нечетный адрес?
  9. (*1) Какое  самое длинное  локальное имя  можно использовать в
     C++ программе в вашей системе? Какое самое длинное внешнее имя
     можно использовать  в C++  программе в  вашей системе? Есть ли
     какие-нибудь ограничения на символы, которые можно употреблять
     в имени?
  10. (*2) Определите one следующим образом:

       const one = 1;

     Попытайтесь  поменять   значение  one  на  2.  Определите  num
     следующим образом:

       const num[] = { 1, 2 };

     Попытайтесь поменять значение num[1] на 2.
  11. (*1) Напишите  функцию, переставляющую  два целых   (меняющую
     значения). Используйте в качесте типа параметра int*. Напишите
     другую переставляющую  функцию, использующую  в  качесте  типа
     параметра int&.
  12. (*1) Каков размер вектора str в следующем примере:

       char str[] = "a short string";

     Какова длина строки "a short string"?
  13. (*1.5) Определите  таблицу названий месяцев года и числа дней
     в них.  Выведите ее. Сделайте это два раза: один раз используя
     вектор для  названий и  вектор для  числа  дней,  и  один  раз
     используя  вектор  структур,  в  каждой  из  которых  хранится
     название месяца и число дней в нем.
  14. (*1) С  помощью typedef  определите типы:  беззнаковый  char;
     константный беззнаковый char; указатель на целое; указатель на
     указатель на  char; указатель на вектора символов; вектор из 7
     целых указателей;   указатель на вектор из 7 целых указателей;
     и вектор из 8 векторов из 7 целых указателей.


                           Глава 3

                       Выражения и операторы

                                                  С другой стороны,
                             мы не можем игнорировать эффективность
                                                      - Джон Бентли

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

     3.1 Настольный калькулятор

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

  r=2.5
  area=pi*r*r

(pi определено заранее), то программа калькулятора напишет:

  2.5
  19.635

где 2.5  - результат  первой введенной строки, а 19.635 - результат
второй.
  Калькулятор  состоит   из  четырех   основных  частей:  программы
синтаксического разбора  (parser'а), функции  ввода, таблицы имен и
управляющей  программы   (драйвера).  Фактически,  это  миниатюрный
компилятор, в  котором программа синтаксического разбора производит
синтаксический  анализ,   функция   ввода   осуществляет   ввод   и
лексический  анализ,   в  таблице   имен  хранится   долговременная
информация,  а   драйвер  распоряжается  инициализцией,  выводом  и
обработкой  ошибок.   Можно  было   бы  многое   добавить  в   этот
калькулятор, чтобы  сделать его  более полезным,  но в существующем
виде эта  программа и  так достаточно длинна (200 строк), и большая
часть дополнительных  возможностей просто  увеличит текст программы
не давая дополнительного понимания применения C++.

____________________
  * Нам  неизвестен русскоязычный термин, эквивалентный английскому
indentation. Иногда это называется отступами. (прим. перев.)

                             - стр 78 -

     3.1.1 Программа синтаксического разбора

  Вот грамматика языка, допускаемого калькулятором:

  program:
      END                    // END - это конец ввода
      expr_list END

  expr_list:
      expression PRINT      // PRINT - это или '\n' или ';'
      expression PRINT expr_list

  expression:
      expression + term
      expression - term
      term

  term:
      term / primary
      term * primary
      primary

  primary:
      NUMBER                // число с плавающей точкой в C++
      NAME                  // имя C++ за исключением '_'
      NAME = expression
      - primary
      ( expression )

Другими словами,  программа есть  последовательность строк.  Каждая
строка состоит  из одного или более выражений, разделенных запятой.
Основными элементами  выражения являются числа, имена и операции *,
/, +,  - (унарный  и бинарный)  и =.  Имена не  обязательно  должны
описываться до использования.
  Используемый  метод  синтаксического  анализа  обычно  называется
Предыдущая страница Следующая страница
1 ... 7 8 9 10 11 12 13  14 15 16 17 18 19 20 ... 50
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (4)

Реклама