WWW.DISS.SELUK.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА
(Авторефераты, диссертации, методички, учебные программы, монографии)

 

Pages:     || 2 | 3 | 4 |

«Т, А. Павловская C/C++ Программирование на языке высокого уровня Допущено Министерством образования Российской Федерации в качестве учебника для студентов высших учебных заведений, обучающихся по направлению Информатика ...»

-- [ Страница 1 ] --

// ^../^.....^ ••:.••• г.-!-...•-.

Т, А. Павловская

C/C++

Программирование

на языке высокого уровня

Допущено Министерством образования Российской Федерации

в качестве учебника для студентов высших учебных

заведений, обучающихся по направлению

«Информатика и вычислительная техника»

3004^

300.piter.com Издательская программа 300 лучших учебников для высшей школы в честь 300-летия Санкт-Петербурга осуществляется при поддержке Министерства образования РФ 1;^пптЕР' Москва • Санкт-Петербург • Нижний Новгород • Воронеж Ростов-на-Дону • Екатеринбург • Самара Киев • Харьков • Минск ББК 32.973-018я УДК 681.3.06(075) П Рецензенты:

Ревунков Г. И., кандидат технических наук, доцент, декан факультета информатики и систем управления МГТУ им. Н. Э. Баумана Варлинский Н. Н., кандидат технических наук, доцент кафедры МО ЭВМ Санкт-Петербургского электротехнического университета Фомичев B.C., доктор технических наук, профессор кафедры вычислительной техники Санкт-Петербургского электротехнического университета П12 C/C++. Программирование на языке высокого уровня / Т. А. Павловская. — СПб.:

Питер, 2003. —461 с: ил.

ISBN 5-94723-568- Задача этой книги — дать краткое и четкое изложение языка C++ в соответствии со стандар­ том ISO/IEC 14882. Она предназначена для студентов, изучающих язык «с нуля», но будет полезна и более искушенным в программировании. Цель, которая ставилась при написании книги — дать правильное представление о возможностях языка C++ и способах их применения, толчок к даль­ нейшему изучению этого и других языков программирования и ключ к пониманию современных объектно-ориентированных технологий.

Контрольные задания по ключевым темам представлены в 20 вариантах, и автор надеется, что преподаватели оценят проявленную о них заботу.

ББК 32.973-018я УДК 681.3.06(075) ISBN 5-94723-568-4 © ЗАО Издательский дом «Питер», Краткое содержание Предисловие Часть I. Структурное программирование Глава 1. Базовые средства языка C++ Глава 2. Модульное программирование Глава 3. Технология создания программ Упражнения к части 1 Часть II. Объектно-ориентированное программирование Глава 4. Классы Глава 5. Наследование Глава 6. Шаблоны классов Глава 7. Обработка исключительных ситуаций Глава 8. Преобразования типов Глава 9. Рекомендации по программированию Упражнения к части И Часть III. Стандартная библиотека Глава 10. Потоковые классы Глава 1 1. Строки Глава 12. Контейнерные классы Глава 13. Итераторы и функциональные объекты Глава 14. Алгоритмы Глава 15. Средства для численных расчетов ^. Глава 16. Другие средства стандартной библиотеки Упражнения к части III Послесловие Литература Приложение 2. Спецификации формата для функций семейства printf Приложение 4. Заголовочные файлы стандартной библиотеки Приложение 5. Константы, макросы и типы данных стандартной библиотеки.. Содержание Часть!. Структурное программирование Базовые конструкции структурного программирования Типы данных, определяемые пользователем Содержание Кодирование и документирование программы Реализация динамических структур с помощью массивов Функции библиотеки для работы со строками и символами Часть I I. Объектно-ориентированное программирование Содержание Исключения в конструкторах и деструкторах Операция приведения типов в стиле С Глава 9. Рекомендации по программированию Часть i l l. Стандартная библиотека Потоки и типы, определенные пользователем Конструкторы и присваивание строк Очереди с приоритетами (priorityqueue) Глава 1 3. Итераторы и функциональные объекты Немодифицирующие операции с последовательностями Модифицирующие операции с последовательностями remove, removejf, removecopy, remove_copy_lf replace, replacejf, replace_copy, replacecopyJf Алгоритмы работы с множествами и пирамидами set_difference, setsymmetric_difference Пример использования алгоритмов работы с множествами Глава 1 5. Средства для численных расчетов Глава 16. Другие средства стандартной библиотеки Приложение 2. Спецификации формата Приложение 3. Арифметические преобразования типов Приложение 4. Заголовочные файлы стандартной библиотеки Приложение 5. Константы, макросы и тмпы данных Приложение 6. Функции стандартной библиотеки Предисловие Задача этой книги — дать краткое и четкое изложение языка C++ в соответствии со стандартом ISO/IEC 14882 (1998) без легковесности и фрагментарности серии «Для чайников» и без пространных рассуждений о нюансах, важность ко­ торых становится очевидной только после приобретения опыта программирова­ ния. Книга предназначена для студентов, изучающих язык «с нуля», но будет по­ лезна и более искушенным в программировании. Можно рассматривать ее как конспект лекций или большую шпаргалку.

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

Кроме конструкций языка, в книге рассматриваются основные структуры дан­ ных, используемые при написании программ, критерии качества программ, а так­ же рекомендации по стилю и технологии программирования.

Вот что написал о важности дисциплины программирования создатель C++ Б. Страуструп: «Вы можете написать небольшую программу (скажем, 1000 строк), используя грубую силу и нарушая все правила хорошего стиля. Для программ большего размера вы не сможете это сделать. Если структура программы, состоя­ щей из 100 000 строк, плоха, вы обнаружите, что новые ошибки появляются с той же скоростью, с которой исправляются старые. C++ разрабатывался таким обра­ зом, чтобы предоставить возможность рационально структурировать большие программы, и чтобы один человек мог работать с большим объемом кода».



Более трети книги посвящено обзору стандартной библиотеки — инструмента, которым должен владеть каждый профессиональный программист для уменьше­ ния сроков разработки программ и повышения их надежности, переносимости и универсальности.

В книге не рассматривается программирование под Windows и другие среды, по­ этому все примеры представляют собой так называемые «консольные приложе­ ния». Синтаксис примеров соответствует стандарту C++, поэтому некоторые из них могут не компилироваться в оболочках, выпущенных до его утверждения.

Книга состоит из трех частей: «Структурное программирование», «Объектноориентированное программирование» и «Стандартная библиотека».

В первой части рассматриваются основные конструкции языка и базовая техно­ логия создания программ, отвечающих современным требованиям качества и на­ дежности.

Вторая часть посвящена описанию средств языка, позволяющих успешно созда­ вать программы большого объема — классов, наследования, шаблонов и исклю­ чений. Материал этой части необходим также для освоения стандартной библио­ теки.

В третьей части дано описание средств стандартной библиотеки С+-ь. Рассматри­ ваются потоки, контейнеры, итераторы, векторы, стандартные алгоритмы и связи между ними.

Определения синтаксических элементов языка выделены в тексте книги полу­ жирным шрифтом.

В приложениях содержится большое количество справочной информации, необ­ ходимой при написании любой программы.

Все ключевые слова, типы, константы, функции, макросы и классы, описанные в книге, можно найти по предметному указателю, что позволяет использовать ее и как справочник. Для удобства пользования после имени элемента в строке ука­ зателя через запятую приведено имя класса, к которому принадлежит данный элемент, или пояснение.

Пользуясь случаем, хочу выразить глубокую благодарность моим первым препо­ давателям программирования П. А. Шипилову и Г. Л. Голованевскому, сотруд­ нику издательства «Питер» А. В. Васильеву, коллегам А. В. Лаздину, Ю. А. Щупаку и И. Яковлеву, всем корифеям, упомянутым в списке литературы, а также моим родителям и сыну Евгению, нарисовавшему картинки.

Книга поддержана проектом «Разработка концепции и научно-методического обеспечения регионального центра целевой подготовки разработчиков про­ граммного обеспечения и компьютерных технологий» программы Министерства образования Российской Федерации «Государственная поддержка региональной научно-технической политики высшей школы и развития ее научного потенциа­ ла» на 2001 год. В основу книги положен курс лекций, читаемый автором в Санкт-Петербургском государственном институте точной механики и оптики (техническом университете).

Доброжелательную и конструктивную критику книги направляйте по адресам [email protected] или 2:5030/[email protected].

От издательства Ваши замечания, предложения, вопросы отправляйте по адресу электронной почты compiapiter.com (издательство «Питер», компьютерная редакция).

Мы будем рады узнать ваше мнение!

Подробную информацию о наших книгах вы найдете па Web-сайте издательства http://www.pi ter.com.

ЧАСТЬ I

Структурное программирование Традиционная технология программирования складывалась в условиях, когда основными потребителями программ были научные учреждения, вычисли­ тельные ресурсы были ограничены, а проблемы сопровождения по существу неизвестны. Основными критериями качества программы считались ее узко по­ нимаемая эффективность и компактность. Со временем сложность программ возросла настолько, что на их разработку уходили годы труда большого коллек­ тива, а в результате системы появлялись с опозданием и содержали тысячи оши­ бок.

Кризис программного обеспечения привел к необходимости создания нового способа создания программ, который снижал бы общие затраты на протяжении всего цикла программы, — от замысла до завершения эксплуатации. Такая техно­ логия появилась в начале 70-х годов и была названа структурным программиро­ ванием. В его основе лежит сочетание теории программирования и личного опы­ та высококвалифицированных программистов, а также учет современных требований к программам и промышленного характера их производства.

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

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

Структурный подход охватывает все стадии разработки проекта: спецификацию, проектирование, собственно программирование и тестирование.

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

Структурный подход к программированию позволил успешно создавать доста­ точно крупные проекты, но сложность программного обеспечения продолжала возрастать, и требовались все более развитые средства ее преодоления. Идеи струк­ турного программирования получили свое дальнейшее развитие в объектно-ори­ ентированном программировании (ООП) — технологии, позволяющей достичь простоты структуры и управляемости очень крупных программных систем.

Не существует единственного самого лучшего способа создания программ. Для решения задач разного рода и уровня сложности требуется применять разные технологии программирования. В простейших случаях достаточно освоить азы структурного написания программ. Для создания же сложных проектов требует­ ся не только свободно владеть языком в полном объеме, но и иметь представле­ ние о принципах проектирования и отладки программ, возможностях стандарт­ ной и других библиотек и т. д. Как правило, чем сложнее задача, тем больше времени требуется на освоение инструментов, необходимых для ее решения.

Мы начнем с изучения состава и основных конструкций языка — предмета для написания программ необходимого, но далеко не достаточного.

ГЛАВА Базовые средства языка C++ Состав языка В тексте на любом естественном языке можно выделить четыре основных эле­ мента: символы, слова, словосочетания и предложения. Подобные элементы со­ держит и алгоритмический язык, только слова называют лексемами (элементар­ ными конструкциями), словосочетания — выражениями, а предложения — операторами. Лексемы образуются из символов, выражения — из лексем и сим­ волов, а операторы — из символов, выражений и лексем (рис. 1.1):

• Алфавит языка, или его символы — это основные неделимые знаки, с по­ мощью которых пишутся все тексты на языке.

• Лексема, или элементарная конструкция, — минимальная единица языка, имеющая самостоятельный смысл.

• Выражение задает правило вычисления некоторого значения.

• Оператор задает законченное описание некоторого действия.

Для описания сложного действия требуется последовательность операторов.

Операторы могут быть объединены в составной оператор, или блок^. В этом слу­ чае они рассматриваются как один оператор.

Операторы бывают исполняемые и неисполняемые. Исполняемые операторы зада­ ют действия над данными. Неисполняемые операторы служат для описания дан­ ных, поэтому их часто называют операторами описания или просто описаниями.

Каждый элемент языка определяется синтаксисом и семантикой. Синтаксиче­ ские определения устанавливают правила построения элементов языка, а семан­ тика определяет их смысл и правила использования.

Объединенная единым алгоритмом совокупность описаний и операторов образу­ ет программу на алгоритмическом языке. Для того чтобы выполнить программу, требуется перевести ее на язык, понятный процессору — в машинные коды. Этот процесс состоит из нескольких этапов. Рисунок 1,2 иллюстрирует эти этапы для языка С-ь+.

Сначала программа передается препроцессору, который выполняет директивы, содержащиеся в ее тексте (например, включение в текст так называемых заголо­ вочных файлов — текстовых файлов, в которых содержатся описания используе­ мых в программе элементов).

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

Компоновщик, или редактор связей, формирует исполняемый модуль программы, подключая к объектному модулю другие объектные модули, в том числе содер­ жащие функции библиотек, обращение к которым содержится в любой програм­ ме (например, для осуществления вывода на экран). Если программа состоит из нескольких исходных файлов, они компилируются по отдельности и объединя­ ются на этапе компоновки. Исполняемый модуль имеет расширение. ехе и запус­ кается на выполнение обычным образом.

Для описания языка в документации часто используется некоторый формаль­ ный метаязык, например, формулы Бэкуса—Наура или синтаксические диаграм­ мы. Для наглядности и простоты изложения в этой книге используется широко распространенный неформальный способ описания, при котором необязатель­ ные части синтаксических конструкций заключаются в квадратные скобки, текст, который необходимо заменить конкретным значением, пишется по-рус­ ски, а выбор одного из нескольких элементов обозначается вертикальной чертой.

Например, запись означает, что вместо конструкции имя необходимо указать конкретное имя в со­ ответствии с правилами языка, а перед ним может находиться либо void, либо int, либо ничего. Фигурные скобки используются для группировки элементов.

^ Блоком в языке С-ы- считается последовательность операторов, заключенная в фигур­ ные скобки { }.

Глава 1. Базовые средства языка C++ из которых требуется выбрать только один. В тех случаях, когда квадратные скобки являются элементом синтаксиса, это оговаривается особо.

Начнем изучение C++ с самого простого — с алфавита, а затем, осваивая все бо­ лее сложные элементы, постепенно углубимся в дебри объектно-ориентирован­ ного программирования и постараемся в них не заблудиться. К концу изучения этой книги читателю будет легко и приятно порассуждать об «инкапсулирован­ ных абстракциях с четким протоколом доступа», о том, отчего нельзя «сгенери­ ровать исключение, если конструктор копии объекта не является общедоступ­ ным», и о многих столь же интересных вещах.

Алфавит языка Алфавит C++ включает:

• прописные и строчные латинские буквы и знак подчеркивания;

• арабские цифры от О до 9;

• специальные знаки:

• пробельные символы: пробел, символы табуляции, символы перехода на но­ вую строку.

Из символов алфавита формируются лексемы языка:

• идентификаторы;

• ключевые (зарезервированные) слова;

• знаки операций;

• константы;

• разделители (скобки, точка, запятая, пробельные символы).

Границы лексем определяются другими лексемами, такими, как разделители или знаки операций.

Идентификаторы Идентификатор — это имя программного объекта. В идентификаторе могут ис­ пользоваться латинские буквы, цифры и знак подчеркивания. Прописные и строчные буквы различаются, например, sysop, SySoP и S S P — три различных имени. Первым символом идентификатора может быть буква или знак подчерки­ вания, но не цифра. Пробелы внутри имен не допускаются.

СОВЕТ Для улучшения читаемости программы следует давать объектам осмысленные имена. Су­ ществует соглашение о правилах создания имен, называемое венгерской нотацией (по­ скольку предложил ее сотрудник компании Microsoft венгр по национальности), по которому каждое слово, составляющее идентификатор, начинается с прописной буквы, а вначале ставится префикс, соответствующий типу величины, например, iMaxLength, IpfnSetFirstDialog. Другая традиция — разделять слова, составляющие имя, знаками под­ черкивания: maxjength, number_of_galosh.

Длина идентификатора по стандарту не ограничена, но некоторые компиляторы и компоновщики налагают на нее ограничения. Идентификатор создается на эта­ пе объявления переменной, функции, типа и т. п., после этого его можно исполь­ зовать в последующих операторах программы. При выборе идентификатора не­ обходимо иметь в виду следующее:

• идентификатор не должен совпадать с ключевыми словами (см. следующий раздел) и именами используемых стандартных объектов языка;

• не рекомендуется начинать идентификаторы с символа подчеркивания, по­ скольку они могут совпасть с именами системных функций или переменных, и, кроме того, это снижает мобильность программы;

Глава 1. Базовые средства языка C++ • на идентификаторы, используемые для определения внешних переменных, налагаются ограничения компоновщика (использование различных компоновщ;иков или версий компоновщика накладывает разные требования на име­ на внешних переменных).

Ключевые слова Ключевые слова — это зарезервированные идентификаторы, которые имеют спе­ циальное значение для компилятора. Их можно использовать только в том смыс­ ле, в котором они определены. Список ключевых слов C++ приведен в табл. 1.1.

Таблица 1. 1. Список ключевых слов C++ continue dynamic_cast namespace Знаки операций Знак операции — это один или более символов, определяющих действие над опе­ рандами. Внутри знака операции пробелы не допускаются. Операции делятся на унарные, бинарные и тернарную по количеству участвующих в них операндов.

Один и тот же знак может интерпретироваться по-разному в зависимости от кон­ текста. Все знаки операций за исключением [ ], ( ) и ? : представляют собой от­ дельные лексемы.

Знаки операций C++ описаны в разделе ^-Юперации», с. 31, а также приведены в приложении 1. Большинство стандартных операций может быть переопределено (перегружено). Перегрузка операций рассмотрена на с. 189.

Константы Константами называют неизменяемые величины. Различаются целые, вещест­ венные, символьные и строковые константы. Компилятор, выделив константу в качестве лексемы, относит ее к одному из типов по ее внешнему виду^.

Форматы констант, соответствующие каждому типу, приведены в табл. 1.2.

Таблица 1.2. Константы в языке C++ цифры (0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F) [цифры][.][цифры]{Е|е}[+|-][цифры]^ Символьная Строковая Допустимые диапазоны значений целых и вещественных констант приведены в табл. 1.4.

Если требуется сформировать отрицательную целую или вещественную кон­ станту, то перед константой ставится знак унарной операции изменения знака (-), например:

-218, -022, -ОхЗС, -4.8, -0.1е4.

Вещественная константа в экспоненциальном формате представляется в виде л/антиссы и порядка. Мантисса записывается слева от знака экспоненты (Е или е), по­ рядок — справа от знака. Значение константы определяется как произведение мантиссы и возведенного в указанную в порядке степень числа 10. Обратите вни­ мание, что пробелы внутри числа не допускаются, а для отделения целой части от дробной используется не запятая, а точка.

^ Программист может задать тип константы самостоятельно (см. стр. 24).

^ Могут быть опущены либо целая часть, либо дробная, но не обе сразу.

^ Могут быть опущены либо целая часть, либо дробная, но не обе сразу. Если указаны обе части, символ точки обязателен.

Глава 1. Базовые средства языка C++ Символьные константы, состоящие из одного символа, занимают в памяти один байт и имеют стандартный тип char. Двухсимвольные константы занимают два байта и имеют тип 1nt, при этом первый символ размещается в байте с меньшим адресом (о типах данных рассказывается в следующем разделе).

Символ обратной косой черты используется для представления:

• кодов, не имеющих графического изображения (например, \а — звуковой сиг­ нал, \п — перевод курсора в начало следующей строки);

• символов апострофа ( ' ), обратной косой черты ( \ ), знака вопроса ( ? ) и ка­ • любого символа с помощью его шестнадцатеричного или восьмеричного кода, например, \073, \0xF5. Числовое значение должно находиться в диапазоне Последовательности символов, начинающиеся с обратной косой черты, называ­ ют управляющими, или escape'Последователь7юстями. В таблице 1.3 приведены их допустимые значения. Управляющая последовательность интерпретируется как одиночный символ. Если непосредственно за обратной косой чертой следует символ, не предусмотренный табл. 1.3, результат интерпретации не определен.

Если в последовательности цифр встречается недопустимая, она считается кон­ цом цифрового кода.

Таблица 1.3. Управляющие последовательности в языке C++ Изображение Шестнадцатеричный код Наименование \Oddcl • \Oxddd Управляющие последовательности могут использоваться и в строковых констан­ тах, называемых иначе строковыми литералами. Например, если внутри строки требуется записать кавычку, ее предваряют косой чертой, по которой компиля­ тор отличает ее от кавычки, ограничивающей строку:

"Издательский дом \"Питер\"" Все строковые литералы рассматриваются компилятором как различные объекты.

Строковые константы, отделенные в программе только пробельными символами, при компиляции объединяются в одну. Длинную строковую константу можно разместить на нескольких строках, используя в качестве знака переноса обрат­ ную косую черту, за которой следует перевод строки. Эти символы игнорируют­ ся компилятором, при этом следующая строка воспринимается как продолжение предыдущей. Например, строка "Никто не доволен своей \ внешностью, но все довольны \ своим умом" полностью эквивалентна строке "Никто не доволен своей внешностью, но все довольны своим умом" В конец каждого строкового литерала компилятором добавляется нулевой символ, представляемый управляющей последовательностью \0. Поэтому длина строки всегда на единицу больше количества символов в ее записи. Таким обра­ зом, пустая строка "" имеет длину 1 байт. Обратите внимание на разницу между строкой из одного символа, например, "А", и символьной константой 'А'.

Пустая символьная константа недопустима.

Комментарии Комментарий либо начинается с двух символов «прямая косая черта» (//) и за­ канчивается символом перехода на новую строку, либо заключается между сим­ волами-скобками /* и */. Внутри комментария можно использовать любые до­ пустимые на данном компьютере символы, а не только символы из алфавита языка C++, поскольку компилятор комментарии игнорирует. Вложенные ком­ ментарии-скобки стандартом не допускаются, хотя в некоторых компиляторах разрешены.

СОВЕТ Рекомендуется использовать для пояснений //-комментарии, а скобки /* */ применять для временного исключения блоков кода при отладке. Советы по содержанию коммента­ риев см. в разделе «Кодирование и документирование программы», с. 102.

Типы данных С++ Концепция типа данных Основная цель любой программы состоит в обработке данных. Данные различ­ ного типа хранятся и обрабатываются по-разному. В любом алгоритмическом языке каждая константа, переменная, результат вычисления выражения или функции должны иметь определенный тип.

Тип данных определяет:

• внутреннее представление данных в памяти компьютера;

• множество значений, которые могут принимать величины этого типа;

• операции и функции, которые можно применять к величинам этого тина.

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

Все типы языка C++ можно разделить на основные и составные. В языке C++ оп­ ределено шесть основных типов данных для представления целых, веществен­ ных, символьных и логических величин. На основе этих типов программист мо­ жет вводить описание составных типов. К ним относятся массивы, перечисления, функции, структуры, ссылки, указатели, объединения и классы.

Основные типы данных Основные {стандартные) типы данных часто называют арифметическими, по­ скольку их можно использовать в арифметических операциях. Для описания ос­ новных типов определены следующие ключевые слова:

• int (целый);

• char (символьный);

• wchar^t (расширенный символьный);

• bool (логический);

• float (веществе1Н1ЫЙ);

• double (вещественный с двойной точностью).

Первые четыре тина называют целочисленными {целыми), последние два — ти­ пами с плавающей точкой. Код, который формирует компилятор для обработки целых величин, отличается от кода для величин с плавающей точкой.

Существует четыре спецификатора типа, уточняющих внутреннее представле­ ние и диапазон значений стандартных типов:

• short (короткий);

• long (длинный);

• signed (знаковый);

• unsigned (беззнаковый).

Целый тип (int) Размер типа int не определяется стандартом, а зависит от компьютера и компи­ лятора. Для 16-разрядного процессора под величины этого типа отводится 2 бай­ та, для 32-разрядного — 4 байта.

Спецификатор short перед именем типа указывает компилятору, что под число требуется отвести 2 байта независимо от разрядности процессора. Спецификатор long означает, что целая величина будет занимать 4 байта. Таким образом, на 16-разрядном компьютере эквиваленты int и short int, а на 32-разрядном — int и long int.

Внутреннее представление величины целого типа — целое число в двоичном коде. При использовании спецификатора signed старший бит числа интерпрети­ руется как знаковый (О — положительное число, 1 — отрицательное). Специфи­ катор unsigned позволяет представлять только положительные числа, поскольку старший разряд рассматривается как часть кода числа. Таким образом, диапазон значений типа 1nt зависит от спецификаторов. Диапазоны значений величин це­ лого типа с различными спецификаторами для IBM PC-совместимых компьюте­ ров приведены в табл. 1.4.

По умолчанию все целочисленные типы считаются знаковыми, то есть специфи­ катор signed можно опускать.

Константам, встречающимся в программе, приписывается тот или иной тип в со­ ответствии с их видом. Если этот тип по каким-либо причинам не устраивает программиста, он может явно указать требуемый тип с помощью суффиксов L, (long) и и, U (unsigned). Например, константа 32L будет иметь тип long и зани­ мать 4 байта. Можно использовать суффиксы L и U одновременно, например, Ox22UL или 05LU.

ПРИМЕЧАНИЕ

Типы short int, long int, signed int и unsigned int можно сокращать до short, long, signed и unsigned соответственно.

Символьный тип (char) Под величину символьного типа отводится количество байт, достаточное для размещения любого символа из набора символов для данного компьютера, что и обусловило название типа. Как правило, это 1 байт. Тип char, как и другие целые типы, может быть со знаком или без знака. В величинах со знаком можно хра­ нить значения в диапазоне от -128 до 127. При использовании спецификатора unsigned значения могут находиться в пределах от О до 255. Этого достаточно для хранения любого символа из 256-символьного набора ASCH. Величины типа char применяются также для хранения целых чисел, не превышающих границы указанных диапазонов.

Расширенный символьный тип (wchar_t) Тип wchar_t предназначен для работы с набором символов, для кодировки кото­ рых недостаточно 1 байта, например, Unicode. Размер этого типа зависит от реа­ лизации; как правило, он соответствует типу short. Строковые константы типа wchar_t записываются с префиксом L, например, L"Gates".

Логический тип (bool) Величины логического типа могут принимать только значения true и false, яв­ ляющиеся зарезервированными словами. Внутренняя форма представления зна­ чения false — О (нуль). Любое другое значение интерпретируется как true. При преобразовании к целому типу true имеет значение 1.

Глава 1. Базовые средства языка C++ Типы с плавающей точкой (float, double и long double) Стандарт C++ определяет три типа данных для хранения вещественных значе­ ний: float, double и long double.

Типы данных с плавающей точкой хранятся в памяти компьютера иначе, чем це­ лочисленные. Внутреннее представление вещественного числа состоит из двух частей — мантиссы и порядка. В IBM PC-совместимых компьютерах величины типа float занимают 4 байта, из которых один двоичный разряд отводится под знак мантиссы, 8 разрядов под порядок и 23 под мантиссу. Мантисса — это чис­ ло, большее 1.0, но меньшее 2.0. Поскольку старшая цифра мантиссы всегда рав­ на 1, она не хранится.

Для величин типа double, занимающих 8 байт, под порядок и мантиссу отводится И и 52 разряда соответственно. Длина мантиссы определяет точность числа, а длина порядка — его диапазон. Как можно видеть из табл. 1.4, при одинаковом количестве байт, отводимом под величины типа float и long int, диапазоны их допустимых значений сильно различаются из-за внутренней формы представле­ ния.

Спецификатор long перед именем типа double указывает, что под величину отво­ дится 10 байт.

Константы с плавающей точкой имеют по умолчанию тип double. Можно явно указать тип константы с помощью суффиксов F, f (float) и L, 1 (long). Напри­ мер, константа 2E+6L будет иметь тип long double, а константа 1.82f — тип float.

Таблица 1.4. Диапазоны значений простых типов данных для IBM PC Для вещественных типов в таблице приведены абсолютные величины минималь­ ных и максимальных значений.

Для написания переносимых на различные платформы программ нельзя делать предположений о размере типа int. Для его получения необходимо пользоваться операцией sizeof, результатом которой является размер типа в байтах. Напри­ мер, для операционной системы MS-DOS sizeof (int) даст в результате 2, а для Windows 9Х или OS/2 результатом будет 4.

В стандарте ANSI диапазоны значений для основных типов не задаются, опреде­ ляются только соотношения между их размерами, например:

slzeof(float) < slzeof(double) < sizeofdong double) sizeof(char) < slzeof(short) < sizeofdnt) < sizeofdong)

ПРИМЕЧАНИЕ

Минимальные и максимальные допустимые значения для целых типов зависят от реали­ зации и приведены в заголовочном файле (), характеристики вещест­ венных типов — в файле (), а также в шаблоне класса numericlimits (см.

раздел «Другие средства стандартной библиотеки», с. 378, и приложение 5).

Различные виды целых и вещественных типов, различающиеся диапазоном и точностью представления данных, введены для того, чтобы дать программисту возможность наиболее эффективно использовать возможности конкретной аппа­ ратуры, поскольку от выбора типа зависит скорость вычислений и объем памяти.

Но оптимизированная для компьютеров какого-либо одного типа программа мо­ жет стать не переносимой на другие платформы, поэтому в общем случае следует избегать зависимостей от конкретных характеристик типов данных.

Тип void Кроме перечисленных, к основным типам языка относится тип void, но множест­ во значений этого типа пусто. Он используется для определения функций, кото­ рые не возвращают значения, для указания пустого списка аргументов функции (о функциях рассказывается на с. 73), как базовый тип для указателей (с. 51) и в операции приведения типов (cf 56).

Структура программы Программа на языке C++ состоит из функций^ описаний и директив препроцессо­ ра (с. 16). Одна из функций должна иметь имя ma1 п. Выполнение программы на­ чинается с первого оператора этой функции. Простейшее определение функции имеет следующий формат:

тип_возвращаемого_значения имя ( [ параметры ] ) { операторы, составляющие тело функции Как правило, функция используется для вычисления какого-либо значения, по­ этому перед именем функции указывается его тип. О функциях рассказывается на с. 73, ниже приведены лишь самые необходимые сведения:

• если функция не должна возвращать значение, указывается тип void:

• тело функции является блоком и, следовательно, заключается в фигурные скобки;

• функции не могут быть вложенными;

• каждый оператор заканчивается точкой с запятой (кроме составного опера­ тора).

Пример структуры программы, содержащей функции main, fl и f2:

директивы препроцессора описания int ma1n(){ операторы главной функции int fl(){ операторы функции f l int f2(){ операторы функции f Программа может состоять из нескольких модулей (исходных файлов).

Несколько предварительных замечаний о вводе/выводе. В языке C++ нет встроен­ ных средств ввода/вывода — он осуществляется с помощью функций, типов и объектов, содержащихся в стандартных библиотеках. Используется два способа:

функции, унаследованные из языка С, и объекты C++.

Основные функции ввода/вывода в стиле С:

int scanf (const char* format.... ) // ввод int printf(const char* format.... ) // вывод Они выполняют форматированный ввод и вывод произвольного количества ве­ личин в соответствии со строкой формата format. Строка формата содержит сим­ волы, которые при выводе копируются в поток (на экран) или запрашиваются из потока (с клавиатуры) при вводе, и спецификации преобразования, начинающие­ ся со знака %, которые при вводе и выводе заменяются конкретными величина­ ми. Список наиболее употребительных спецификаций преобразования приведен в приложении 2.

Пример программы, использующей функции ввода/вывода в стиле С:

#1nclude 1nt main(){ printf("Введите целое число\п"):

scafifC'^d". &1):

printf("Вы ввели число ^d. спасибо!", i):

Первая строка этой программы — директива препроцессора, по которой в текст программы вставляется заголовочный файл, содержащий описание ис­ пользованных в программе функций ввода/вывода (в данном случае угловые скобки являются элементом языка). Все директивы препроцессора начинаются со знака #. Директивы препроцессора описаны на с. 93.

Третья строка — описание переменной целого типа с именем i. Переменные рас­ сматриваются на с. 28.

Функция printf в четвертой строке выводит приглашение «Введите целое чис­ ло» и переходит на новую строку в соответствии с управляющей последовательЧасть I. Структурное программирование ностью \п. Функция scanf заносит введенное с клавиатуры целое число в пере­ менную 1 (знак & означает операцию получения адреса), а следующий оператор выводит на экран указанную в нем строку, заменив спецификацию преобразова­ ния на значение этого числа. Ввод/вывод в стиле С рассмотрен в разделе «Функ­ ции ввода/вывода» (с. 88).

А вот как выглядит та же программа с использованием библиотеки классов C++:

int main(){ cout « "Введите целое число\п":

cout « "Вы ввели число " « 1 « ". спасибо!":

Заголовочный файл содержит описание набора классов для управле­ ния вводом/вывод:.;. В нем определены стандартные объекты-потоки с1п для ввода с клавиатуры и cout для вывода на экран, а также операции помещения в поток « и чтения из потока ». Потоки рассмотрены в разделе «Потоковые клас­ сы» на с. 265.

В дальнейшем изложении будут использоваться оба способа, но в одной про­ грамме смешивать их не рекомендуется.

Переменные и выражения В любой программе требуется производить вычисления. Для вычисления значе­ ний используются выражения, которые состоят из операндов, знаков операций и скобок. Операнды задают данные для вычислений. Операции задают действия, которые необходимо выполнить. Каждый операнд является, в свою очередь, вы­ ражением или одним из его частных случаев, например, константой или перемен­ ной. Операции выполняются в соответствии с приоритетами. Для изменения по­ рядка выполнения операций используются круглые скобки.

Рассмотрим составные части выражений и правила их вычисления.

Переменные Переменная — это именованная область памяти, в которой хранятся данные оп­ ределенного типа. У переменной есть имя и значение. Имя служит для обраще­ ния к области памяти, в которой хранится значение. Во время выполнения про­ граммы значение переменной можно изменять. Перед использованием любая переменная должна быть описана.

Пример описания целой переменной с именем а и вещественной переменной х:

1nt а; float х;

Общий вид оператора описания переменных:

[класс памяти] [const] тип и я [инициализатор];

Рассмотрим правила задания составных частей этого оператора.

• Необязательный класс памяти может принимать одно из значений auto, extern, static и register. О них рассказывается чуть ниже.

• Модификатор const показывает, что значение переменной изменять нельзя.

Такую переменную называют именованной константой, или просто констан­ • При описании можно присвоить переменной начальное значение, это называ­ ется инициализацией. Инициализатор можно записывать в двух формах — со знаком равенства:

" значение или в круглых скобках:

( значение ) Константа должна быть инициализирована при объявлении. В одном операторе можно описать несколько переменных одного типа, разделяя их запятыми.

Примеры:

const char С = ' С ; // символьная константа С char s. sf = 'f ; // инициализация относится только к sf char t (54);

float с = 0.22. x(3). sum;

Если тип инициализирующего значения не совпадает с типом переменной, вы­ полняются преобразования типа по определенным правилам (см. с. 38 и прило­ жение 3).

Описание переменной, кроме типа и класса памяти, явно или по умолчанию за­ дает ее область действия. Класс памяти и область действия зависят не только от собственно описания, но и от места его размещения в тексте программы.

Область действия идентификатора — это часть программы, в которой его мож­ но использовать для доступа к связанной с ним области памяти. В зависимости от области действия переменная может быть локальной или глобальной.

Если переменная определена внутри блока (напомню, что блок ограничен фигур­ ными скобками), она называется локальной, область ее действия — от точки описа­ ния до конца блока, включая все вложенные блоки. Если переменная определена вне любого блока, она называется глобальной и областью ее действия считается файл, в котором она определена, от точки описания до его конца.

Класс памяти определяет время жизни и область видимости программного объ­ екта (в частности, переменной). Если класс памяти не указан явным образом, он определяется компилятором исходя из контекста объявления.

Время жизни может быть постоянным (в течение выполнения программы) и вре­ менным (в течение выполнения блока).

Областью видимости идентификатора называется часть текста программы, из которой допустим обычный доступ к связанной с идентификатором областью па­ мяти. Чаще всего область видимости совпадает с областью действия. Исключе­ нием является ситуация, когда во вложенном блоке описана переменная с таким же именем. В этом случае внешняя переменная во вложенном блоке невидима, хотя он и входит в ее область действия. Тем не менее к этой переменной, если она глобальная, можно обратиться, используя операцию доступа к области видимо­ сти ::.

Для задания класса памяти используются следующие спецификаторы:

auto — автоматическая переменная. Память под нее выделяется в стеке и при не­ обходимости инициализируется каждый раз при выполнении оператора, содер­ жащего ее определение. Освобождение памяти происходит при выходе из блока, в котором описана переменная. Время ее жизни — с момента описания до конца блока. Для глобальных переменных этот спецификатор не используется, а для локальных он принимается по умолчанию, поэтому задавать его явным образом большого смысла не имеет.

extern — означает, что переменная определяется в другом месте программы (в другом файле или дальше по тексту). Используется для создания переменных, доступных во всех модулях программы, в которых они объявлены i. Подробнее об употреблении внешних переменных рассказывается в разделе «Внешние объяв­ ления», с. 98.

static — статическая переменная. Время жизни — постоянное. Инициализиру­ ется один раз при первом выполнении оператора, содержащего определение пе­ ременной. В зависимости от расположения оператора описания статические пе­ ременные могут быть глобальными и локальными. Глобальные статические переменные видны только в том модуле, в котором они описаны.

register — аналогично auto, но память выделяется по возможности в регистрах процессора. Если такой возможности у компилятора нет, переменные обрабаты­ ваются как auto.

int main(){ extern int X: / / 3 переменная x определена в другом месте static int с: // 4 локальная статическая переменная с В этом примере глобальная переменная а определена вне всех блоков. Память под нее выделяется в сегменте данных в начале работы программы, областью действия является вся программа. Область видимости — вся программа, кроме строк 6-8, так как в первой из них определяется локальная переменная с тем же именем, область действия которой начинается с точки ее описания и заканчиваЕсли переменная в том же операторе инициализируется, спецификатор extern игнориру­ ется.

ется при выходе из блока. Переменные b и с — локальные, область их видимо­ сти — блок, но время жизни различно: память под b выделяется в стеке при входе в блок и освобождается при выходе из него, а переменная с располагается в сег­ менте данных и существует все время, пока работает программа.

Если при определении начальное значение переменных явным образом не зада­ ется, компилятор присваивает глобальным и статическим переменным нулевое значение соответствующего типа. Автоматические переменные не инициализи­ руются.

Имя переменной должно быть уникальным в своей области действия (например, в одном блоке не может быть двух переменных с одинаковыми именами). Более подробно области действия рассматриваются на с. 97.

СОВЕТ Не жалейте времени на придумывание подходящих имен. Имя должно отражать смысл хранимой величины, быть легко распознаваемым и, желательно, не содержать символов, которые можно перепутать друг с другом, например, 1,1 (строчная L) или I (прописная i).

Для разделения частей имени можно использовать знак подчеркивания. Не давайте пере­ менным имена, демонстрирующие знание иностранного слэнга — ведь «как вы лодку назо­ вете, так она и поплывет». Как правило, переменным с большой областью видимости даются более длинные имена (желательно с префиксом типа), а для переменных, вся жизнь которых проходит на протяжении нескольких строк исходного текста, хватит и од­ ной буквы с комментарием при объявлении.

Описание переменной может выполняться в форме объявления или определения.

Объявление информирует компилятор о типе переменной и классе памяти, а оп­ ределение содержит, кроме этого, указание компилятору выделить память в со­ ответствии с типом переменной. В C++ большинство объявлений являются од­ новременно и определениями. В приведенном выше примере только описание является объявлением, но не определением.

Переменная может быть объявлена многократно, но определена только в одном месте программы, поскольку объявление просто описывает свойства переменной, а определение связывает ее с конкретной областью памяти.

Операции в таблице 1.5 приведен список основных операций, определенных в языке C++, в соответствии с их приоритетами (по убыванию приоритетов, операции с разны­ ми приоритетами разделены чертой). Остальные операции будут вводиться по мере изложения. Полный список операций приведен в приложении 1.

В соответствии с количеством операндов, которые используются в операциях, они делятся на унарные (один операнд), бинарные (два операнда) и тернарную (три операнда).

Все приведенные в таблице операции, кроме условной и sizeof, могут быть пере­ гружены. О перегрузке операций рассказывается на с. 189.

Таблица 1.5. Основные операции языка C++ 1 Операция 1 Унарные операции sizeof delete (type) Бинарные и тернарная операции Пробелы между символами внутри операции не допускаются.

Глава 1. Базовые средства языка C++ 1 Операция Рассмотрим основные операции подробнее.

Операции увеличения и уменьшения на 1 (++ и —). Эти операции, называемые также инкрементом и декрементом, имеют две формы записи — префиксную, когда операция записывается перед операндом, и постфиксную. В префиксной форме сначала изменяется операнд, а затем его значение становится результи­ рующим значением выражения, а в постфиксной форме значением выражения является исходное значение операнда, после чего он изменяется.

#include int main(){ printfC"Значение префиксного выражения: ^d\n". ++х):

printf("Значение постфиксного выражения: М\п", у++);

pr1ntf("Значение х после приращения: ^d\n". х);

printfC"Значение у после приращения: ^d\n". у ) ;

Результат работы программы:

Значение префиксного выражения: Значение постфиксного выражения: Значение х после приращения: Значение у после приращения: Операндом операции инкремента в общем случае является так называемое Ь-значение (L'value). Так обозначается любое выражение, адресующее некоторый уча­ сток памяти, в который можно занести значение. Название произошло от опера­ ции присваивания, поскольку именно ее левая (Left) часть определяет, в какую область памяти будет занесен результат операции. Переменная является част­ ным случаем L-значения.

Операция определения размера sizeof предназначена для вычисления размера объекта или типа в байтах, и имеет две формы:

sizeof выражение Пример:

int ma1n(){ cout « "sizeof (float) : « sizeof (float):

cout « "\nsizeof (x + 1.0) : « sizeof (x +1.0):

Результат работы профаммы:

sizeof (float) : Последний результат связан с тем, что вещественные константы по умолчанию имеют тип doubl е, к которому, как к более длинному, приводится тип переменной X и всего выражения. Скобки необходимы для того чтобы выражение, стоящее в них, вычислялось раньше операции приведения типа, имеющей больший при­ оритет, чем сложение.

Операции отрицания (-, I и ~). Арифметическое отрицание (унарный минус - ) изменяет знак операнда целого или вещественного типа на противоположный.

Логическое отрицание (!) дает в результате значение О, если операнд есть истина (не нуль), и значение 1, если операнд равен нулю. Операнд должен быть целого или вещественного типа, а может иметь также тип указатель. Поразрядное отри­ цание (--), часто называемое побитовым, инвертирует каждый разряд в двоичном представлении целочисленного операнда.

Деление (/) и остаток от деления (%). Операция деления применима к операндам арифметического типа. Если оба операнда целочисленные, результат операции округляется до целого числа, в противном случае тип результата определяется правилами преобразования (см. раздел «Выражения», с. 38, и приложение 3). Опе­ рация остатка от деления применяется только к целочисленным операндам. Знак результата зависит от реализации.

int main(){ printf("Результаты деления: ^d ^f\n". x/y. x/z):

printf("Остаток: ^d\n". x^y):

Результат работы программы:

Результаты деления: 2 2. Остаток: Операции сдвига ( « и » ) применяются к целочисленным операндам. Они сдвигают двоичное представление первого операнда влево или вправо на количе­ ство двоичных разрядов, заданное вторым операндом. При сдвиге влево ( « ) осво­ бодившиеся разряды обнуляются. При сдвиге вправо (>) освободившиеся биты заполняются нулями, если первый операнд беззнакового типа, и знаковым разря­ дом в противном случае. Операции сдвига не учитывают переполнение и потерю значимости.

Операции отношения (=. =. !=) сравнивают первый операнд со вто­ рым. Операнды могут быть арифметического типа или указателями. Результатом операции является значение true или false (любое значение, не равное нулю, ин­ терпретируется как true). Операции сравнения на равенство и неравенство име­ ют меньший приоритет, чем остальные операции сравнения.

ВНИМАНИЕ

Обратите внимание па разницу между операцией проверки на равенство (=) и операцией присваивания (=), результатом которой является значение, присвоенное левому операнду.

Поразрядные операции (&, |, ^) применяются только к целочисленным операн­ дам и работают с их двоичными представлениями. При выполнении операций операнды сопоставляются побитово (первый бит первого операнда с первым би­ том второго, второй бит первого операнда со вторым битом второго, и т д.).

При поразрядной конъюнкции, или поразрядном И (опер^щия обозначается &) бит результата равен 1 только тогда, когда соответствующие биты обоих операндов равны 1.

При поразрядной дизъюнкции, или поразрядном ИЛИ (операция обозначается |) бит результата равен 1 тогда, когда соответствующий бит хотя бы одного из опе­ рандов равен 1.

При поразрядном исключающем ИЛИ (операция обозначается ^) бит результата равен 1 только тогда, когда соответствующий бит только одного из операндов ра­ вен 1.

#1nclucle 1nt main(){ Результат работы профаммы:

6^5 = Логические операции (&& и ||). Операнды логических операций И (&&) и ИЛИ (II) могут иметь арифметический тип или быть указателями, при этом операнды в каждой операции могут быть различных типов. Преобразования типов не произ­ водятся, каждый операнд оценивается с точки зрения его эквивалентности нулю (операнд, равный нулю, рассматривается как false, не равный нулю — как true).

Результатом логической операции является true или fal se. Результат операции логическое И имеет значение true только если оба операнда имеют значение true.

Результат операции логическое ИЛИ имеет значение true, если хотя бы один из операндов имеет значение true. Логические операции выполняются слева напра­ во. Если значения первого операнда достаточно, чтобы определить результат операции, второй операнд не вычисляется.

Операции присваивания (=, +=, -=, *= и т. д.). Операции присваивания могут использоваться в программе как законченные операторы.

Формат операции простого присваивания (*=):

операнд^! = операнд_ Первый операнд должен быть L-значением, второй — выражением. Сначала вы­ числяется выражение, стоящее в правой части операции, а потом его результат записывается в область памяти, указанную в левой части (мнемоническое прави­ ло: «присваивание — это передача данных "налево"»). То, что ранее хранилось в этой области памяти, естественно, теряется.

1nt main(){ Результат работы программы:

a=5b=5c=

ВНИМАНИЕ

При присваивании производится преобразование типа выражения к типу L-значения, что может привести к потере информации.

В сложных операциях присваивания ( +=, *«=, /== и т п.) при вычислении выраже­ ния, стоящего в правой части, используется и L-значение из левой части. Напри­ мер, при сложении с присваиванием ко второму операнду прибавляется первый, и результат записывается в первый операнд, то есть выражение а += Ь является более компактной записью выражения а « а + Ь.

Условная операция (?:). Эта операция тернарная, то есть имеет три операнда.

Ее формат:

операндах ? операнд_2 : операндов Первый операнд может иметь арифметический тип или быть указателем. Он оце­ нивается с точки зрения его эквивалентности нулю (операнд, равный нулю, рас­ сматривается как false, не равный пулю — как true). Если результат вычисления операнда 1 равен true, то результатом условной операции будет значение второго операнда, иначе — третьего операнда. Вычисляется всегда либо второй операнд, либо третий. Их тип может различаться. Условная операция является сокращен­ ной формой условного оператора if (он рассмотрен на с. 40).

#1nc1ude 1nt main(){ printfC"Наибольшее число: М ". max):

Результат работы программы:

Наибольшее число: Другой пример применения условной операции. Требуется, чтобы некоторая це­ лая величина увеличивалась на 1, если ее значение не превышает п, а иначе при­ нимала значение 1:

Не рассмотренные в этом разделе операции будут описаны позже.

Выражения Как уже говорилось выше, выражения состоят из операндов, знаков операций и скобок и используются для вычисления некоторого значения определенного типа. Каждый операнд является, в свою очередь, выражением или одним из его частных случаев — константой или переменной.

Примеры выражений:

(а + 0.12)/ (t * sin(x)-L05e4)/((2 * к + 2) * (2 * к + 3)) Операции выполняются в соответствии с приоритетами. Для изменения порядка выполнения операций используются круглые скобки. Если в одном выражении записагю несколько операций одинакового приоритета, унарные операции, условная операция и операции присваивания выполняются справа палево, остальные — сле­ ва направо. Например, а = b = с означает а = (Ь = с), а а + b + с означает (а + Ь) + с.

Порядок вычисления подвыражений внутри выражений не определен: например, нельзя считать, что в выражении (sin(x + 2) + cos(y + 1)) обращение к синусу бу­ дет выполнено раньше, чем к косинусу, и что х + 2 будет вычислено раньше, чем У+1.

Результат вычисления выражения характеризуется значением и типом. Напри­ мер, если а и b — переменные целого типа и описаны так:

то выражение а + b имеет значение 7 и тип 1nt, а выражение а = b имеет значение, равное помещенному в переменную а (в данному случае 5) и тип, совпадающий с типом этой переменной. Таким образом, в C++ допустимы выражения вида а = b = с: сначала вычисляется выражение b = с, а затем его результат становится правым операндом для операции присваивания переменной а.

В выражение могут входить операнды различных типов. Если операнды имеют одинаковый тип, то результат операции будет иметь тот же тип. Если операнды разного типа, перед вычислениями выполняются преобразования типов по опре­ деленным правилам, обеспечивающим преобразование более коротких типов в более длинные для сохранения значимости и точности.

Преобразования бывают двух типов:

• изменяющие внутреннее представление величин (с потерей точности или без потери точности);

• изменяющие только интерпретацию внутреннего представления.

К первому типу относится, например, преобразование целого числа в веществен­ ное (без потери точности) и наоборот (возможно, с потерей точности), ко второ­ му — преобразование знакового целого в беззнаковое.

В любом случае величины типов char, signed char, unsigned char, short int и unsigned short int преобразуются в тип int, если он может представить все значе­ ния, или в unsigned int в противном случае.

После этого операнды преобразуются к типу наиболее длинного из них, и он ис­ пользуется как тип результата. Правила преобразований приведены в приложе­ нии 3.

Программист может задать преобразования типа явным образом (об этом расска­ зывается в разделе «Преобразования типов» на с. 231).

Итак, мы потратили достаточно много времени, рассматривая самый нижний уровень конструкций, составляющих язык программирования, и теперь пора на­ чинать строить из них нечто более осмысленное — сначала отдельные операторы, а затем программы. Даже самая простая программа должна создаваться по опре­ деленным правилам, тогда она будет надежна, эффективна и красива. Изучением этих правил мы и займемся в следующем разделе.

Базовые конструкции структурного программирования В теории программирования доказано, что программу для решения задачи любой сложности можно составить только из трех структур, называемых следованием, ветвлением и циклом. Этот результат установлен Боймом и Якопини еще в 1966 году путем доказательства того, что любую программу можно преобразо­ вать в эквивалентную, состоящую только из этих структур и их комбинаций.

Следование, ветвление и цикл называют базовыми конструкциями структурного программирования. Следованием называется конструкция, представляющая со­ бой последовательное выполнение двух или более операторов (простых или соГлава 1. Базовые средства языка С++ ставных). Ветвление задает выполнение либо одного, либо другого оператора в зависимости от выполнения какого-либо условия. Цикл задает многократное вы­ полнение оператора (рис. 1.3). Особенностью базовых конструкций является то, что любая из них имеет только один вход и один выход, поэтому конструкции могут вкладываться друг в друга произвольным образом, например, цикл может содержать следование из двух ветвлений, каждое из которых включает вложен­ ные циклы (рис. 1.4).

Рис. 1.3. Базовые конструкции структурного программирования Целью использования базовых конструкций является получение программы простой структуры. Такую программу легко читать (а программы чаще прихоЧасть I. Структурное программирование дится читать, чем писать), отлаживать и при необходимости вносить в нее изме­ нения. Структурное профаммирование часто называли «программированием без goto», и в этом есть большая доля правды: частое использование операторов пе­ редачи управления в произвольные точки программы затрудняет прослеживание логики ее работы. С другой стороны, никакие принципы нельзя возводить в аб­ солют, и есть ситуации, в которых использование goto оправдано и приводит, на­ против, к упрощению структуры программы. О них говорится в разделе «Опера­ тор goto» (с. 49).

В большинстве языков высокого уровня существует несколько реализаций базо­ вых конструкций; в C++ есть три вида циклов и два вида ветвлений (на два и на произвольное количество направлений). Они введены для удобства программи­ рования, и в каждом случае надо выбирать наиболее подходящие средства. Глав­ ное, о чем нужно помнить даже при написании самых простых программ, — что они должны состоять из четкой последовательности блоков строго определенной конфигурации. «Кто ясно мыслит, тот ясно излагает» — практика давно показа­ ла, что программы в стиле «поток сознания» нежизнеспособны, не говоря о том, что они просто некрасивы.

Рассмотрим операторы языка, реализующие базовые конструкции структурного программирования.

Оператор «выражение»

Любое выражение, завершающееся точкой с запятой, рассматривается как опера­ тор, выполнение которого заключается в вычислении выражения. Частным слу­ чаем выражения является пустой оператор ; (он используется, когда по синтак­ сису оператор требуется, а по смыслу — нет). Примеры:

а* = b + с; // выполняется умножение с присваиванием Операторы ветвления Условный оператор if Условный оператор if используется для разветвления процесса вычислений на два направления. Структурная схема оператора приведена на рис. 1.5. Формат оператора:

i f ( выражение ) оператор_1; [else оператор_2;] Сначала вычисляется выражение, которое может иметь арифметический тип или тип указателя. Если оно не равно нулю (имеет значение true), выполняется пер­ вый оператор, иначе — второй. После этого управление передается на оператор, следующий за условным.

Одна из ветвей может отсутствовать, логичнее опускать вторую ветвь вместе с ключевым словом еТ se. Если в какой-либо ветви требуется выполнить несколь­ ко операторов, их необходимо заключить в блок, иначе компилятор не сможет понять, где заканчивается ветвление. Блок может содержать любые операторы, Глава 1. Базовые средства языка C++ в том числе описания и другие условные операторы (но не может состоять из од­ них описаний). Необходимо учитывать, что переменная, описанная в блоке, вне блока не существует.

Примеры:

if (а Maxlter){ if (flag) cout « "\пЗначение функции: " « у;

Оператор continue Оператор перехода к следующей итерации цикла continue пропускает все опера­ торы, оставшиеся до конца тела цикла, и передает управление на начало следую­ щей итерации.

Оператор return Оператор возврата из функции return завершает выполнение функции и переда­ ет управление в точку ее вызова. Вид оператора:

return [ выражение ];

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

Указатели и массивы Указатели Когда компилятор обрабатывает оператор определения переменной, например, int i=10:, он выделяет память в соответствии с типом (int) и инициализирует ее указанным значением (10). Все обращения в программе к переменной по ее име­ ни (i) заменяются компилятором на адрес области памяти, в которой хранится значение переменной. Программист может определить собственные переменные для хранения адресов областей памяти. Такие переменные называются указате­ лями.

Итак, указатели предназначены для хранения адресов областей памяти. В C++ различают три вида указателей — указатели на объект, на функцию и на void, от­ личающиеся свойствами и набором допустимых операций. Указатель не являет­ ся самостоятельным типом, он всегда связан с каким-либо другим конкретным типом.

Указатель на функцию содержит адрес в сегменте кода, по которому располагает­ ся исполняемый код функции, то есть адрес, по которому передается управление при вызове функции. Указатели на функции используются для косвенного вызо­ ва функции (не через ее имя, а через обращение к переменной, хранящей ее ад­ рес), а также для передачи имени функции в другую функцию в качестве пара­ метра. Указатель функции имеет тип «указатель функции, возвращающей значение заданного типа и имеющей аргументы заданного типа»:

тип (*имя) ( список_типов_аргументов );

Например, объявление:

1nt (*fun) (double, double);

задает указатель с именем fun на функцию, возвращающую значение типа int и имеющую два аргумента типа double.

Указатель на объект содержит адрес области памяти, в которой хранятся данные определенного типа (основного или составного). Простейшее объявление указа­ теля на объект (в дальнейшем называемого просто указателем) имеет вид:

где тип может быть любым, кроме ссылки (о ссылках рассказывается на с. 57) и битового поля (см. с. 69), причем тип может быть к этому моменту только объяв­ лен, но еще не определен (следовательно, в структуре, например, может присут­ ствовать указатель на структуру того же типа).

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

Размер указателя зависит от модели памяти. Можно определить указатель на указатель и т. д.

Указатель на void применяется в тех случаях, когда конкретный тип объекта, ад­ рес которого требуется хранить, не определен (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов раз­ личных типов).

Указателю на void можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями, но перед выполнением каких-либо дейст­ вий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом (см. с. 231).

Указатель может быть константой или переменной, а также указывать на кон­ станту или переменную. Рассмотрим примеры:

int * const ср = &i; // указатель-константа на целую переменную const i n t * const срс = &ci; // указатель-константа на целую константу Как видно из примеров, модификатор const, находящийся между именем указа­ теля и звездочкой, относится к самому указателю и запрещает его изменение, а const слева от звездочки задает постоянство значения, на которое он указывает.

Для инициализации указателей использована операция получения адреса &.

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

Инициализация указателей Указатели чаще всего используют при работе с динамической памятью, называе­ мой некоторыми эстетами кучей (перевод с английского языка слова heap). Это свободная память, в которой можно во время выполнения программы выделять место в соответствии с потребностями. Доступ к выделенным участкам динами­ ческой памяти, называемым динамическими переменными, производится только через указатели. Время жизни динамических переменных — от точки создания до конца программы или до явного освобождения памяти. В C++ используется два способа работы с динамической памятью. Первый использует семейство функций та 11 ос и достался в наследство от С, второй использует операции new и delete.

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

Существуют следующие способы инициализации указателя:

1. Присваивание указателю адреса существующего объекта:

• с помощью операции получения адреса:

• с помощью значения другого инициализированного указателя:

• с помощью имени массива или функции, которые трактуются как адрес (см.

«Массивы», с. 58, и «Передача имен функций в качестве параметров», с. 80):

2. Присваивание указателю адреса области памяти в явном виде:

char* vp = (char *)0хВ8000000:

Здесь ОхВ8000000 — шестнадцатеричная константа, (char *) — операция приве­ дения типа: константа преобразуется к типу «указатель на char».

3. Присваивание пустого значения:

1nt* SUXX = NULL;

В первой строке используется константа N L, определенная в некоторых за­ головочных файлах С как указатель, равный нулю. Рекомендуется использо­ вать просто О, так как это значение типа 1 nt будет правильно преобразовано стандартными способами в соответствии с контекстом. Поскольку гарантиру­ ется, что объектов с нулевым адресом нет, пустой указатель можно использо­ вать для проверки, ссылается указатель на конкретный объект или нет.

4. Выделение участка динамической памяти и присваивание ее адреса указателю:

• с помощью операции new:

• с помощью функции mal 1 oc^ 1nt* u = ( i n t *)malloc(s1zeof(int)); // В операторе 1 операция new выполняет выделение достаточного для размещения величины типа 1 nt участка динамической памяти и записывает адрес начала это­ го участка в переменную п. Память под саму переменную п (размера, достаточно­ го для размещения указателя) выделяется на этапе компиляции.

В операторе 2, кроме описанных выше действий, производится инициализация выделенной динамической памяти значением 10.

В операторе 3 операция new выполняет выделение памяти под 10 величин типа int (массива из 10 элементов) и записывает адрес начала этого участка в пере­ менную q. которая может трактоваться как имя массива. Через имя можно обра­ щаться к любому элементу массива. О массивах рассказывается на с. 58.

Если память выделить не удалось, по стандарту должно порождаться исключе­ ние bad^alloc (об исключениях рассказывается в разделе «Обработка исключи­ тельных ситуаций», с. 222, а об обработке ошибок выделения памяти — на с. 378).

Старые версии компиляторов могут возвращать 0.

В операторе 4 делается то же самое, что и в операторе 1, но с помощью функции выделения памяти та 11 ос, унаследованной из библиотеки С, В функцию переда­ ется один параметр — количество выделяемой памяти в байтах. Конструкция (int*) используется ц,ля приведения типа указателя, возвращаемого функцией, к требуемому типу (о явном преобразовании типов см. с. 231). Если память выде­ лить не удалось, функция возвращает 0.

Операцию new использовать предпочтительнее, чем функцию та 11 ос, особенно при работе с объектами.

Освобождение памяти, выделенной с помощью операции new, должно выпол­ няться с помощью delete, а памяти, выделенной функцией та 11 ос — посредством ^ Для того чтобы использовать malloc, требуется подключить к программе заголовочный файл.

функции free. При этом переменная-указатель сохраняется и может инициали­ зироваться повторно. Приведенные выше динамические переменные уничтожа­ ются следующим образом:

delete п; delete m; delete [ ] q; free (u);

Если память выделялась с помощью new[], для освобождения памяти необходимо применять delete[]. Размерность массива при этом не указывается. Если квад­ ратных скобок нет, то никакого сообщения об ошибке не выдается, но помечен как свободный будет только первый элемент массива, а остальные окажутся не­ доступны для дальнейших операций. Такие ячейки памяти называются мусором.

ВНИМАНИЕ

Если переменная-указатель выходит из области своего действия, отведенная под нее па­ мять освобождается. Следовательно, динамическая переменная, на которую ссылался ука­ затель, становится недоступной. При эт^м память из-под самой динамической переменной не освобождается. Другой случай появления «мусора» — когда инициализированному указателю присваивается значение другого указателя. При этом старое значение бесслед­ но теряется.

С помощью комбинаций звездочек, круглых и квадратных скобок можно описы­ вать составные типы и указатели на составные типы, например, в операторе объявляется массив из 10 указателей на функции без параметров, возвращающих указатели на 1 nt.

По умолчанию квадратные и круглые скобки имеют одинаковый приоритет, больший, чем звездочка, и рассматриваются слева направо. Для изменения по­ рядка рассмотрения используются круглые скобки.

При интерпретации сложных описаний необходимо придерживаться правила «изнутри наружу»:

1) если справа от имени имеются квадратные скобки, это массив, если скобки круглые — это функция;

2) если слева есть звездочка, это указатель на проинтерпретированную ранее конструкцию;

3) если справа встречается закрывающая круглая скобка, необходимо приме­ нить приведенные выше правила внутри скобок, а затем переходить наружу;

4) в последнюю очередь интерпретируется спецификатор типа.

Для приведенного выше описания порядок интерпретации указан цифрами:

1nt *(*р[10])():

Операции с указателями с указателями можно выполнять следующие операции: разадресация, или кос­ венное обращение к объекту (*), присваивание, сложение с константой, вычита­ ние, инкремент (++), декремент (—), сравнение, приведение типов. При работе с указателями часто используется операция получения адреса (&).

Операция разадрееации, или разыменования, предназначена для доступа к ве­ личине, адрес которой хранится в указателе. Эту операцию можно использовать как для получения, так и для изменения значения величины (если она не объяв­ лена как константа):

char * р = new char: / * выделение памяти под указатель и под динамическую *р = 'Ю': а = *р: / / присваивание значения обеим переменным Как видно из примера, конструкцию *имя_указателя можно использовать в левой части оператора присваивания, так как она является L-значением (см. с. 33), то есть определяет адрес области памяти. Для простоты эту конструкцию можно считать именем переменной, на которую ссылается указатель. С ней допустимы все действия, определенные для величин соответствующего типа (если указатель инициализирован). На одну и ту же область памяти может ссылаться несколько указателей различного типа. Примененная к ним операция разадресации даст разные результаты. Например, программа 1nt main(){ unsigned long 1nt A = 0Xcc77ffaa:

unsigned short int* pint = (unsigned short int*) &A:

unsigned char* pchar = (unsigned char *) &A:

на IBM РС-совместимом компьютере выведет на экран строку^:

I cc77ffaa | ffaa | аа | Значения указателей pint и pchar одинаковы, но разадресация pchar дает в резуль­ тате один младший байт по этому адресу, а pint — два младших байта.

В приведенном выше примере при инициализации указателей были использова­ ны операции приведения типов. Синтаксис операции явного приведения типа прост: перед именем переменной в скобках указывается тип, к которому ее требу­ ется преобразовать. При этом не гарантируется сохранение информации, поэто­ му в общем случае явных преобразований типа следует избегать. Подробнее опе­ рация приведения типов рассмотрена на с. 231.

При смешивании в выражении указателей разных типов явное преобразование типов требуется для всех указателей, кроме void*. Указатель может неявно пре­ образовываться в значение тина boo! (например, в выражении условного опера­ тора), при этом ненулевой указатель преобразуется в true, а нулевой в false.

Присваивание без явного приведения типов допускается в двух случаях:

• указателям типа void*:

• если тип указателей справа и слева от операции присваивания один и тот же.

Старые версии компилятора могут выдать в результате строку | ffaa | сс77 | ffaa Таким образом, неявное преобразование выполняется только к типу void*. Зна­ чение О неявно преобразуется к указателю на любой тип. Присваивание указате­ лей на объекты указателям на функции (и наоборот) недопустимо. Запрещено и присваивать значения указателям-константам, впрочем, как и константам любо­ го типа (присваивать значения указателям на константу и переменным, на кото­ рые ссылается указатель-константа, допускается).

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

Инкремент перемещает указатель к следующему элементу массива, декремент — к предыдущему. Фактически значение указателя изменяется на величину sizeof (тип). Если указатель на определенный тип увеличивается или уменьшает­ ся на константу, его значение изменяется на величину этой константы, умножен­ ную на размер объекта данного типа, например:

Разность двух указателей — это разность их значений, деленная на размер типа в байтах (в применении к массивам разность указателей, например, на третий и шестой элементы равна 3). Суммирование двух указателей не допускается.

При записи выражений с указателями следует обращать внимание на приорите­ ты операций. В качестве примера рассмотрим последовательность действий, за­ данную в операторе *р++ = 10;

Операции разадресации и инкремента имеют одинаковый приоритет и выполня­ ются справа налево, но, поскольку инкремент постфиксный, он выполняется по­ сле выполнения операции присваивания. Таким образом, сначала по адресу, за­ писанному в указателе р, будет записано значение 10, а затем указатель будет увеличен на количество байт, соответствующее его типу. То же самое можно за­ писать подробнее:

Выражение (*р)++. напротив, инкрементирует значение, на которое ссылается указатель.

Унарная операция получения адреса & применима к величинам, имеющим имя и размещенным в оперативной памяти. Таким образом, нельзя получить адрес ска­ лярного выражения, неименованной константы или регистровой переменной.

Примеры операции приводились выше.

Ссылки Ссылка представляет собой синоним имени, указанного при инициализации ссылки. Ссылку можно рассматривать как указатель, который всегда разымено­ вывается. Формат объявления ссылки:

где тип — это тип величины, на которую указывает ссылка, & — оператор ссылки, означающий, что следующее за ним имя является именем переменной ссылочного типа, например:

Запомните следующие правила.

• Переменная-ссылка должна явно инициализироваться при ее описании, кро­ ме случаев, когда она является параметром функции (см. с. 77), описана как extern или ссылается на поле данных класса (см. с. 178).

• После инициализации ссылке не может быть присвоена другая переменная.

• Тип ссылки должен совпадать с типом величины, на которую она ссылается.

• Не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки.

Ссылки применяются чаще всего в качестве параметров функций и типов воз­ вращаемых функциями значений. Ссылки позволяют использовать в функциях переменные, передаваемые по адресу, без операции разадресации, что улучшает читаемость программы (подробнее об этом рассказывается в разделе «Парамет­ ры функции», с. 77).

Ссылка, в отличие от указателя, не занимает дополнительного пространства в па­ мяти и является просто другим именем величины. Операция над ссылкой приво­ дит к изменению величины, на которую она ссылается.

Массивы При использовании простых переменных каждой области памяти для хранения данных соответствует свое имя. Если с группой величин одинакового типа требу­ ется выполнять однообразные действия, им дают одно имя, а различают по по­ рядковому номеру. Это позволяет компактно записывать множество операций с помощью циклов. Конечная именованная последовательность однотипных вели­ чин называется массивом. Описание массива в программе отличается от описа­ ния простой переменной наличием после имени квадратных скобок, в которых задается количество элементов массива (размерность):

f!oat а [10]; / / описание массива из 10 вещественных чисел

ВНИМАНИЕ

При описании массивов квадратные скобки являются элементом синтаксиса, а не указани­ ем на необязательность конструкции.

Элементы массива нумеруются с нуля. При описании массива используются те же модификаторы (класс памяти, const и инициализатор), что и для простых пе­ ременных. Инициализирующие значения для массивов записываются в фигур­ ных скобках. Значения элементам присваиваются по порядку. Если элементов в массиве больше, чем инициализаторов, элементы, для которых значения не ука­ заны, обнуляются:

int b[5] = {3. 2. 1}; / / b[0]=3. b[l]=2. b[2]=l. b[3]=0. b[4]= Размерность массива вместе с типом его элементов определяет объем памяти, не­ обходимый для размещения массива, которое выполняется на этапе компиляции, поэтому размерность может быть задана только целой положительной констан­ той или константным выражением. Если при описании массива не указана раз­ мерность, должен присутствовать инициализатор, в этом случае компилятор вы­ делит память по количеству инициализирующих значений. В дальнейшем мы увидим, что размерность может быть опущена также в списке формальных пара­ метров (см. раздел «Параметры функции», с. 77).

Для доступа к элементу массива после его имени указывается номер элемента (индекс) в квадратных скобках. В следующем примере подсчитывается сумма элементов массива.

#include int ma1n(){ int marksCn] = {3. 4. 5. 4. 4};

Если первая ветвь оператора if содержит передачу управления, использовать else нет необходимости:

i f (is_equal > 0) {... break;} i f (1sequal < 0) {... return:} Бессмысленно использовать проверку на неравенство нулю (или, что еще хуже, на равенство true или fа! se):

bool is_busy;

i f (is_busy == true) {... } // плохо! Лучше i f (is_busy) i f (is^busy == false) {.... } // плохо! Лучше i f (!isbusy) char s[80]:

while (fgets(s) != NULL) {... } // плохо! Лучше while (fgets(s)) Если одна из ветвей условного оператора гораздо короче, чем другая, более ко­ роткую ветвь if лучше поместить сверху, иначе вся управляющая структура мо­ жет не цоместиться на экране, что затруднит отладку.

В некоторых случаях условная операция лучше условного оператора:

При использовании циклов надо стремиться объединять инициализацию у проверку условия выхода и приращение в одном месте. Рекомендации но выбору наиболее подходящего оператора цикла были приведены на с. 49. При записи итеративных циклов (в которых для проверки условия выхода используются соотношения переменных, формирующихся в теле цикла), необходимо предусматривать ава­ рийный выход но достижении заранее заданного максимального количества ите­ раций.

Необходимо проверять коды возврата ошибок и предусматривать печать сообще­ ний в тех точках программы, куда управление при нормальной работе програм­ мы передаваться не должно (именно это сообщение вы с большой вероятностью получите при первом же запуске программы). Например, оператор switch должен иметь слово default с обработкой ситуации по умолчанию, особенно если в нем перечислены все возможные значения переключателя.

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

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

После написания программу следует тщательно отредактировать (для этого по­ лезно представить себе, к примеру, что ее писал ваш злейший враг, которого вы хо­ тите уличить в полной некомпетентности) — убрать ненужные фрагменты, сгруп­ пировать описания, оптимизировать проверки условий и циклы, проверить, оптимально ли разбиение на функции и т. д. Подходить к написанию программы нужно таким образом, чтобы ее можно было в любой момент передать другому программисту. Полезно дать почитать свою программу кому-нибудь из друзей или коллег (а еще лучше — врагов или завистников), и в тех местах, которые не будут им понятны без устных комментариев, внести их прямо в текст программы.

Комментарии имеют очень важное значение, поскольку программист, как ни странно, чаще читатель, чем писатель. Даже если сопровождающим программи­ стом является автор программы, разбираться через год в плохо документирован­ ном тексте — сомнительное удовольствие.

Дальнейшие советы касаются комментариев и форматирования текста програм­ мы. Программы на C++ весьма плохо читаемы (лучше, чем программы на Perl, но хуже, чем на Pascal), и в них особенно важно придерживаться хорошего стиля при форматировании и документации.

«Программа без комментариев несопровождаема. Это... часовая бомба, а не ком­ пьютерная программа» [9]. Программа, если она используется, живет не один год, потребность в каких-то ее новых свойствах появляется сразу же после ввода в эксплуатацию, и сопровождение программы занимает гораздо больший проме­ жуток времени, чем ее написание. Основная часть документации должна нахо­ диться в тексте программы. Хорошие комментарии написать почти так же слож­ но, как и хорошую программу.

Комментарии должны представлять собой правильные предложения без сокраще­ ний и со знаками препинания^ и не должны подтверждать очевидное (коммента­ рии в этой книге не могут служить образцом, поскольку они предназначены для обучения, а не для сопровождения). Например, бессмысленны фразы типа «вы­ зов функции f» или «описание переменных». Если комментарий к фрагменту программы занимает несколько строк, лучше разместить его до фрагмента, чем справа от него, и выровнять по вертикали. Абзацный отступ комментария дол­ жен соответствовать отступу комментируемого блока.

и Комментарий, описывающий.

/ / что происходит в следующем ниже / / блоке программы.

{ / * Непонятный программы * / } Для разделения функций и других логически законченных фрагментов пользуй­ тесь пустыми строками или комментарием вида Вложенные блоки должны иметь отступ в 3-4 символа, причем блоки одного уровня вложенности должны быть выровнены по вертикали. Желательно, чтобы закрывающая фигурная скобка находилась строго под открывающей или в ко­ лонке, соответсвующей уровню вложенности блока. Форматируйте текст по столбцам везде, где это возможно:

#inc1ude #1nc1ude 1nt main(){ ^ Совсем хорошо, если они при этом не будут содержать орфографических ошибок.

for( i n t к = 0; kd = d: pv->next = 0; pv->prev = '^pend;

('*^pend)->next = pv;

// Поиск элемента по ключу Node * find(Node * const pbeg. int d){ // Удаление элемента bool remove(Node **pbeg. Node **pend. int key){ if(Node *pkey = find(*pbeg. key)){ (pkey->prev)->next = pkey->next;

(pkey->next)->prev = pkey->prev;} // Вставка элемента Node * insert (Node '^ const pbeg. Node ^'^^pend. int key. int d){ if(Node *pkey = find(pbeg. key)){ // 1 - установление связи нового узла с последующим:

pv->next = pkey->next;

// 2 - установление связи нового узла с предыдущим:

// 3 - установление связи предыдущего узла с новым:

// 4 - установление связи последующего узла с новым:

1f( ркеу != *репс1) (pv->next)->prev = pv;

/ / Обновление указателя на конец списка, / / если узел вставляется в конец:

return 0:

Результат работы программы:

Все параметры, не изменяемые внутри функций, должны передаваться с моди­ фикатором const. Указатели, которые могут измениться (например, при удале­ нии из списка последнего элемента указатель на конец списка требуется скор­ ректировать), передаются по адресу.

Рассмотрим подробнее функцию удаления элемента из списка remove. Ее парамет­ рами являются указатели на начало и конец списка и ключ элемента, подлежаще­ го удалению. В строке 1 выделяется память под локальный указатель ркеу, ко­ торому присваивается результат выполнения функции нахождения элемента по ключу f 1 nd. Эта функция возвращает указатель на элемент в случае успешного поиска и О, если элемента с таким ключом в списке нет. Если ркеу получает нену­ левое значение, условие в операторе 1 f становится истинным (элемент существу­ ет), и управление передается оператору 2, если нет — выполняется возврат из функции со значением false (оператор 6).

Удаление из списка происходит по-разному в зависимости от того, находится элемент в лачале списка, в середине или в конце. В операторе 2 проверяется, на­ ходится ли удаляемый элемент в начале списка — в этом случае следует скоррек­ тировать указатель pbeg на начало списка так, чтобы он указывал на следующий элемент в списке, адрес которого находится в поле next первого элемента. Новый начальный элемент списка должен иметь в своем поле указателя на предыдущий элемент значение 0.

Если удаляемый элемент находится в конце списка (оператор 3), требуется сме­ стить указатель pend конца списка на предыдущий элемент, адрес которого мож­ но получить из поля prev последнего элемента. Кроме того, нужно обнулить для нового последнего элемента указатель на следующий элемент. Если удаление происходит из середины списка, то единственное, что надо сделать, — обеспечить двустороннюю связь предыдущего и последующего элементов. После корректиpoBijH указателей память из-под элемента освобождается, и функция возвращает значение true.

Работа функции вставки элемента в список проиллюстрирована на рис. 3.2. Но­ мера около стрелок соответствуют номерам операторов в комментариях.

Сортировка связанного списка заключается в изменении связей между элемента­ ми. Алгоритм состоит в том, что исходный список просматривается, и каждый эле­ мент вставляется в новый список на место, определяемое значением его ключа.

Глава 3. Технология создания программ Ниже приведена функция формировапия упорядоченного списка (предполагается, что первый элемент существует):

void acld_sort(Node **pbeg. Node **pend. 1nt d){ Node *pv = new Node; / / добавляемый элемент i f (d < pt->d){ / / занести перед текущим элементом (pt) pv->prev = *pend:

(*pend)->next = pv:

Стеки Стек — это частный случай однонаправленного списка, добавление элементов в который и выборка из которого выполняются с одного конца, называемого вер­ шиной стека. Другие операции со стеком не определены. При выборке элемент исключается из стека. Говорят, что стек реализует принцип обслуживания LIFO (last in — first out, последним пришел — первым ушел). Стек проще всего пред­ ставить себе как закрытую с одного конца узкую трубу, в которую бросают мячи.

Достать первый брошенный мяч можно только после того, как вынуты все остальные. Кстати, сегмент стека назван так именно потому, что память под ло­ кальные переменные выделяется по принципу LIFO. Стеки широко применяют­ ся в системном программном обеспечении, компиляторах, в различных рекур­ сивных алгоритмах.

Ниже приведена программа, которая формирует стек из пяти целых чисел (1,2, 3, 4, 5) и выводит его на экран. Функция помещения в стек по традиции называ­ ется push, а выборки — pop. Указатель для работы со стеком (top) всегда ссылает­ ся на его вершину.

#1nclude struct Nocle{ Node * firstdnt d);

void pushCNode **top. int d);

int pop(Node **top);

int niain(){ // Занесение в стек void push(Node **top. int d){ // Выборка из стека int pop (Node *Пор){ Результат работы программы:

Очереди Очередь — это частный случай однонаправленного списка, добавление элементов в который выполняется в один конец, а выборка — из другого конца. Другие опе­ рации с очередью не определены. При выборке элемент исключается из очереди.

Говорят, что очередь реализует принцип обслуживания FIFO (first in — first out, первым пришел — первым ушел). Очередь проще всего представить себе, постояв в ней час-другой. В программировании очереди применяются, например при мо­ делировании, диспетчеризации задач операционной системой, буферизованном вводе/выводе.

Ниже приведена программа, которая формирует очередь из пяти целых чисел и выводит ее на экран. Функция помещения в конец очереди называется add, а вы­ борки — del. Указатель на начало очеред!! называется pbeg, указатель на конец — pend.

#include struct Node{ Node * firstdnt d):

void addCNode **pend. int d):

int del(Node **pbeg):

Node *pbeg = first(l):

Node *pend = pbeg:

/ / Добавление в конец void add(Node **pend. i n t d)( / / Выборка i n t del(Node **pbeg){ *pbeg = (*pbeg)->p:

Результат работы программы:

Бинарные деревья Бинарное дерево — это динамическая структура данных, состоящая из узлов, ка­ ждый из которых содержит, кроме данных, не более двух ссылок на различные бинарные деревья. На каждый узел имеется ровно одна ссылка. Начальный узел называется корнем дерева.

На рисунке 3.3 приведен пример бинарного дерева (корень обычно изображается сверху, впрочем, книгу можно и перевернуть) i. Узел, не имеющий поддеревьев, называется листом. Исходящие узлы называются предками, входящие — потом­ ками. Высота дерева определяется количеством уровней, на которых располага­ ются его узлы.

Если дерево организовано таким образом, что для каждого узла все ключи его ле­ вого поддерева меньше ключа этого узла, а все ключи его правого поддерева — больше, оно называется деревом поиска. Одинаковые ключи не допускаются.

В дереве поиска можно найти элемент по ключу^, двигаясь от корня и переходя ^ Нужно не забывать о том, что в оперативной памяти ячейки расположены линейно по возрастанию адресов, а дерево — это только метод логической организации данных.



Pages:     || 2 | 3 | 4 |


Похожие работы:

«Санкт-Петербургский государственный политехнический университет Факультет технической кибернетики Кафедра Измерительные информационные технологии РЕФЕРАТ Дисциплина: Высокопроизводительные вычисления Тема: Процессор Cell от фирмы IBM Выполнил студент гр. 6085/2 А.А. Ярошевич Руководитель К.С. Солнушкин Санкт-Петербург 2007 СОДЕРЖАНИЕ 1. История появления процессора Cell 2. Цели и задачи проекта 2.1. Высокая производительность 2.2. Отклик в режиме реального времени 2.3. Предполагаемые сферы...»

«к ^ MSji въ КОММЕРЧЕСКИХЪ ЗНАНІЙ В. В. Кузьмина и И. М. Громогласова въ у оскв. Волхонка, противъ храма Христа Спасителя, д. князя С. М. Голицына. МОСКВА Типо-Литографія М. Г. Грамакова, Газетный щ,} д. едотовой. І ОІ. Ъ ПРАВИЛА и ПРОГРАММЫ ТОРГОВОЙ школы В. В. Кузьмина и И. М. Громогласова ffi**Ht въ ^ / оскв. Д ->•>ітельность перпыхъ л тъ: пересмотръ Судебііика; земскія учреждеяія [оанна І\': Стоглавый (-ooop'b. Покореніс Казани, Астра.хаии іг Сибири. Начало торговля съ англичанамм. Вррьба...»

«О реализации программы поэтапного совершенствования системы оплаты труда в государственных (муниципальных) учреждениях на 2012-2018 годы в Чувашской Республике, на примере отрасли социального обслуживания населения Заместитель Председателя Кабинета Министров Чувашской Республики – министр здравоохранения и социального развития Чувашской Республики Самойлова А. В. Повышение заработной платы работников государственных (муниципальных) учреждений к 2018 году в рамках Указа Президента Российской...»

«ПРАВИЛА ПРОГРАММЫ ЛОЯЛЬНОСТИ МИКРОГОРОД В ЛЕСУ Принимая участие в Программе лояльности Микрогород В лесу, Участники полностью соглашаются в настоящими правилами (далее – Правила). 1. Термины и определения 1.1. В настоящих Правилах термины, написанные с заглавной буквы, имеют следующие значения: Программа лояльности Микрогород В лесу по предоставлению Участниками Программы Клиентам Программа Скидок и/или Привилегий при покупке/оплате ими товаров/услуг у Участника Программы. Общество с...»

«Главные новости Риека, 19 сентября 2009 года Примитивных разменов не будет Президент России дал большое интервью швейцарским СМИ в преддверии визита в Швейцарию. Дмитрий Медведев позитивно оценил шаги американской администрации по изменению программы ПРО, однако подчеркнул, что примитивных уступок и компромиссов со стороны России не будет. Говоря о внутренней ситуации в стране, он высказался за конкуренцию между несколькими сильными партиями, однако выступил против искусственного изменения...»

«Хлебопечки ZELMER 1 Книга рецептов 1 Хлебопечки Зелмер 60 лет технического совершенства Содержание Обращение к потребителям от компании Zelmer 4 Этапы приготовления хлеба в хлебопечке Zelmer 6 Классический хлеб Сладкий хлеб Белый хлеб 10 Йогуртовый хлеб 30 Чёрный хлеб 11 Сдоба творожная 31 Хлеб 7 злаков 12 Шоколадный хлеб с орехами 32 Луковый хлеб с укропом 13 Кекс 33 Бородинский хлеб 14 Шарлотка Хлеб на манке Вишневый пирог Сметанный хлеб Медовый хлеб Горчичный хлеб Хлеб с орехами, Хлеб на...»

«УДК 378 Формирование представлений о будущей специальности у студентов младших курсов в рамках дисциплины Введение в специальность В.С. Зарубин, Г.Н. Кувыркин, И.К. Марчевский c МГТУ им. Н.Э. Баумана, Москва, 105005, Россия Рассмотрены вопросы преподавания курса Введение в специальность, знакомящего студентов со сферой их будущей профессиональной деятельности. Предлагаемая тематика лекций направлена на расширение кругозора и повышение уровня общей эрудиции обучающихся, что способствует более...»

«ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ Государственное образовательное учреждение высшего профессионального образования Сыктывкарский государственный университет Факультет управления Кафедра экономической теории и корпоративного управления Учебно-методический комплекс Дисциплина Экономика Блок дисциплин ГСЭ.Ф Специальность 011000.65 – Химия; 011600.65 – Биология, 020801 Экология Факультет химико-биологический Форма обучения дневная Сыктывкар 2008 ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ...»

«ОСНОВНАЯ ОБЩЕОБРАЗОВАТЕЛЬНАЯ ПРОГРАММА НАЧАЛЬНОГО ОБЩЕГО ОБРАЗОВАНИЯ НА 2013 – 2014 УЧЕБНЫЙ ГОД (1-3 классы) г. Вятские Поляны, 2013 г. СОДЕРЖАНИЕ I.Целевой раздел 1. Пояснительная записка 2. Планируемые результаты освоения обучающимися основной образовательной программы начального общего образования 3. Система оценки достижения планируемых результатов освоения основной образовательной программы начального общего образования II.Содержательный раздел 4. Программа формирования универсальных...»

«В статье автор дает прогноз, что акмеологическое развитие созидательной деятельности субъектов образования возможно средствами образовательных программ, акме-технологий, акме-стратегий, тренингов самосовершенствования, индивидуальных программ осознанной саморегуляции и саморазвития, структурно-содержательной модели Акмеологическое развитие созидательной деятельности субъектов образования по данным измерений факторного и кластерного анализа. Оно заключается в создании условий, при которых в...»

«МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ Г ОУ ВПО Р О С С ИЙ С К О-А Р МЯ Н С К ИЙ (С Л А ВЯ НС КИ Й) УН ИВ Е РСИ Т Е Т Составлена в соответствии с федеральными государственными требованиями к структуре основной профессиональной образовательной программы послевузовского УТВЕРЖДАЮ: профессионального образования (аспирантура) Проректор по научной работе _ П.С. Аветисян 2011г. Факультет: Прикладная математики и информатика Кафедра: Системное программирование Учебная программа...»

«МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РФ Уральский государственный лесотехнический университет Кафедра менеджмента и внешнеэкономической деятельности предприятия Одобрена: Утверждаю: кафедрой менеджмента и ВЭД предприятия Декан ФЭУ В.П.Часовских протокол № 8 от 5 апреля 2012 г. Зав.кафедрой _ В.П. Часовских методической комиссией ФЭУ Протокол № 8 от 26 апреля 2012 г. Председатель НМС ФЭУ Д.Ю. Захаров Программа учебной дисциплины СД.07 Инновационный менеджмент Для специальности 080507 – менеджмент...»

«Gimnazjum Nr 50 im. Lotnictwa Polskiego w Poznaniu. Областное Государственное Бюджетное Общеобразовательное Учреждение Кадетская Школа – Интернат Смоленский Фельдмаршала Кутузова Кадетский Корпус Polsko – Rosyjska Konferencja Metodyczna Jzyk, historia i kultura Polski oraz Rosji w szkolnym programie wsppracy i wymiany modziey. Язык, история и кyльтура Польши и России в школьной программе сотрудничества и обмена молодежи. 2012 1. Wstp Dnia 2 lipca 2012 roku odbya si midzynarodowa konferencja...»

«МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ ФГБОУ ВПО Уральский государственный экономический университет УТВЕРЖДАЮ Проректор по учебной работе Л.М. Капустина _2011 г. СОВРЕМЕННЫЕ ПРОБЛЕМЫ НАУКИ В ПИЩЕВЫХ И ПЕРЕРАБАТЫВАЮЩИХ ОТРАСЛЯХ АГРОПРОМЫШЛЕННОГО КОМПЛЕКСА (АПК) Программа учебной дисциплины Наименование магистерской программы Организация производства и обслуживания на предприятиях общественного питания Наименование направления подготовки магистров 26 01 00 Технология продуктов...»

«Проект Center for Economic Research ЧАСТНО-ГОСУДАРСТВЕННОЕ ПАРТНЕРСТВО В УЗБЕКИСТАНЕ: ВОЗМОЖНОСТИ, ПРОБЛЕМЫ И ПУТИ ВНЕДРЕНИЯ Аналитический доклад Ташкент 2007 Доклад отражает мнения и взгляды рабочей группы, которые могут не совпадать с официальной точкой зрения Программы Развития ООН в Узбекистане, Торговопромышленной палаты Республики Узбекистан и Центра экономических исследований. Содержание доклада защищается авторскими правами. Воспроизводство, копирование и распространение доклада и/или...»

«КОНСУЛЬТАТИВНАЯ ГРУППА ПО ЗАПАДНО- WGWAP 4/4 ТИХООКЕАНСКИМ СЕРЫМ КИТАМ 4-е заседание 22-25 апреля 2008 года Лозанна, Швейцария ДОКЛАД КОНСУЛЬТАТИВНОЙ ГРУППЫ ПО ЗАПАДНОТИХООКЕАНСКИМ СЕРЫМ КИТАМ НА ЕЕ ЧЕТВЕРТОМ ЗАСЕДАНИИ ОРГАНИЗОВАНО МСОП WGWAP 4 Page 2 WGWAP 4 СОДЕРЖАНИЕ 1  ВВЕДЕНИЕ 1.1  СПРАВОЧНАЯ ИНФОРМАЦИЯ 1.2  УТВЕРЖДЕНИЕ ПОВЕСТКИ ДНЯ И ПРОЦЕДУР ОТЧЕТНОСТИ 2  ОБЗОР РЕКОМЕНДАЦИЙ, ВЫРАБОТАННЫХ НА ПРЕДЫДУЩЕМ ЗАСЕДАНИИ(ЯХ) 3  ОЦЕНКА ПОПУЛЯЦИИ 3.1  ПРОГРЕСС И ИНФОРМАЦИЯ О СОСТОЯНИИ ОЦЕНКИ...»

«Рынок маркетинговых услуг Сергей Моисеев, вице-президент РАМУ, президент Market Group Маркетинговые услуги Почтовые рассылки, телемаркетинг, SMS-рассылки, Прямой маркетинг CRM, маркетинг баз данных Consumer promotion (сэмплинги, дегустации, игровой Потребительский маркетинг, switch-selling, презентация), POSмаркетинг материалы, визуальные коммуникации, программы повышения лояльности) События, спонсорство, шоу-маркетинг, спортивный Событийный маркетинг маркетинг, выставки и т.д. Стимулирование...»

«Учебная программа составлена на основе учебной программы курса Молекулярные основы биосигнализации, 05.04.2012 г., регистрационный № УДч. Название учебной программы, дата утверждения, регистрационный номер Рассмотрена и рекомендована к утверждению на заседании кафедры молекулярной биологии название кафедры ПОЯСНИТЕЛЬНАЯ ЗАПИСКА Спецкурс Молекулярные основы биосигнализации рассчитан на студентов пятого курса биологического факультета специальности 1-31 01 01 “Биология” с целью сформировать у...»

«Министерство образования и науки РФ ФГБОУ ВПО Дагестанский государственный технический университет Кафедра БиМАиС УТВЕРЖДАЮ Ректор ФГБОУ ВПО ДГТУ, прдфе* Щ/ Т.А. Исмаилов 2014 г. ПРОГРАММА вступительного экзамена в аспирантуру направлению 12.06.01 - Фотоника, приборостроение, оптические и биотехнические системы и технологии Одобрена на заседании кафедры БиМАиС (протокол № 7 от 20 февраля 2014 г.) Заведующий кафедрой БиМАиС д.т.н., профессор^ ^ L OiyC C t t -Сс\ Магомедов Д.А. Махачкала - 1....»

«ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ КЕМЕРОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ Утверждаю Проректор по УОР _Т.Н.Семенкова 2012г. ПРОГРАММА ВСТУПИТЕЛЬНЫХ ИСПЫТАНИЙ ДЛЯ ПОСТУПАЮЩИХ ПО ПРОГРАММАМ МАГИСТРАТУРЫ НАПРАВЛЕНИЕ 030900.68 – ЮРИСПРУДЕНЦИЯ Кемерово, 2012 Целью вступительных испытаний по юриспруденции является определение теоретической и практической подготовленности поступающего к выполнению профессиональных задач, установленных...»






 
2014 www.av.disus.ru - «Бесплатная электронная библиотека - Авторефераты, Диссертации, Монографии, Программы»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.