Б. Страуструп - Язык программирования С++ (1119446), страница 23
Текст из файла (страница 23)
Однако, в больших программах трудно гарантировать,что объединение используется только таким способом, а в результате использования не того членаобЪединения могут возникать трудно обнаруживаемые ошибки. Но можно встроить объединение втакую структуру, которая обеспечит правильную связь между значением поля типа и текущим типомчлена объединения ($$5.4.6).Иногда объединения используют для "псевдо-преобразований" типа (в основном на это идутпрограммисты, привыкшие к языкам, в которых нет средств преобразования типов, и в результатеприходится обманывать транслятор). Приведем пример такого "преобразования" int в int* на машинеVAX, которое достигается простым совпадением разрядов:struct fudge {union {67Бьерн Страуструп.Язык программирования С++int i;int* p;};};fudge a;a.i = 4095;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.7 Упражнения1.2.(*1) Запустить программу "Hello, world" (см. $$1.3.1).(*1) Для каждого описания из $$2.1 сделать следующее: если описание не является определением, тонаписать соответствующее определение; если же описание является определением, написать для негоописание, которое не являлось бы одновременно и определением.3. (*1) Напишите описания следующих объектов: указателя на символ; массива из 10 целых; ссылки намассив из 10 целых; указателя на массив символьных строк; указателя на указатель на символ; целогоконстанты; указателя на целое-константу; константного указателя на целое.
Описания снабдитьинициализацией.4. (*1.5) Напишите программу, которая печатает размеры основных типов и типа указателя.Используйте операцию sizeof.5. (*1.5) Напишите программу, которая печатает буквы от 'a' до 'z' и цифры от '0' до '9' и их целыезначения. Проделайте то же самое для других видимых символов. Проделайте это, используяшестнадцатеричную запись.6. (*1) Напечатайте последовательность разрядов представления указателя 0 на вашей машине.Подсказка: см.$$2.6.2.7. (*1.5) Напишите функцию, печатающую порядок и мантиссу параметра типа double.8.
(*2) Каковы на используемой вами машине наибольшие и наименьшие значения следующих типов: char,short,int,long, float, double, long double, unsigned, char*, int* и void*? Есть ли какие-то особыеограничения на эти значения? Например, может ли int* быть нечетным целым? Как выравниваются впамяти объекты этих типов? Например, может ли целое иметь нечетный адрес?9.
(*1) Какова максимальная длина локального имени, которое можно использовать в вашей реализацииС++ ? Какова максимальная длина внешнего имени? Есть ли какие-нибудь ограничения на символы,которые можно использовать в имени?10. (*1) Напишите функцию, которая меняет местами значения двух целых. В качестве типа параметровиспользуйте int*. Напишите другую функцию с тем же назначением, используя в качестве типапараметров int&.68Бьерн Страуструп.Язык программирования С++11.
(*1) Каков размер массива str в следующем примере: char str[] = "a short string"; Какова длинастроки "a short string"?12. (*1.5) Составьте таблицу из названий месяцев года и числа дней в каждом из них. Напишитепрограмму, печатающую ее. Проделайте это дважды: один раз - используя массивы для названиймесяцев и количества дней, а другой раз - используя массив структур, каждая из которых содержитназвание месяца и количество дней в нем.13. (*1) С помощью typedef определите типы: unsigned char, константный unsigned char, указатель нацелое, указатель на указатель на символ, указатель на массив символов, массив из 7 указателей нацелое, указатель на массив из 7 указателей на целое и массив из 8 массивов из 7 указателей на целое.14.
(*1) Определить функции f(char), g(char&) и h(const char&) и вызвать их, используя в качествепараметров 'a', 49, 3300, c, uc, и sc, где c - char, uc - unsigned char и sc - signed char. Какой вызовявляется законным? При каком вызове транслятору придетсязавести временную переменную?69Бьерн Страуструп.Язык программирования С++ГЛАВА 3. ВЫРАЖЕНИЯ И ОПЕРАТОРЫ"Но с другой стороны не следует забывать про эффективность"(Джон Бентли)С++ имеет сравнительно небольшой набор операторов, который позволяет создавать гибкие структурыуправления, и богатый набор операций для работы с данными. Основные их возможности показаны вэтой главе на одном завершенном примере. Затем приводится сводка выражений, и подробнообсуждаются операции преобразования типа и размещение в свободной памяти.
Далее дана сводкаоператоров, а в конце главы обсуждается выделение текста пробелами и использование комментариев.3.1 КалькуляторМы познакомимся с выражениями и операторами на примере программы калькулятора. Калькуляторреализует четыре основных арифметических действия в виде инфиксных операций над числами сплавающей точкой. В качестве упражнения предлагается добавить к калькулятору переменные.Допустим, входной поток имеет вид:r=2.5area=pi*r*r(здесь pi имеет предопределенное значение). Тогда программа калькулятора выдаст:2.519.635Результат вычислений для первой входной строки равен 2.5, а результат для второй строки - это19.635.Программа калькулятора состоит из четырех основных частей: анализатора, функции ввода, таблицыимен и драйвера. По сути – это транслятор в миниатюре, в котором анализатор проводитсинтаксический анализ, функция ввода обрабатывает входные данные и проводит лексический анализ,таблица имен хранит постоянную информацию, нужную для работы, а драйвер выполняетинициализацию, вывод результатов и обработку ошибок.
К такому калькулятору можно добавить многодругих полезных возможностей, но программа его и так достаточно велика (200 строк), а введениеновых возможностей только увеличит ее объем, не давая дополнительной информации для изученияС++.3.1.1 АнализаторГрамматика языка калькулятора определяется следующими правилами:программа:ENDсписок-выражений END// END - это конец вводасписок-выражений:выражение PRINT// PRINT - это '\n' или ';'выражение PRINT список-выраженийвыражение:выражение + термвыражение - термтермтерм:терм / первичноетерм * первичноепервичное70Бьерн Страуструп.Язык программирования С++первичное:NUMBER// число с плавающей запятой в С++NAME// имя в языке С++ за исключением '_'NAME = выражение- первичное( выражение )Иными словами, программа есть последовательность строк, а каждая строка содержит одно илинесколько выражений, разделенных точкой с запятой.
Основные элементы выражения - это числа,имена и операции *, /, +, - (унарный и бинарный минус) и =. Имена необязательно описывать доиспользования.Для синтаксического анализа используется метод, обычно называемый рекурсивным спуском. Этораспространенный и достаточно очевидный метод. В таких языках как С++, то есть в которых операциявызова не сопряжена с большими накладными расходами, это метод эффективен.Для каждого правила грамматики имеется своя функция, которая вызывает другие функции.Терминальные символы (например, END, NUMBER, + и -) распознаются лексическим анализаторомget_token().
Нетерминальные символы распознаются функциями синтаксического анализатора expr(),term() и prim(). Как только оба операнда выражения или подвыражения стали известны, оновычисляется. В настоящем трансляторе в этот момент создаются команды, вычисляющие выражение.Анализатор использует для ввода функцию get_token(). Значение последнего вызова get_token()хранится в глобальной переменной curr_tok. Переменная curr_tok принимает значения элементовперечисления token_value:enum token_value {NAME,NUMBER,END,PLUS='+', MINUS='-', MUL='*',PRINT=';', ASSIGN='=', LP='(',};token_value curr_tok;DIV='/',RP=')'Для всех функций анализатора предполагается, что get_token() уже была вызвана, и поэтому в curr_tokхранится следующая лексема, подлежащая анализу.
Это позволяет анализатору заглядывать на однулексему вперед. Каждая функция анализатора всегда читает на одну лексему больше, чем нужно дляраспознавания того правила, для которого она вызывалась. Каждая функция анализатора вычисляет"свое" выражение и возвращает его результат. Функция expr() обрабатывает сложение и вычитание.Она состоит из одного цикла, в котором распознанные термы складываются или вычитаются:double expr(){double left = term();for(;;)switch(curr_tok) {case PLUS:get_token();left += term();break;case MINUS:get_token();left -= term();break;default:return left;}}// складываети вычитает// ``вечно''// случай '+'// случай '-'Сама по себе эта функция делает немного.