Главная страница
Навигация по странице:

  • 1.9.2. Директивы препроцессора

  • ifndef HERBERTS_MATH_FUNCTIONS_INCLUDE define HERBERTS_MATH_FUNCTIONS_INCLUDE include double sine(double x); endif

  • if defined

  • ifdef

  • endif Преимущество такого подхода перед применением /* ... */ состоит в том, что обеспечивается возможность вложенности таких закомментированных блоков:if 0

  • if 0 ... Бессмыслица в квадрате внутри просто бессмыслицы. endif ... Окончание просто бессмыслицы. ... (К счастью, игнорируемое компилятором.) endif

  • 1.10. Упражнения 1.10.1. Возраст

  • 1.10.2. Массивы и указатели Напишите следующие объявления: указатель на символ, массив из 10 целых 1.

  • 1.10.3. Чтение заголовка файла Matrix Market

  • Основы C Моим детям. Никогда не смейтесь, помогая мне осваивать


    Скачать 1.68 Mb.
    НазваниеОсновы C Моим детям. Никогда не смейтесь, помогая мне осваивать
    Дата12.10.2022
    Размер1.68 Mb.
    Формат файлаpdf
    Имя файлаsovremennyy-cpp-piter.pdf
    ТипГлава
    #729589
    страница9 из 9
    1   2   3   4   5   6   7   8   9
    94
    Здесь строка с присваиванием x78zy9b должна быть закомментирована, но пред- шествующие символы
    */ преждевременно прерывают новый комментарий.
    Вложенные комментарии можно корректно реализовать с помощью директивы препроцессора
    #if, как будет показано в разделе 1.9.2.4. Еще одной возможностью отключить несколько строк является использование соответствующей функции интегрированной среды разработки или редактора, предназначенного для работы с исходными текстами данного языка программирования.
    1.9.2. Директивы препроцессора
    В этом разделе мы представим команды (директивы), которые могут исполь- зоваться в предварительной обработке (препроцессинге) исходных текстов. По- скольку они практически не зависят от языка программирования, мы рекомен- дуем минимизировать их применение, в особенности это касается макросов.
    1.9.2.1. Макросы
    Почти любой макрос является демонстрацией недостатка
    языка программирования, программы или программиста.
    — Бьярне Страуструп (Bjarne Stroustrup)
    Это очень старый метод повторного использования кода — путем расширения имен макросов до текста их определения, потенциально с аргументами. Макросы дают много возможностей украсить свои программы, но еще больше возможнос- тей их погубить. Макросы не связаны пространствами имен, областями видимос- ти или любыми иными возможностями языка потому, что они являются простой заменой текста без какого-либо понятия о типах. К сожалению, некоторые библи- отеки определяют макросы с такими распространенными именами, как, напри- мер, major. Мы должны бескомпромиссно отменить такие макросы, например, используя
    #undef major, без малейшей пощады по отношению к тем, у кого до- станет ума их использовать. Visual Studio определяет — даже сегодня!!! — макросы min и max, и мы настоятельно рекомендуем вам отключить их при компиляции с помощью опции командной строки
    /DNOMINMAX. Почти все макросы могут быть заменены чем-то иным (константами, шаблонами, встроенными функциями). Но если вы действительно не в состоянии найти другой способ реализации чего-то, прислушайтесь к следующему совету.
    Имена макросов
    Используйте для макросов длинные и уродливые имена, состоящие из пропис- ных букв, — наподобие
    LONG_AND_UGLY_NAMES_IN_CAPITALS!
    Макросы могут создавать странные проблемы почти всеми мыслимыми и не- мыслимыми способами. Чтобы дать вам общее представление, мы рассмотрим
    02_ch01.indd 94 14.07.2016 10:46:48

    1.9. Структурирование программных проектов
    95
    несколько примеров в разделе A.2.10, с некоторыми советами о том, как с ними бороться. Вы можете спокойно отложить чтение этого раздела до тех пор, пока не столкнетесь с некоторыми из этих проблем на практике.
    Как вы узнаете из этой книги, C++ предоставляет куда лучшие альтернативы, такие как как константы, встраиваемые функции и constexpr.
    1.9.2.2. Включение
    Чтобы упростить язык C, многие возможности языка, такие как операции ввода- вывода, были исключены из ядра языка и вместо этого реализованы библиоте- кой. C++ следует этому дизайну и реализует новые возможности там, где это воз- можно, в стандартной библиотеке, но пока что еще никто не назвал C++ простым языком.
    Как следствие почти каждая программа должна включать один или несколько заголовочных файлов. Наиболее часто включается заголовочный файл, отвечаю- щий за ввод-вывод:
    #include <iostream>
    Препроцессор ищет этот файл в стандартных каталогах заголовочных файлов, таких как
    /usr/include, /usr/local/include и т.д. Мы можем добавить в этот путь поиска дополнительные каталоги с помощью флагов командной строки ком- пилятора — обычно
    -I в мире Unix/Linux/Mac OS и /I в Windows.
    Когда мы записываем имя файла в двойных кавычках, например
    #include "herberts_math_functions.hpp"
    компилятор обычно начинает поиск в текущем каталоге, а только затем — в стан- дартных путях
    18
    . Это эквивалентно использованию угловых скобок и добавлению текущего каталога в пути поиска. Некоторые программисты утверждают, что уг- ловые скобки должны использоваться только для системных заголовочных фай- лов, а двойные кавычки — для пользовательских заголовочных файлов.
    Чтобы избежать коллизий имен, зачастую к пути поиска добавляется родитель- ский каталог, а в директиве используется относительный путь:
    #include "herberts_includes/math_functions.hpp"
    #include <another_project/more_functions.h>
    Косая черта является переносимым символом разделения каталогов и работает и в Windows, несмотря на тот факт, что в этой операционной системе для вложен- ных каталогов используется символ обратной косой черты.
    Защита включения. Часто используемые заголовочные файлы могут оказать- ся включенными несколько раз в одной единице трансляции из-за косвенного включения заголовочного файла. Чтобы избежать запрещенных повторений и ограничить расширение текста, используется так называемая защита включений,
    18
    Какие именно каталоги будут просматриваться в поисках файла в двойных кавычках, зависит от реализации и в стандарте не оговаривается.
    02_ch01.indd 95 14.07.2016 10:46:49

    Основы C++
    96
    гарантирующая, что будет выполнено только первое включение заголовочного файла. Эта защита представляет собой простые макросы, которые указывают со- стояние включения определенного файла. Типичный включаемый файл выглядит следующим образом:
    // Автор: я
    // Лицензия: $100 только за каждое чтение этого файла
    #ifndef HERBERTS_MATH_FUNCTIONS_INCLUDE
    #define HERBERTS_MATH_FUNCTIONS_INCLUDE
    #include double sine(double x);
    #endif // HERBERTS_MATH_FUNCTIONS_INCLUDE
    Таким образом, содержимое файла включается только тогда, когда защищаю- щий макрос не определен. В тексте заголовочного файла мы определяем защища- ющий макрос для предотвращения повторных включений.
    Как и в случае любых макросов, мы должны уделять повышенное внимание уникальности определяемого имени, причем не только в нашем проекте, но и во всех других заголовочных файлах, которые мы включаем прямо или косвенно.
    В идеале имя должно представлять проект и имя файла. Оно также может содер- жать относительный путь проекта или пространство имен (§3.2.1). Завершение имени защитного макроса суффиксом
    _INCLUDE или _HEADER — весьма распро- страненная практика. Случайное повторное использование имени защитного мак- роса может породить множество различных ошибок. Исходя из нашего опыта об- наружение причины этих неприятностей оказывается не самым простым делом.
    Опытные разработчики генерируют их автоматически с помощью упомянутых сведений или даже с помощью генераторов случайных чисел.
    Удобной альтернативой является строка
    #pragma once. С ее использованием предыдущий пример сокращается до
    // Автор: я
    // Лицензия: $100 только за каждое чтение этого файла
    #pragma once
    #include double sine(double x);
    Эта прагма не является частью стандарта, но все нынешние крупные компиля- торы ее поддерживают. С помощью этой директивы ответственность за однократ- ное включение заголовочного файла перекладывается на компилятор.
    02_ch01.indd 96 14.07.2016 10:46:49

    1.9. Структурирование программных проектов
    97
    1.9.2.3. Условная компиляция
    Важным и необходимым применением директив препроцессора является уп- равление условной компиляцией. Препроцессор предоставляет директивы
    #if,
    #else, #elif и #endif для возможности ветвления. Условия могут быть срав- нениями, проверкой определений имен макросов или логическими выражениями с ними. Директивы
    #ifdef и #ifndef являются сокращениями соответственно для:
    #if defined ( MACRO_NAME )
    #if !defined ( MACRO_NAME )
    Длинная форма должна использоваться при проверке определения в сочета- нии с другими условиями. Аналогично
    #elif является сокращением для #else и
    #if.
    В идеальном мире мы могли бы писать только переносимые, соответствующие стандарту C++ программы. В действительности иногда мы вынуждены использо- вать непереносимые библиотеки. Скажем, у нас есть библиотека, доступная толь- ко в Windows, точнее — только при использовании Visual Studio. Для всех других компиляторов у нас есть альтернативная библиотека. Самый простой способ реа- лизации, зависящей от платформы, — предоставить альтернативные фрагменты кода для различных компиляторов:
    # ifdef _MSC_VER
    ... Код для Windows
    # else
    ... Код для Linux/Unix
    # endif
    Аналогично нам нужна условная компиляция, когда мы хотим использовать новую возможность языка, которая доступна не на всех целевых платформах, ска- жем, семантику перемещения (§2.3.5):
    # ifdef MY_LIBRARY_WITH_MOVE_SEMANTICS
    ... Что-то эффективное с использованием семантики перемещения
    # else
    ... Менее эффективное, но более переносимое решение
    # endif
    Так мы можем использовать некоторую возможность при ее наличии и при этом по-прежнему поддерживать переносимость для компиляторов без этой воз- можности. Конечно, нам нужны надежные инструменты, которые определяют макрос только тогда, когда эта возможность действительно доступна. Условная компиляция является довольно мощным средством, но она имеет свою цену: под- держка исходного текста и тестирование становятся более трудоемкими и подвер- женными ошибкам. Эти недостатки могут быть уменьшены с помощью хорошо продуманной инкапсуляции, так что различные реализации используются более общими интерфейсами.
    02_ch01.indd 97 14.07.2016 10:46:49

    Основы C++
    98
    1.9.2.4. Вкладываемые комментарии
    Директива
    #if может использоваться для того, чтобы закомментировать це- лые блоки кода:
    #if
    0
    ... Это код с ошибками. Мы исправим его. Потом. Может быть. Честно!
    #endif
    Преимущество такого подхода перед применением
    /* ... */ состоит в том, что обеспечивается возможность вложенности таких закомментированных блоков:
    #if 0
    ... Начало просто бессмыслицы.
    #if 0
    ... Бессмыслица в квадрате внутри просто бессмыслицы.
    #endif
    ... Окончание просто бессмыслицы.
    ... (К счастью, игнорируемое компилятором.)
    #endif
    Тем не менее следует умеренно использовать этот метод. Если три четверти программы представляют собой комментарии, ее следует серьезно пересмотреть.
    Изложив материал этой главы конспективно, мы иллюстрируем основные воз- можности C++ в разделе A.3 приложения. Мы не включили его в основное со- держание книги, чтобы поддержать высокий темп для нетерпеливых читателей.
    Тем, кто не любит такой спешки, мы рекомендуем найти время, чтобы прочитать упомянутый раздел и увидеть, какой путь в реальности проходят постепенно раз- вивающиеся нетривиальные программы.
    1.10. Упражнения
    1.10.1. Возраст
    Напишите программу, которая запрашивает входные данные с клавиатуры и получает их, а также выводит результат на экран и записывает его в файл. Вопрос, который задает программа, — “Сколько вам лет?”
    1.10.2. Массивы и указатели
    Напишите следующие объявления: указатель на символ, массив из 10 целых
    1.
    чисел, указатель на массив из 10 целых чисел, указатель на массив символь- ных строк, указатель на указатель на символ, целочисленная константа, ука- затель на целочисленную константу, константный указатель на целое число.
    Инициализируйте все эти объекты.
    02_ch01.indd 98 14.07.2016 10:46:49

    1.10. Упражнения
    99
    Напишите небольшую программу, которая создает массивы в стеке (масси-
    2.
    вы фиксированного размера) и массивы в куче (с помощью распределения памяти). Воспользуйтесь valgrind, чтобы посмотреть, что происходит, ког- да вы не удаляете их корректно.
    1.10.3. Чтение заголовка файла Matrix Market
    Формат данных Matrix Market используется для хранения плотных и разрежен- ных матриц в формате ASCII. Заголовок содержит некоторую информацию о типе и размер матрицы. Для разреженных матриц данные хранятся в трех столбцах.
    Первый столбец содержит номер строки, второй — номер столбца, а третий — числовое значение на их пересечении. В случае матрицы с комплексными значе- ниями добавляется четвертый столбец для мнимой части.
    Вот пример файла Matrix Market:
    %%MatrixMarket Действительная матрица с координатами
    %
    % Матрица рассчитанных значений
    %
    2025 2025 100015 1 1 .9273558001498543E-01 1 2 .3545880644900583E-01
    Первая строка, которая начинается не с символа
    %, содержит количество строк, количество столбцов и количество ненулевых элементов разреженной матрицы.
    Используйте fstream для чтения заголовка файла Matrix Market и вывода на экран размера матрицы и количества ее ненулевых элементов.
    02_ch01.indd 99 14.07.2016 10:46:49
    1   2   3   4   5   6   7   8   9


    написать администратору сайта