Глава 1. НАПИСАНИЕ ВАШЕЙ ПЕРВОЙ ПРОГРАММЫ...25
Глава 2. ПРЕМУДРОСТИ ОБЪЯВЛЕНИЯ ПЕРЕМЕННЫХ...41
Глава 3. ВЫПОЛНЕНИЕ МАТЕМАТИЧЕСКИХ ОПЕРАЦИЙ...50
Глава 4. ВЫПОЛНЕНИЕ ЛОГИЧЕСКИХ ОПЕРАЦИЙ...55
Глава 5. ОПЕРАТОРЫ УПРАВЛЕНИЯ ПРОГРАММОЙ...66
В этой части...
И новейшие потрясающие воображение авиационные симуляторы, и незамысловатые, но мощные вычислительные программы состоят из одних и тех же базовых блоков. В этой части вы найдёте основные сведения, необходимые для написания самых замысловатых программ.
В этой главе...
Итак, мы на старте. Никого вокруг нет — только вы, я и книга. Сосредоточьтесь и постарайтесь овладеть некоторыми фундаментальными понятиями.
Компьютер — это поразительно быстрая, но невероятно глупая машина. Он может выполнить то и только то, что прикажешь ( причём с умом! ), — ни больше, ни меньше.
К нашему глубокому сожалению, компьютер не понимает привычного человеку языка — ни английского, ни русского, ни какого-либо другого. Знаю, вы хотите возразить: "Я видел компьютеры, понимающие английский". В действительности язык понимала выполняемая компьютером специально разработанная программа. ( Это объяснение не совсем корректно. Но, с другой стороны, если я захочу рассказать своему маленькому сыну что-то слишком для него сложное, то постараюсь объяснить это доступными для него словами и понятиями. )
Компьютеры понимают язык, который называют машинным или языком программирования. Человеку крайне сложно разговаривать машинным языком. Поэтому в качестве посредника между компьютерами и людьми решили использовать такие языки высокого уровня, как С++. Они более или менее понятны людям и конвертируются в машинный язык, воспринимаемый компьютерами.
В начале семидесятых консорциум очень умных людей разрабатывал компьютерную систему Multix. Её предназначением было обеспечение недорогого всеобщего доступа к графическим, текстовым и другим файлам, к электронной почте, эротике ( ладно, это я уже переборщил ). Конечно, это была совершенно глупая идея, и в целом проект провалился.
Небольшая группа инженеров, работающих в лабораториях Белла, решила использовать фрагменты Multix в небольшой операционной системе, которую окрестили Unix ( Un-ix, Multix — словом, всё понятно? ).
У этих инженеров не было одной большой мощной машины, а лишь несколько маломощных машин разных производителей. Поскольку все они были разные, каждую программу требовалось перерабатывать под каждую машину. Чтобы избежать этих мучений, был разработан небольшой, но мощный язык, который назвали С.
_______________
25 стр. Глава 1. Написание вашей первой программы
Язык С оказался действительно мощным и очень скоро завоевал передовые позиции среди средств разработки программного обеспечения. Однако со временем в программировании появились новые технологии ( например, достойное самого большого внимания объектно-ориентированное программирование ), которые постепенно вытесняли язык С. Не желая остаться за бортом, инженерное сообщество усовершенствовало С, дополнив его новыми возможностями и получив в результате новый язык программирования — С++.
Язык С++ включает:
►1.Семантику: словарь понятных для людей команд, которые конвертируются в машинный язык;
►2.Синтаксис: структуру языка ( или грамматику ), которая предоставляет пользователям возможность составлять из команд работающие программы.
«Семантика представляет собой строительные блоки, из которых создаётся программа на С++, а синтаксис — способ собрать эти блоки в единое целое.»
[Советы]
Программа — это текстовый файл, содержащий последовательность команд, связанных между собой по законам грамматики С++. Этот файл называют исходным текстом ( возможно, потому, что он является началом всех наших страданий ). Исходный файл в С++ имеет расширение .СРР, так же как файлы Microsoft Word оканчиваются на .DOC или командные файлы MS DOS имеют окончание .ВАТ. Расширение .СРР всего лишь соглашение.
Задача программирования — это написание такой последовательности команд, после преобразования которой в машинный язык можно получить программу, выполняющую наши желания. Такие машинно-исполнимые программы имеют расширение .ЕХЕ[ 1 ]. Процесс превращения программы С++ в исполнимую называется компиляцией или построением ( разница между этими понятиями поясняется в главе 22, "Разложение классов" ).
Пока всё выглядит достаточно легко, не так ли? Но это лишь цветочки. Продолжим...
Для написания программы вам нужны две вещи: редактор для создания исходного .СРР-файла и программа ( транслятор ), которая преобразует исходный текст в понятный машине код — .ЕХЕ-файл, выполняющий ваши команды. Инструмент ( программа-транслятор ), осуществляющий такое превращение, называется компилятором.
Современные инструменты разработки программ обычно совмещают в себе и компилятор и редактор — в единой среде разработки. После ввода текста программы для создания выполнимого файла нужно только щёлкнуть на кнопке.
Одна из популярнейших сред разработки — Visual С++ .NET фирмы Microsoft. В ней можно скомпилировать и выполнить все программы, представленные в этой книге; однако не все из вас могут приобрести этот программный продукт из-за его довольно высокой стоимости ( кстати, многие в это не поверят, но далеко не все программисты работают в Windows — есть и другие операционные системы ).
( На сайте:
http://www.visualstudio.com/ru-ru/downloads#d-express-windows-8
Вы можете скачать совершенно бесплатно Microsoft Visual Studio Express 2013 для Windows Desktop с небольшими ограничениями, но вполне пригодной для обучения и создания программ ( правда весит эта IDE 1 Gb, но готовые Release файлы - EXE файлы получаются в десятки и сотни раз меньше чем в Dev-C++ ) и выполняются они быстрее чем откомпилированные в Dev-C++. — Прим. рерайтера. )
______________
1Как правило, но, вообще говоря, это выполняется не всегда. — Прим.ред.
_________________
26 стр. Часть 1. Первое знакомство с С++
«Существуют и общедоступные среды разработки программ С++. Одна из них — Dev-C++, которая имеется на прилагаемом компакт-диске, а самую последнюю версию вы сможете найти по адресу www.bloodshed.net.»
[Диск]
Множество свободно распространяющихся программ можно найти в Internet, некоторые из этих программ не совсем бесплатны — для их получения вы всё-таки должны будете внести небольшую сумму. За использование Dev-C++ вам не придётся ничего платить. Более подробно об условиях работы с этой средой вы можете узнать на упомянутом Web-узле.
Все программы в этой книге тестировались с использованием Dev-C++ 4.9.8.0 , но они должны без проблем работать и с другими версиями среды разработки. Вы можете зайти на мой Web-узел www.stephendavis.com и познакомиться с последней информацией по этому вопросу.
«Dev-C++ — это не какая-то урезанная и переполненная ошибками версия компилятора, разработанного непонятно кем. Это нормальная, полноценная среда программирования, поддерживающая стандарт С++ ( и способная скомпилировать все программы из данной книги, что для нас в настоящий момент самое важное ).»
«Dev-C++ генерирует Windows-совместимые программы, но не является пакетом разработки программ для Windows в классическом понимании этого слова. Если я разгадал ваши тайные желания, то у вас нет другого выхода, кроме как приобрести коммерческий пакет наподобие Visual Studio.NET. Тем не менее, я настоятельно рекомендую сперва разобраться со всеми примерами из данной книги, до того как вы перейдёте к разработке программ для Windows.»
[Атас!]
Вам следует начать с установки Dev-C++, описанной в следующем разделе, и для практики скомпилировать свою первую программу, переводящую температуру в градусах Цельсия в температуру в градусах Фаренгейта.
«Все программы в данной книге совместимы с Visual С++ .NET ( и С++-частью Visual Studio.NET, что по сути одно и то же ). Для инсталляции Visual С++ .NET воспользуйтесь поставляемой с этим пакетом документацией. Конечно, Visual С++ .NET и Dev-C++ отличаются, но не настолько, чтобы вы не смогли работать с одной средой, хорошо зная другую.»
[Атас!]
На прилагаемом компакт-диске имеется инсталляционная программа Dev-C++. Это исполнимый файл, который находится в каталоге devcpp. Вот какие шаги следует выполнить для того, чтобы установить на своём компьютере Dev-C++.
1. Найдите на прилагаемом компакт-диске и запустите файл devcpp4980.ехе.
• Двойной щелчок на этом файле запустит инсталляцию автоматически. Заметим, что 4980 означает номер версии. Так, если вы загрузите последнюю версию Dev-С++ из Web, имя файла может отличаться от указанного здесь.
• Вы можете воспользоваться в Windows командой Start => Run, ввести в диалоговом окне Run x:\devcpp\devcpp4980 , где х — буква вашего дисковода компакт-дисков.
• Установка Dev-C++ начинается с предупреждения ( рис. 1.1 ) о необходимости удалить старую версию Dev-C++, если таковая у вас установлена. ( Нехорошо, конечно, начинать знакомство с угроз, но это лучше, чем иметь неприятности потом... )
_______________
27 стр. Глава 1. Написание вашей первой программы
Рис. 1.1. Перед установкой Dev-C++ вы должны удалить старую версию
2. Если у вас не установлена предыдущая версия Dev-C++, переходите прямо к п. 4 ; в противном случае прервите инсталляцию и переходите к следующему пункту.
«Не удивляйтесь, если вы даже не слышали о Dev-C++, и тем более никогда не ставили эту среду на свой компьютер — это диалоговое окно не более чем напоминание.»
[Советы]
3. Чтобы удалить старую версию, войдите в папку Dev-C++ и дважды щёлкните на файле uninstall.ехе.
При этом запустится программа деинсталляции Dev-C++, которая удалит её с вашего компьютера, подготовив его к установке новой версии.
4. Прочтите лицензионное соглашение и щёлкните на кнопке , если оно не противоречит вашим жизненным принципам.
Если вы не согласитесь с лицензионным соглашением, установка Dev-C++ выполняться не будет. В противном случае вы увидите диалоговое окно, показанное на рис. 1.2, в котором вам предлагается выбрать опции установки Dev-C++. Предлагаемые по умолчанию опции вполне безобидны, надо только сделать два замечания.
• Опция Mingw compiler system... должна быть включена.
• Опция Associate С and С++ files to Dev-C++ означает, что при двойном щелчке на файле с расширением .срр будет автоматически запущена среда Dev-C++, а не какое-то иное приложение ( например, Visual С++ .NET). Отменить впоследствии такую привязку можно, но сложно.
«Не включайте эту опцию, если у вас установлен Visual Studio .NET. Программы Dev-C++ и Visual Studio .NET вполне могут сосуществовать на одном компьютере, но связывать с Dev-C++ исходные файлы программ на С++ неразумно, тем более что вы всегда можете запустить именно Dev-C++, щёлкнув на файле .срр правой кнопкой мыши и выбрав из выпадающего меню команду Open with. Впрочем, лично я как раз предпочитаю использовать описанную опцию, хотя бы просто потому, что Dev-C++ загружается гораздо быстрее Visual Studio.»
[Атас!]
5. Щёлкните на кнопке Next.
Программа инсталляции спросит вас, куда именно устанавливать Dev-C++ ( рис. 1.3 ).
6. Согласитесь с предложенным каталогом С: \Dev-Cpp.
«Не устанавливайте Dev-C++ в каталог \Program Files вместе с другими программами. Это связано с тем, что Dev-C++ плохо работает с каталогами, в имени которых есть пробелы. Так будет безопаснее.»
[Атас!]
_________________
28 стр. Часть 1. Первое знакомство с С++
Рис. 1.2. Опции установки Dev-C++, предлагаемые по умолчанию
Рис. 1.3. Размещение Dev-C++ на диске по умолчанию
7. Убедитесь, что на диске достаточно места.
Хотя Dev-C++ занимает всего лишь около 45 Мбайт дискового пространства, лучше всё же убедиться, что эти 45 Мбайт на диске имеются.
8. Щёлкните на кнопке Install.
_______________
29 стр. Глава 1. Написание вашей первой программы
Сначала покажется, что ничего не происходит. Затем Dev-C++ будет копировать свои файлы в указанный вами каталог, абсолютно ничего не помещая в каталог Windows. Конечный результат показан на рис. 1.4.
Рис. 1.4. При инсталляции Dev-C++ на диск помещается много небольших файлов
По окончании инсталляции Dev-C++ выведет диалоговое окно, в котором спросит вас, следует ли установить Dev-C++ для всех пользователей вашего компьютера? Смысл в том, что если на вашем компьютере работает кто-то ещё, то разрешаете ли вы ему пользоваться установленной вами средой Dev-C++?
9. Щёлкните на кнопке Close для завершения инсталляции.
Перед вами откроется диалоговое окно первичных настроек среды, в котором самое интересное — выбор языка интерфейса среды ( выбор весьма богатый, включающий русский и украинский языки ).
Запустите файл devcpp.exe
Нажмите Tools => Environment Optinons => Interface
В
Если выскочит окошко
то нажмите
После этого — язык интерфейса русский .
В консольных приложениях ( похожих на текстовый режим ) русские буквы выводятся к кодировке CP-866, а в оболочке Dev-C++ они набираются в кодировке CP-1251. Это значит, что вместо русских букв вы увидите «кракозябры». Чтобы этого не случилось, кодировку нужно исправить с помощью простой дополнительной программы. Сделайте следующее:
1. Найдите на диске программы gccrus.exe и g++rus.exe ( 193 Кб ).
2. Скопируйте программы gccrus.exe и g++rus.exe в папку C:\Dev-Cpp\bin ( если при установке оболочки вы указали другой каталог вместо стандартного C:\Dev-Cpp, скопируйте программы в его подкаталог bin ).
3. Запустите оболочку Dev-C++ и войдите в меню Сервис-Параметры компилятора.
4. Перейдите на вкладку Программа и исправьте названия двух первых программ так, как показано на рисунке.
5. Теперь при выводе на экран можно смело использовать русские буквы, они будут «на лету» перекодироваться.
Если Вы не захотите использовать программки gccrus.exe и g++rus.exe , то в рабочих программах пишите setlocale ( LC_ALL , ".1251" ) ;, а если установите программки gccrus.exe и g++rus.exe , то в рабочих программах не пишите setlocale ( LC_ALL , ".1251" ) ;.
Если установите программки gccrus.exe и g++rus.exe и в рабочих программах напишите setlocale ( LC_ALL , ".1251" ) ;, то в консоли вместо кириллицы будут кракозябры. Выбирайте что-то одно.
Теперь необходимо настроить среду. Выполните следующие шаги.
• Выберите команду меню Tools => Compiler Options ( Сервис => Параметры компилятора ).
Изменить эти параметры можно в любой момент, но лучше сделать это сейчас, до начала работы.
10. Выберите в диалоговом окне вкладку Settings ( Настройки ).
11. Выберите в меню слева Code Generation ( Генерация кода ).
Убедитесь, что опция Enable Exception Handling ( Включить обработку исключений ) включена, как показано на рис. 1.5. ( Если она отключена, щёлкните на кнопке выпадающего списка справа и выберите Yes. )
_________________
30 стр. Часть 1. Первое знакомство с С++
Рис. 1.5. Обработка исключений должна быть включена
12. Выберите в меню слева пункт Linker ( Компоновщик ) и убедитесь, что опция Generate Debugging Information ( Генерировать отладочную информацию ) включена.
На рис. 1.6 показано, как должно выглядеть при этом диалоговое окно параметров компилятора.
Рис. 1.6. Генерация отладочной информации должна быть включена
13. Щёлкните на кнопке ОК.
На этом инсталляция завершена ( все внесённые вами изменения сохранятся автоматически ).
!Знаете ли вы, что...
...вы не должны отключать данные советы?
Советы, представленные здесь дают вам недокументированную информацию, которая не может быть найдена нигде более. Если вы отключите их, и захотите увидеть их в будущем, выберите "Справка / Совет дня"...
!Знаете ли вы, что...
...вы можете использовать vUpdate для постоянного поддержания своей среды Dev-C++ обновлённой последними исправлениями ошибок и многими приятными возможностями?
Выберите "Сервис / Проверить обновления / Пакеты..."
!Знаете ли вы, что...
...возможность автоматического завершения кода может помочь вам увеличить производительность путём предложения всех команд, доступных в данном контексте?
Чтобы включить автоматическое завершение кода, войдите в "Сервис / Параметры редактора" и включите обзор классов и завершение кода.
!Знаете ли вы, что...
...Функция автоматического завершения кода срабатывает, когда вы вводите ".", " -> " или "::" в окне редактора, по истечении определённого времени?
Чтобы задать требуемый промежуток времени, войдите в "Сервис / Параметры редактора / Обзор классов / Завершение кода".
!Знаете ли вы, что...
...вы можете в любое время вызвать Функцию автоматического завершения кода нажатием "Ctrl+Пробел" в окне редактора?
( Попробуйте также нажать "Ctrl+Shift+Пробел", когда курсор находится между скобками, где перечислены аргументы Функции... )
!Знаете ли вы, что...
...Shift + щелчок мышью на элементе обзора классов даст вам объявление вместо реализации?
Конечно, для этих ( и многих других! ) Функций вы можете использовать контекстное меню...
!Знаете ли вы, что...
...вы можете добавлять собственные папки в окно обзора классов, чтобы в больших проектах было проще находить то, что вы ищете? Вы даже можете вкладывать папки друг в друга!
Простой щелчок правой кнопкой мыши в окне обзора классов вызовет контекстное меню...
!Знаете ли вы, что...
...информация о папках пользователя из окна обзора классов хранится в Файле "classfolders.dcf" в корневом каталоге вашего проекта?
Если что-то пойдёт неправильно и возникнет путаница, просто удалите этот файл и откройте заново свой проект!
!Знаете ли вы, что...
...окно обзора имеет два режима просмотра?
Один режим показывает все классы / члены / функции проекта, а другой показывает вам соответствующую информацию только для текущего редактируемого Файла.
Просто щёлкните правой кнопкой на окне обзора классов и выберите "Режим просмотра"...
!Знаете ли вы, что...
...вы можете задать, хотите ли вы открывать Файлы в редакторе одинарным или двойным щелчком в менеджере проекта?
Чтобы задать это, войдите в "Сервис / Параметры среды" и включите ( или отключите ) пункт "Открывать Файлы двойным щелчком в менеджере проекта"...
!Знаете ли вы, что...
...вы можете определить собственную папку в менеджере проектов ( только верхнего уровня ) для лучшей организации своих файлов?
Чтобы сделать это, щёлкните правой кнопкой на файле в менеджере проекта для вызова контекстного меню...
!Знаете ли вы, что...
...если вы создали собственные папки в менеджере проекта, вы можете перетаскивать файлы, чтобы переупорядочить их?
!Знаете ли вы, что...
...вы можете получить сведения о файле, над которым вы работаете?
Выберите "Файл / Свойства" и вы получите сведения о размере файла, числе строк, строчных комментариях, пустых строках и включаемых файлах!
!Знаете ли вы, что...
...вы можете сохранить свой проект как шаблон?
Выберите "Файл / Создать / Шаблон", это сохранит ваш проект как шаблон Dev-C++, и он будет доступен, когда вы выберете "Файл / Создать / Проект".
Этот способ даёт вам возможность начать кодирование, основываясь на личном вкусе!
!Знаете ли вы, что...
...вы можете импортировать в Dev-C++ свои проекты MS Visual С++
Достаточно выбрать "Файл / Импорт / Импорт проекта MS Visual С++". Возможно, вам придётся немного поколдовать над параметрами проекта после его импорта, но всё-таки, оно работает!
!Знаете ли вы, что...
...вы можете импортировать исходный Файл или весь проект в HTML или RTF? Этот способ даёт возможность опубликовать свои исходники на веб-сайте и сделать доступными их всему миру!
Просто щёлкните на пункте "Файл / Экспорт" и выберите метод экспорта...
!Знаете ли вы, что...
...вы можете закомментировать / раскомментировать набор строк, выделив их, а затем щёлкнуть "Правка / Закомментировать( Раскомментировать )"?
!Знаете ли вы, что...
...вы можете увеличить / уменьшить отступ набора строк, выделив его, и щёлкнуть по пункту "Правка / Увеличить ( Уменьшить отступ )"?
!Знаете ли вы, что...
...вы можете использовать закладки в редакторе для ускоренного перемещения по коду?
Установите или снимите закладку щелчком на "Правка / Переключить закладку" и выборе номера закладки. Переход к закладке — щелчок на "Правка / Перейти к закладке" и выбор номера закладки.
!Знаете ли вы, что...
...в меню "Поиск" существует мощная команда "Перейти к функции"?
Она выдаёт список всех функций файла, и при вводе части имени функции список фильтруется. Нажмите Enter, и вы немедленно перейдёте к данной функции! Примечание: обзор классов должен быть включен...
Сейчас вы приступите к созданию своей первой программы на С++. Для этого потребуется ввести программный код в файл Conversion-рус.cpp, а потом скомпилировать его в выполнимую программу.
_______________
31 стр. Глава 1. Написание вашей первой программы
При создании любой программы на С++ первым шагом становится введение команд языка с помощью текстового редактора. В среде Dev-C++ имеется встроенный редактор, разработанный специально для создания программ на С++.
1. Выберите в Windows команду Starts => Programs => Bloodshed Dev-C++ для запуска среды.
Интерфейс Dev-C++ выглядит так же, как и у большинства других программ для Windows.
«При запуске Dev-C++ вам пришлось немало поработать мышкой, продираясь через все эти меню. Для упрощения этого процесса можно создать ярлык для Dev-С++ на рабочем столе. Для этого дважды щёлкните на пиктограмме My Computer, затем в открывшемся окне — на диске С:, а затем — папке Dev-Cpp. После этого щёлкните на файле devcpp.ехе правой кнопкой мыши и выберите из выпадающего меню Create Shortcut. Затем перетащите созданный ярлык на рабочий стол ( или в какое-то другое легкодоступное место ). Теперь вы можете запускать Dev-С++ двойным щелчком на ярлыке.»
[Советы]
_________________
32 стр. Часть 1. Первое знакомство с С++
2. Выберите в меню Dev-C++ File => New => Source File ( Файл => Создать => Исходный файл ).
Dev-C++ откроет пустое окно, в котором вы можете ввести ваш код. Не беспокойтесь, если вы пока что не понимаете, что вводите — назначение этой книги как раз и заключается в том, чтобы всё непонятное стало простым и ясным.
3. Введите представленную ниже программу в точности так, как она приведена далее в книге.
«Пусть количество отступов и пробелов вас не волнует: не так важно, сколько пробелов вы поставили перед началом строки или между соседними словами. Однако С++ весьма чувствителен к другому: надо следить, чтобы все команды набирались в нижнем регистре[ 2 ].»
[Советы]
«Вы можете просто воспользоваться файлом Conversion.срр из каталога \Cpp_Programs\Chap01 или файлом Conversion-рус.срр из каталога \Cpp_Programs\Chap01 на прилагаемом компакт-диске.»
[Советы]
//
/* Conversion-рус.срр. Программа для преобразования градусов Цельсия в градусы Фаренгейта: Fahrenheit = Celsius*( 212 - 32 )/100 + 32 */
//
#include
#include
#include
using namespace std ;
int main( int nNumberofArgs , char* pszArgs[ ] )
{
/* печать кириллицы, если Вы не установите программки gccrus.exe и g++rus.exe */
setlocale ( LC_ALL , ".1251" ) ;
/* Введите температуру в градусах Цельсия */
int celsius ;
cout << "Введите температуру по Цельсию: " ;
cin >> celsius ; /* строка номер 15 */
/* для приведённой формулы преобразования вычислим преобразующий множитель */
int factor ;
factor = 212 - 32 ;
/* используем вычисленный множитель для преобразования градусов Цельсия в градусы Фаренгейта */
int fahrenheit ;
fahrenheit = factor * celsius / 100 + 32 ;
/* вывод результатов */
cout << "Температура по Фаренгейту:" ;
cout << fahrenheit << endl ;
/* Пауза для того, чтобы посмотреть на результат работы программы, ожидание перед завершением программы пока пользователь не прочтёт результат и нажмёт клавишу */
system( "PAUSE" ) ;
return 0 ;
}
_____________
2В С++ отнюдь не запрещается использование символов в верхнем регистре — просто язык чувствителен к регистру, а это значит, что int main и Int Main, например, означают совершенно разные вещи. — Прим. ред.
_______________
33 стр. Глава 1. Написание вашей первой программы
4. После ввода этого кода выберите команду меню File => Save As ( Файл => Сохранить как... ) и сохраните файл.
Хотя это вам может показаться и не очень впечатляющим, но только что вы создали вашу первую программу на С++!
«При работе над книгой я создал каталог \Cpp_Programs, а в нём — каталог Chap01, и сохранил в нём созданный файл под именем Conversion.срр ( Conversion-рус.срр ). Обращаю ваше внимание на то, что Dev-C++ некорректно работает с дисковыми именами, в которых имеется пробел ( хорошо хоть, что Dev-C++ в состоянии работать с именами длиннее 8 символов — спасибо и за это... ).»
[Советы]
После сохранения на диске исходного файла Conversion.срр самое время сгенерировать выполняемый машинный код.
Для этого нужно выбрать команду меню Execute => Compile ( Выполнить => Скомпилировать ) или просто нажать клавиши
Рис. 1.7. Сообщение о компиляции программы без ошибок
Если компилятор находит ошибки в программе — а это такое же обычное дело, как снег на Чукотке — он сообщает об этом программисту. Вы обязательно столкнётесь с многочисленными предупреждениями и сообщениями об ошибках, возможно, даже при работе с простенькой программой Conversion.срр или Conversion-рус.срр . Чтобы продемонстрировать процесс исправления ошибок, изменим оператор в 15 строке cin >> celsius ; на cin >>> celsius ;.
Это нарушение кажется совсем невинным — и вы, и я вскоре бы о нём забыли. Но при компиляции открывается вкладка Compiler ( Компилятор ) с сообщением об ошибке ( рис. 1.8 ). Для того чтобы исправить ошибку, просто удалите лишний символ ">" и скомпилируйте программу заново.
_________________
34 стр. Часть 1. Первое знакомство с С++
Рис. 1.8. Сообщение об ошибке в программе
Как видим, компилятор смог определить строку, которую мы испортили в предыдущем примере. Однако если он нашёл ошибку, то почему же он сам не решит эту проблему — и дело с концом? Ответ достаточно прост. Хотя в данном случае Dev-C++ считает, что мы всего лишь допустили опечатку при вводе символов ">>", полностью положиться на его интуицию нельзя. Ведь правильной командой в действительности может оказаться совершенно другая, не имеющая никакого отношения к ошибочной команде. Если бы компилятор исправлял ошибки так, как считает нужным, то он скрывал бы от разработчиков многие реальные проблемы.
Требуется много усилий и времени, чтобы найти скрытую в программе ошибку. Намного лучше позволить найти эту ошибку компилятору. Мы тратим наше драгоценное время, создавая ошибки. Но зачем же расходовать его ещё и на их поиск, если компилятор может выловить огрехи, не тратя нашего времени. Каким, по вашему мнению, будет мой выбор?..
«Термин parse в описании ошибки обозначает, что ошибка была найдена при проведении синтаксического анализа команд С++.»
[Советы]
_______________
35 стр. Глава 1. Написание вашей первой программы
Пришло время испытания вашего нового творения. Для выполнения программы нужно запустить файл Conversion-рус.exe или Conversion.exe и обеспечить его входными данными. Полученный результат можно использовать для анализа.
Чтобы запустить программу из среды Dev-C++, нужно выбрать команду меню Ехесute => Run ( Выполнить => Выполнить ) или нажать
При этом откроется окно, в котором вам предложат ввести температуру по Цельсию. Для проверки правильности внесите какую-то заранее известную температуру, например 100°. После нажатия клавиши
Введите температуру по Цельсию: 100
Температура по Фаренгейту: 212
Для продолжения нажмите любую клавишу...( Press any key to continue... )
Сообщение Для продолжения нажмите любую клавишу... ( Press any key to continue... ) позволяет вам увидеть результаты работы программы перед тем, как окно будет закрыто. Нажмите
! ! ! ! ! ! ! ! ! ! !
Поздравляю! Вы только что ввели, скомпилировали и запустили свою первую программу на языке программирования С++.
! ! ! ! ! ! ! ! ! ! !
Заметьте, что пакет Dev-C++ не предназначен для разработки программ для Windows. Написать Windows-приложение с помощью Dev-C++ теоретически можно, но весьма непросто.
Windows-программы имеют ярко выраженный визуально-ориентированный оконный интерфейс. A Conversion.ехе является 32-битовой программой, которая выполняется в среде Windows, но Windows-программой в визуальном смысле её не назовёшь.
Если вы не знаете, чем 32-битовая программа отличается от 16-битовых, не беспокойтесь об этом. Как уже отмечалось, эта книга не о написании программ для Windows. Программы, разработанные нами в данной книге, имеют интерфейс командной строки и работают в окне MS DOS.
Начинающим Windows-программистам огорчаться не следует: ваше время не пропадёт зря. Изучение С++ совершенно необходимо как предварительное условие для написания Windows-программ.
Dev-C++ обеспечивает разработчиков системой помощи, доступной посредством команды меню Help( Справка ). В системе помощи представлена информация по различным аспектам работы в Dev-C++, но, пожалуй, наиболее существенной можно назвать справку по самому языку программирования С[ 3 ].
Хотя разбор программы, написанной другим разработчиком, — занятие не самое впечатляющее, но на этапе вашего становления как программиста делать это очень даже полезно. Программы чем-то похожи на автомобили. Все автомобили в принципе одинаковы, но между французскими и английскими автомобилями, при всей принципиальной схожести, всё же имеется масса отличий. Хотя построены они по одному шаблону — руль перед вами, сиденье под вами... Так и программы на С++ следуют общему шаблону, который проявляется даже в первой скомпилированной нами простейшей программе.
_______________
3К сожалению, недостаточно полная, и только на английском языке. — Прим. ред.
_________________
36 стр. Часть 1. Первое знакомство с С++
Каждая программа, написанная с использованием материала этой книги, в своей основе будет иметь одну и ту же базовую схему:
/* Template.срр. ШАБЛОН. Это многострочные комментарии, которые компьютер игнорирует. */
// Или такие
// комментарии,
// однострочные.
// Template.срр
// ШАБЛОН.
// Это
// комментарии,
// которые
// компьютер
// игнорирует.
#include
#include
#include
using namespace std ;
int main( int nNumberofArgs , char* pzArgs[ ] )
{
/* ...здесь записывается код программы... */
/* Следующая строка нужна только для демонстрационных целей, чтобы пользователь мог посмотреть на вывод программы перед тем, как закроется её окно */
system( "pause" ) ;
return 0 ;
}
Если не вникать в детали, то выполнение программы начинается с кода, который помещён между открывающей и закрывающей скобками, следующими за строкой с main( ).
«Этот код вы найдёте на прилагаемом компакт-диске в каталоге Cpp_Programs в файле Template.срр.»
[Диск]
Нетрудно заметить, что первые несколько строк Conversion.срр являются обычным текстом. Значит, или компилятор Dev-C++ оказался более понятливым, чем я его представил, или — что вероятнее всего — этот код предназначается для человеческих глаз. Оформленные таким образом строки называют комментариями. Чаще всего в комментариях программист объясняет конкретные действия, которые он собирается реализовать в следующем фрагменте кода. Компилятор комментарии игнорирует.
Комментарии в С++ начинаются с двойной косой черты ( // ) и заканчиваются переходом на новую строку. В их тексте можно использовать любые символы. Длина комментариев не ограничена, но, так как желательно, чтобы они не превосходили размеров экрана, обычно придерживаются нормы не более 80 символов.
Во времена печатных машинок перевод каретки означал начало новой строки. Но ввод с клавиатуры — это не печатание на машинке. В этом случае новая строка является символом, который завершает текущую командную строку.
«Допустима и другая форма комментариев, при которой игнорируется всё, что /* заключается в такие скобки */, однако эта форма комментариев в С++ почти не используется.»
[Атас!]
_______________
37 стр. Глава 1. Написание вашей первой программы
Присутствие в программах игнорируемых компьютером команд С++ ( или любого другого языка программирования ) может показаться странным. Однако все компьютерные языки предлагают те или иные способы оформления комментариев. Объяснения программиста раскрывают ход его мыслей при написании программного кода. Ведь замыслы программиста могут быть совсем неочевидными для людей, которые захотят воспользоваться программой или её модифицировать. Да и сам автор программы, взглянув на неё через месяц, не всегда сможет вспомнить её суть.
Все программы С++ в своей основе имеют то, что называют инструкциями. В этом разделе рассмотрим такие из них, которые составляют остов программы Convert.
Инструкция — это команда, которую понимает компилятор. Все инструкции, кроме комментариев, оканчиваются точкой с запятой ( для комментариев на то есть свои причины, но всё же иногда это неудобно; мне кажется, что во избежание путаницы после комментариев точку с запятой следовало бы ставить тоже ).
При запуске программы первой выполняется инструкция, находящаяся после открывающей фигурной скобки, а затем поочередно выполняются и все остальные инструкции.
Просмотрев программу, можно увидеть, что пробелы, символы табуляции и новой строки появляются на протяжении всей программы. Переход на новую строку осуществляется практически после каждой инструкции. Все эти символы называют непечатаемыми, так как на экране монитора их увидеть нельзя.
«Для повышения удобочитаемости допускается добавление символов пробела в любом месте программы ( но не внутри слов! ).»
[Советы]
Игнорируя пропуски, язык С++ учитывает регистр. Например, переменные fullspeed и FullSpeed, с его точки зрения, не имеют между собой ничего общего.
Строка int сelsius ; является инструкцией объявления. Объявление — это инструкция, которая определяет переменную. Переменная — это контейнер, в котором хранятся значения некоторого типа. Переменная может содержать числовые или символьные значения.
Термин "переменная" был заимствован из алгебры, где он является стереотипным для следующих выражений:
х = 10
у = 3 * х
Во втором выражении переменной у присваивается значение, определяемое формулой 3 * х. Но что такое х? Переменная х играет роль контейнера для хранения каких-то значений. В нашем случае значением х является 10 , но с таким же успехом можно определить значение х равным 20 , 30 или -1. Вторая формула имеет смысл при любом числовом значении х.
В алгебре можно начать работу непосредственно с выражения наподобие х = 10. Программируя на С++, переменную х перед её использованием необходимо объявить.
В С++ переменная имеет тип и имя. Переменная, определённая в строке 15, называется celsius. Согласно объявлению она целочисленная ( подобные названия типов, наверное, имеют целью развить у программистов ассоциативное мышление — тип int представляет собой сокращённое слово integer ).
_________________
38 стр. Часть 1. Первое знакомство с С++
Для С++ имя переменной не имеет никакого специфического значения. Имя должно начинаться с букв английского алфавита A-Z или a-z[ 4 ]. Остальные символы могут быть буквами, цифрами от 0 до 9 или подчёркивающей чертой ( _ ). Имена переменных могут быть настолько длинными, насколько это вам удобно.
«Существует негласная договорённость о том, что имена переменных должны начинаться со строчной буквы. Каждое слово внутри имени переменной пишется с прописной буквы, например myVariable.»
[Советы]
«Старайтесь давать переменным короткие, но наглядные имена. Избегайте таких имён , как х, потому что они не несут никакого смысла. Примером достаточно наглядного имени переменной может служить lengthOfLineSegment.»
[Советы]
Строки, начинающиеся с cout и сin, называют инструкциями ввода-вывода, или сокращённо I/O ( input/output ) ( как и все инженеры, программисты любят сокращения и аббревиатуры ).
Первая инструкция I/O выводит фразу "Введите температуру по Цельсию" в cout ( произносится как "си-аут" — сокращённо от console output ). В С++ cout — это имя стандартного устройства вывода. В нашем случае таким устройством является монитор.
В следующей строке всё происходит с точностью до наоборот. Со стандартного устройства ввода мы получаем значение и сохраняем его в целой переменной Celsius. Стандартным устройством ввода для С++ в данном случае служит клавиатура. Этот процесс является аналогом упоминаемой выше алгебраической формулы х = 10 в С++. Программа будет считать значением celsius любое целое число, введённое пользователем.
Почти все программы выполняют вычисления того или иного вида. В С++ выражением называется инструкция, которая выполняет какие-либо вычисления. Иными словами, выражение — это инструкция, которая имеет значение. Команда, генерирующая это значение, называется оператором.
Например, в программе Conversion можно назвать "вычисляющим выражением" совокупность строк с объявлением переменной factor и определением её значения как результата вычислений. Эта команда вычисляет разность между 212 и 32. В данном примере оператором является знак "минус" ( "-" ), а выражением — "212-32".
Разговорный язык может быть далеко не однозначным. Яркий тому пример — слово равный. Оно может употребляться в значении "одинаковый" ( например, равные силы ), а может применяться в математике для построения выражений типа "у равен утроенному х".
Чтобы избежать двусмысленности, программисты на С++ называют знак "=" оператором присвоения. Оператор присвоения сохраняет результат выражения, находящегося справа от "=", в переменной, записанной слева. Программисты говорят, что "переменной factor присвоено значение 212-32".
__________
4Имя может также начинаться с символа подчёркивания, хотя на практике это используется довольно редко. — Прим. ред.
_______________
39 стр. Глава 1. Написание вашей первой программы
«Никогда не говорите "factor равно 212 минус 32". Такое приходится слышать от всяких ленивых типов, но мы-то с вами знаем, как говорить правильно!»
[Атас!]
Второе выражение, представленное в Conversion.срр, несколько сложнее первого. В нём используются всем известные математические символы: " * " для умножения, " / " для деления, " + " для сложения. В этом случае, однако, вычисления выполняются не просто с константами, а с переменными.
Значение переменной factor ( кстати, уже вычисленное ) умножается на значение переменной celsius ( которое было введено с клавиатуры ). Результат делится на 100 и к нему прибавляется 32. Результат всего выражения приводится к целому типу и присваивается переменной fahrenheit.
Последние команды выводят строку "Температура по Фаренгейту:" и отображают значение переменной fahrenheit.
_________________
40 стр. Часть 1. Первое знакомство с С++
В этой главе...
►Объявление разных типов переменных 42
►Объявления типов переменных 45
Одним из основных в С++ является понятие переменной. Переменную можно представить как небольшую шкатулку, в которой хранятся вещи для дальнейшего многократного использования. Понятие переменной заимствовано из математики. Инструкция вида
х = 1
сохраняет значение 1 в переменной х. После такого присвоения математики могут использовать переменную х вместо константы 1, пока не изменят значение х на другое.
В С++ переменные используются таким же образом. После присвоения х = 1 ; и до следующего изменения содержимого переменная х становится обозначением числа 1 в программе. При этом говорят, что значение х есть 1.
К сожалению, в С++ возни с переменными несколько больше, чем в математике. Эта глава как раз и повествует о заботах, связанных с использованием переменных в С++.
Все числа, с которыми работает С++, хранятся в небольших "ларцах", которые называются переменными. В математике с переменными обращаются достаточно свободно. Допускаются формулировки наподобие
Уверен, вам не нужно объяснять, что такой способ задания переменных действительно однозначен. К сожалению, С++ не так сообразителен ( как я уже упоминал, компьютеры ну просто очень глупы! ).
Прежде чем использовать в программе новую переменную, вы должны её объявить:
int х ;
х = 10 ;
int у ;
у = 5 ;
_________________
41 стр. Глава 2. Премудрости объявления переменных
Таким образом, мы объявили переменные х, у и определили, что они могут содержать значения типа int ( типы переменных обсуждаются в следующем разделе ). Объявлять переменные можно в любом удобном для вас месте программы, но обязательно перед их использованием.
Вы, вероятно, думаете, что переменная в математике — это совершенно аморфное хранилище для любой информации, которая взбредёт в голову. Ведь в принципе можно свободно написать следующее:
х = 1 ;
х = 2.3
х = "Это - предложение."
х = Техас
Но С++ не настолько гибкий язык. ( С другой стороны, С++ очень легко может справиться с совершенно непосильными для нас задачами. Например, ему ничего не стоит сложить миллион чисел всего за одну секунду, хотя, строго говоря, этим занимается компьютер, а не сам язык. ) В С++ переменные могут хранить значения только одного типа. Причиной тому является большая разница в размерах памяти, необходимой для хранения значений переменных разных типов. Если некоторые данные программы могут состоять всего из одного числа, то довольно часто разработчикам приходится манипулировать целыми предложениями.
Вы должны сообщить С++, сколько памяти вам надо для хранения той или иной переменной, перед тем как приступить к её использованию. Добавлю, что особенности использования переменных разных типов различны. Пока вы встречались только с переменными типа int:
int х ;
х = 1 ;
В С++ тип int определяет множество целых чисел. Напомню, что целым называется число, не имеющее дробной части.
Целые числа используют для самых разных видов вычислений. Детально этому учат в младшей школе, приблизительно до шестого класса, и лишь потом начинается путаница с дробями. Та же тенденция характерна и для С++, в котором более 90% всех переменных имеют тип int[ 5 ].
К сожалению, иногда использование в программах переменных типа int приводит к ошибочным результатам. Когда в первой главе вы работали с программой, преобразующей температуру, существовала ( пусть неявно ) проблема: программа могла работать только с целыми значениями температуры. Отмечу, что в этой конкретной программе использование исключительно целых чисел вряд ли приведёт к отрицательным последствиям. Но при проведении серьёзных метеорологических исследований усечение дробной части температурных значений может поставить под вопрос истинность полученных результатов. Указанная проблема осложняется тем, что компилятор, не давая никаких предупреждающих сообщений, просто отбрасывает дробную часть числа. Согласитесь, было бы неприятно приземлиться на самолёте, не долетев полкилометра до взлётно-посадочной полосы просто из-за округления в программе навигации...
________
5Эта величина опять-таки существенно зависит от типа разрабатываемой программы. — Прим. ред.
_________________
42 стр. Часть 1. Первое знакомство с С++
Целочисленные переменные в С++ представляются типом int. На переменные этого типа накладываются те же ограничения, что и на их эквиваленты в математике.
Рассмотрим проблему вычисления среднего трёх чисел. Введём три целочисленные переменные — nValue1, nValue2, nValue3. Среднее значение вычисляется по формуле
( nValue1 + nValue2 + nValue3 ) / 3
Поскольку все три значения являются целыми, их сумма тоже будет целым числом. Например, сумма чисел 1, 2 и 2 равна 5. Но если 5 поделить на 3, получим 12/3 , или 1,666.... В отличие от людей ( обладающих разумом ), компьютеры ( которым он свойственен далеко не всегда ) приводят полученный результат к целому значению, просто отбрасывая его дробную часть. При этом 1,666 утратит свой "дьявольский" остаток и превратится в 1.
Для многих приложений усечение дробной части числа не представляет большой проблемы. Зачастую оно может быть даже полезным ( разумеется, сказанное не касается математических или экономических программ ). Однако такое округление целых значений может весьма пагубно сказаться на работе других программ. Рассмотрим следующую, эквивалентную приведённой выше формулу:
( nValue1 / 3 ) + ( nValue2 / 3 ) + ( nValue3 / 3 )
Подставляя в неё те же значения 1, 2 и 2, в результате получим 0. Это случилось потому, что каждое слагаемое оказалось числом, меньшим 1. Компьютер округлил их до 0 , а сумма трёх нулей, как известно, равна 0. Так что такого приведения к целочисленным значениям, вообще говоря, нужно избегать.
Второй проблемой переменной типа int является ограниченный диапазон возможных её значений. Максимальным значением обычной целочисленной переменной является число 2147483647, минимальным — -2147483648, т.е. общий диапазон — около 4 млрд. чисел[ 6 ].
«Два миллиарда — число весьма большое, чтобы быть достаточным для большинства применений. Тем не менее есть множество задач, где этого недостаточно. Например, ваш компьютер может иметь тактовую частоту, превышающую 2 ГГц ( приставка Г — Гига — как раз и обозначает миллиард ).»
[Советы]
С++ позволяет объявлять целые числа как беззнаковые, что означает, что они не могут быть отрицательны. Целое число типа unsigned int может принимать значения от 0 до 4294967295, что иногда облегчает ситуацию.
«Вы можете объявить переменную просто как unsigned, опустив объявление int, которое подразумевается неявно.»
[Советы]
__________
6Вообще говоря, диапазон значений типа int определяется множеством факторов — в первую очередь компилятором, на выбор типа int которого оказывает огромное влияние тип компьютера, поэтому считать определённым раз и навсегда, что диапазон значений int простирается от -232 до +232-1, нельзя. — Прим. ред.
_________________
43 стр. Глава 2. Премудрости объявления переменных
Рассмотренные особенности переменных типа int делают невозможным их использование в некоторых приложениях. Но, к счастью, С++ умеет работать и с десятичными числами, которые могут иметь ненулевую дробную часть ( математики называют их действительными числами ). Используя действительные числа, можно избежать большинства перечисленных проблем. Заметьте, что десятичные числа могут иметь ненулевую дробную часть, а могут и не иметь, оставаясь действительными. В С++ число 1.0 такое же действительное число, как и 1.5. Эквивалентным им целым числом является 1. Десятичные числа могут также быть отрицательны, например, -2.3.
В С++ действительные числа определены как числа с плавающей точкой, или просто double ( что означает "двойной" точности; в С++ есть действительные числа и одинарной точности, но это экзотика, о которой мы не будем говорить ). Используя выражение "с плавающей точкой", имеют в виду, что десятичную запятую ( или используемую вместо неё в программах точку ) в десятичных числах можно перемещать вперёд и назад настолько, насколько этого требуют вычисления. Действительные переменные объявляются так же, как и переменные типа int:
double dValue ;
Начиная с этой строки, во всей остальной части программы переменная dValue может принимать значения типа double. Тип уже объявленной переменной изменить нельзя: dValue является действительной переменной и останется ею до конца программы. Рассмотрим, как решается присущая целочисленным переменным проблема отбрасывания дробной части. Для этого в объявлении все переменные определим как действительные ( тип double ):
double dValue ;
dValue = 1.0/3.0 + 2.0/3.0 + 2.0/3.0 ;
Это эквивалентно выражению
dValue = 0.333... + 0.666... + 0.666... ;
которое даёт значение
dValue = 1.666... ;
«Я записал значение 1.666... как число с бесконечным числом шестёрок, однако на самом деле такая запись по сути невозможна в силу того, что имеется предел количества цифр, которое может быть в переменной типа double.»
[Атас!]
«На прилагаемом компакт-диске в папке Cpp_Programs\Chap02 программы IntAverage и FloatAverage демонстрируют разобранные здесь примеры вычисления среднего значения.»
[Диск]
//
/* IntAverage — среднее 3 чисел, используя целочисленную арифметику. */
/* Сперва сумма трёх отношений */
/* ( сумма каждого числа разделённого на 3), */
/* второе разделить сумму трёх чисел на 3. */
#include
#include
#include
using namespace std ;
int main( int nNumberofArgs , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
int nValue1 ;
int nValue2 ;
int nValue3 ;
// введите три числа
cout << "Эта программа вычисляет среднее трёх чисел типа int\n "
<< "integer ( целочисленной ) арифметики\n\n" ;
cout << "Введите три целых числа:\n" ;
cout << "n1 - " ;
cin >> nValue1 ;
cout << "n2 - " ;
cin >> nValue2 ;
cout << "n3 - " ;
cin >> nValue3 ;
/* Сперва сумма трёх отношений */
cout << "n1/3 + n2/3 + n3/3 = " ;
cout << nValue1/3 + nValue2/3 + nValue3/3 ;
cout << "\n" ;
/* Сейчас соотношение трёх сумм */
cout << "( n1 + n2 + n3 ) / 3 = " ;
cout << ( nValue1 + nValue2 + nValue3) / 3 ;
cout << "\n" ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ;
return 0 ;
}
/* FloatAverage — среднее 3 чисел, используя арифметику с плавающей точкой.*/
/* В противном случае, так же как IntAverage */
#include
#include
#include
using namespace std ;
int main( int nNumberofArgs , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
float fValue1 ;
float fValue2 ;
float fValue3 ;
// введите три числа
cout << "Эта програма вычисляет среднее трёх чисел типа float\n"
<< "floating point( с плавающей точкой ) арифметики\n\n" ;
cout << "Введите три числа:\n" ;
cout << "f1 - " ;
cin >> fValue1 ;
cout << "f2 - ";
cin >> fValue2 ;
cout << "f3 - " ;
cin >> fValue3 ;
/* Сперва сумма трёх отношений */
cout << "n1/3 + n2/3 + n3/3 = " ;
cout << fValue1/3 + fValue2/3 + fValue3/3 ;
cout << "\n" ;
/* Сейчас соотношение трёх сумм */
cout << "(n1 + n2 + n3)/3 = " ;
cout << (fValue1 + fValue2 + fValue3) / 3 ;
cout << "\n" ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ;
return 0 ;
}
Хотя применение чисел с плавающей точкой может решить многие вычислительные проблемы, на их использование тоже существуют ограничения. Проблемы отчасти противоположны тем, которые характерны для целочисленных переменных. Действительные переменные не могут использоваться для перечисления, с ними сложнее работать компьютеру, и они тоже страдают от ошибок округления ( хотя намного меньше, чем переменные типа int ).
Использовать переменные с плавающей точкой для простого перечисления нельзя. С++ не умеет определять, какое целочисленное значение подразумевается под действительным числом.
_________________
44 стр. Часть 1. Первое знакомство с С++
Например, ясно, что 1.0 есть 1. Но что такое 0.9 или 1.1 ? Следует ли их рассматривать как 1 ? Так что С++ избегает многих проблем, требуя использовать при перечислении только целые значения.
Исторически сложилось так, что процессор компьютера выполняет операции с целыми числами гораздо быстрее, чем с действительными. Для сложения 1000 целых чисел процессору может потребоваться столько же времени, сколько для выполнения только 200 вычислений с плавающей точкой.
Однако с увеличением производительности микропроцессоров проблема скорости вычислений становится всё менее важной. Большинство современных процессоров содержат специальные вычислительные схемы, которые позволяют вычислять выражения с плавающей точкой почти так же быстро, как и целочисленные выражения.
Действительные переменные не могут решить всех вычислительных проблем. Обычно их точность ограничена приблизительно шестью разрядами, но есть и расширенный вариант типа для действительных чисел, который может содержать после десятичной точки до 15 значимых разрядов.
Чтобы понять эту проблему, представим 1/3 в виде бесконечной последовательности 0.333.... Однако математическое понятие периода в программировании не имеет смысла, так как точность компьютерных вычислений ограничена и где-то наша дробь должна оборваться ( что зависит от использованного для хранения числа типа переменной ). Поэтому, усреднив числа 1, 2, 2, мы получим не точное, а приблизительное значение 1.666667.
В некоторых случаях ошибки округления может исправлять сам С++; например, выводя информацию на экран, вместо числа 0.999999 С++ выдаст пользователю значение 1.
Тип данных double также ограничен, хотя его диапазон намного обширнее диапазона целочисленных переменных. Максимальным значением типа int является число чуть больше 2 млрд.; максимальное значение переменной типа double приблизительно равно 10308, т.е. 1 с 308 нулями[ 7 ].
«Представляя переменные с плавающей точкой в стандартном виде, С++ учитывает после десятичной точки только первые 13 разрядов. Остальные 25 разрядов становятся жертвами ошибочных округлений.»
[Атас!]
Вы уже знаете, что все переменные в программе должны быть объявлены и что им должен быть назначен тип. Однако в С++ имеется масса различных типов. В табл. 2.1 представлен список некоторых стандартных типов переменных языка С++ с указанием их достоинств и недостатков.
________
7Это не означает, будто тип double может представить 1038 разных значений; вспомните, что говорилось выше о количестве разрядов в числах этого типа. — Прим. ред.
_________________
45 стр. Глава 2. Премудрости объявления переменных
Таблица 2.1. Переменные С++
_________________
Переменная — Пример — Характеристика
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
int — 1 — Простые положительные или отрицательные числа, используемые для перечисления .
unsigned — 1U — Неотрицательные числа, предназначенные в первую очередь для перечислений.
long — 10L — Потенциально расширенная версия типа int. В Dev-C++ и Microsoft Visual С++ .NET разницы между типами long и int нет.
unsigned long —10UL — Неотрицательная версия типа long.
float — 1.0F — Действительные числа единичной точности. Это уменьшенная версия double , требующая меньшего количества памяти, но при этом имеющая меньшую точность и диапазон возможных значений.
double — 1.0 — Стандартное представление чисел с плавающей точкой.
char — c — Символьный тип; значением переменных может быть символ алфавита, цифра, знак препинания или знак арифметической операции. Не годится для арифметических операций.
string — "this is a string" — Строка символов, составляющая предложение.
bool — true — Тип имеет только два логических значения — true и false ( истина и ложь ).
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Может показаться странным, что основным типом для действительных чисел является тип двойной точности, а не одинарной. Тому есть исторические причины — уходящие корнями в те времена, когда Большой Билл утверждал, что 640 Кбайт памяти хватит всем и всегда, а экономия памяти и повышение скорости вычислений за счёт точности представлялась разумной мерой. С удешевлением памяти и наращиванием мощности процессоров ( которые теперь работают с типом float с той же скоростью, что и с double ) тип double стал стандартом для чисел с плавающей точкой.
Следующий оператор объявляет переменные lVariable типа long и dVariable типа double и присваивает им начальные значения:
/* объявление переменной типа long и установка её равной 1 */
long lVariable ;
lVariable = 1
/* объявление переменной типа double и её инициализация */
double dVariable ;
dVariable = 1.0 ;
«Объявить и инициализировать переменную можно одним оператором:
int nVariable = 1 ;
/* объявление переменной и её инициализация */
»
[Советы]
Единственное преимущество инициализации переменной в объявлении — уменьшение размеров текстов программ.
Переменная типа char может содержать единственный символ, в то время как строковая переменная — строку символов. Поэтому а можно интерпретировать и как символ а, и как строку, содержащую только букву а . В главе 9, "Второе знакомство с указателями", вы найдёте детальное описание этого типа данных. В качестве грубой аналогии можно рассматривать символ 'а' как отдельный патрон, а строку "а" — как автомат, в магазине которого имеется только один патрон.
_________________
46 стр. Часть 1. Первое знакомство с С++
«Символ а и строка а — это далеко не одно и то же. Если вы захотите присвоить символьной переменной строковое значение ( или наоборот ), вы не сможете этого сделать даже в том случае, когда строка содержит единственный символ.»
[Атас!]
Константой называют произвольную постоянную величину ( например, 1, 0.5 или 'с' ). Подобно переменным, константы имеют свой тип. В выражении n = 1 ; константа 1 имеет тип int. Чтобы привести 1 к типу long, нужно написать n = 1L ; . Для лучшего понимания можно провести следующую аналогию: если под 1 подразумевать поездку на грузовике, то 1L можно интерпретировать как путешествие на лимузине. Их маршруты могут быть совершенно одинаковыми, но согласитесь, что путешествовать вторым способом гораздо удобнее.
Константу 1 можно также привести к действительному числу 1.0. Однако заметим, что по умолчанию типом действительной константы является double. Поэтому 1.0 будет числом типа double , а не float.
«Величина true представляет собой константу типа bool, но "true" ( обратите внимание на кавычки ) — это уже строка. Кроме того, с учётом чувствительности С++ к регистру, true — это константа типа bool, a TRUE может быть чем угодно ( тем, чем объявит программист ).»
[Атас!]
Для работы с любыми печатаемыми символами можно использовать переменные типа char или string. Однако значениями переменных, используемых в качестве символьных констант, могут быть и непечатаемые символы. В табл. 2.2 приведено описание некоторых важных непечатаемых символов.
С символом новой строки вы уже встречались раньше. Он позволяет разделить строку в любом месте на две части. Например, строка "Это первая строка\nЭто вторая строка" при выводе на экран будет выглядеть так:
Это первая строка
Это вторая строка
По аналогии символ табуляции \t перемещает выводимую информацию к следующей позиции табуляции. В зависимости от типа компьютера, на котором вы запустите программу, эта позиция может изменяться. Символ "обратная косая черта" используется для обозначения специальных символов, поэтому, чтобы вывести его на экран, необходимо записать два символа: \\.
_________________
47 стр. Глава 2. Премудрости объявления переменных
В MS DOS для разделения имён файлов в указаниях пути используется символ обратной косой черты. Так, root\folderА\file представляет собой путь к файлу file в папке folderA, которая является подкаталогом каталога root.
К сожалению, функциональное предназначение обратной косой черты в MS DOS и С++ не совпадает. Обратная косая черта в С++ используется для обозначения управляющих символов, а её саму можно вывести с помощью символов \\. Поэтому путь MS DOS root\folderA\file должен быть представлен в С++ строкой "root\\folderA\\file".
С++ предоставляет в распоряжение программиста логический тип bool. Название этого типа происходит от имени Буля, автора символьной логики. Булева переменная может иметь только одно из двух значений — true или false.
«В С++ имеются выражения, которые дают результат типа bool — например, выражение "х равно у" может иметь значение true или false.»
[Советы]
С++ позволяет использовать в одном выражении переменные разных типов. Например, можно складывать целые и действительные переменные. В следующем выражении переменная nValue1 является целой:
/* в следующем выражении перед выполнением операции сложения значение nValue1 преобразуется к типу double */
int nValue1 = 1 ;
double dValue = nValue1 + 1.0 ;
Выражение, в котором два операнда относятся к разным типам, называется выражением смешанного типа. Тип генерируемого в результате значения будет соответствовать более мощному типу операнда. В нашем случае перед началом вычислительного процесса nValue1 конвертируется в тип double. По тому же принципу выражение одного типа может быть присвоено переменной другого типа, например:
/*в следующем задании целая часть dVariable сохраняется в nVariable */
double dVariable = 1.0 ;
int nVariable ;
nVariable = dVariable ;
«Если переменная в левой стороне равенства относится к типу менее мощному, чем переменная справа, то при таком присвоении можно потерять точность или диапазон значений ( например, если значение переменной dVariable превышает диапазон допустимых значений переменной nVariable ).»
[Атас!]
Преобразование типа большего размера в меньший называется понижающим приведением ( demotion ), а обратное преобразование — повышающим приведением ( promotion ).
_________________
48 стр. Часть 1. Первое знакомство с С++
Вы могли заметить, что имя каждой переменной начинается с определённого символа, который, как может показаться, совсем ни к чему ( эти специальные символы приведены в таблице ниже ). С помощью соглашений по использованию этих символов можно мгновенно распознать, что dvariable — это переменная типа double. Данные символы помогают программисту распознавать типы переменных, не обращаясь к их объявлениям в другом месте программы. Так, нетрудно определить, что в представленном ниже выражении осуществляется присвоение смешанного типа ( переменная типа long присваивается целочисленной переменной ):
nVariable = lVariable ;
Для С++ использование этих специальных символов не имеет никакого значения. При желании для обозначения переменной типа int вы можете использовать любую другую букву. Однако "первобуквенное" соглашение упрощает понимание, и многие программисты постоянно используют подобные схемы в своей работе, хотя следующие объявления вполне корректны и допустимы в программе на С++:
double myVariable ;
int someIntValue ;
double nThisDoesntEvenMatch ;
Символ —Тип
n — int
l — long
f — float
d — double
с —character
sz — string
«Использование в С++ выражений смешанного типа — идея далеко не самая блестящая; их лучше избегать, не позволяя С++ делать преобразования за вас.»
[Советы]
_________________
49 стр. Глава 2. Премудрости объявления переменных
В этой главе...
►Определение порядка операций 52
►Выполнение унарных операций 53
►Использование операторов присвоения 54
Переменные придуманы математиками не только для того, чтобы было что описывать и в чём сохранять какие-то значения. Над переменными можно выполнять самые разные действия: складывать, перемножать, вычитать и т.д. Список возможных операций достаточно обширен.
Эти основные математические операции используются и в программах С++. Ведь приложения без вычислительных возможностей себя совершенно не оправдывают. Кому нужна страховая программа, которая не может подсчитать даже суммы взносов?
Операции С++ внешне идентичны обыкновенным арифметическим операциям, выполняемым на клочке бумаги; разве что применяемые в вычислениях переменные перед использованием нужно объявлять:
int var1 ;
int var2 = 1 ;
var1 = 2 * var2 ;
В этом примере объявлены две переменные, var1 и var2. Переменной var2 присвоено начальное значение 1, var1 определена как результат удвоения переменной var2.
В этой главе вы найдёте описание всего множества математических операторов С++.
Бинарными называются операторы, которые имеют два аргумента. В выражениях типа var1 op var2 оператор op бинарный. Самыми распространёнными бинарными операторами являются простые математические операции, изучаемые ещё за школьными партами. Бинарные операции, которые поддерживает С++, приведены в табл. 3.1.
Таблица 3.1. Математические операции в порядке приоритета
_________________
Приоритет — Оператор — Значение
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
1 — + ( унарный ) — Реально ничего не изменяет
1 — - ( унарный ) — Возвращает противоположное по знаку, равное по модулю значение
2 — ++ ( унарный ) — Оператор инкремента, увеличивает значение аргумента на 1
_________________
50 стр. Часть 1. Первое знакомство с С++
2 — -- ( унарный ) — Оператор декремента, уменьшает значение аргумента на 1
3 — * ( бинарный ) — Умножение
3 — / ( бинарный ) — Деление
3 — % ( бинарный ) — Остаток ( деление по модулю )
4 — + ( бинарный ) — Сложение
4 — - ( бинарный ) — Вычитание
5 — =, *=, %=, +=, -= ( специальные ) — Операторы присвоения
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Как видите, операторы умножения, деления, деления по модулю, сложения и вычитания имеют вид обычных математических операций. Да они и работают так же, как соответствующие им арифметические операции:
float var = 133 / 12 ;
Значение большинства операторов вам хорошо известно ещё из начальной школы, кроме разве что операции деления по модулю.
По своей сути этот оператор означает получение остатка от деления. Например, 4 входит в 15 три раза, и остаток при этом составляет 3. Выражаясь терминами С++, 15, делённое по модулю 4, равно 3.
int var = 15 % 4 ; /*переменной var присваивается значение 3 */
Программисты всегда пытаются удивить непрограммистов, а потому в С++ деление по модулю определяется так:
IntValue % IntDivisor
что эквивалентно
IntValue - ( IntValue / IntDivisor ) * IntDivisor
Вот пример:
15 % 4 равно 15 - ( 15/4 ) * 4
15 - 3 * 4
15 - 12
3
«Для действительных переменных оператор деления по модулю не определён, поскольку он целиком основан на использовании округления ( округления рассматривались в главе 2, "Премудрости объявления переменных" ).»
[Атас!]
Самый распространённый вид инструкций в С++ — выражение. Выражением в С++ называют любую последовательность операторов ( длиной не меньше одного ), которая возвращает значение. Все выражения типизированы. Тип выражения определяется типом возвращаемого значения. Например, значение выражения 1 + 2 равняется 3, следовательно, это целочисленное выражение ( тут нужно вспомнить, что константы без дробной части определяются как имеющие тип int ). Синтаксическая конструкция, включающая математический оператор, является выражением, так как в результате выполнения любой операции получается число.
_________________
51 стр. Глава 3. Выполнение математических операций
Выражения бывают как сложными, так и крайне простыми. С++ понимает под выражением любой завершённый оператор. Поэтому корректным оператором является, например, 1 ;. Он тоже представляет собой выражение, потому что его значение 1, а тип int. В операторе
z = х * у + w;
можно выделить пять выражений:
x * у + w
x * у
x
y
w
Необычный аспект С++ состоит в том, что выражение само по себе является завершённой инструкцией, т.е. упомянутое выражение 1 ; — завершённая инструкция С++.
Все операторы выполняют определённые функции. Чтобы установить порядок выполнения различных операторов, им назначены приоритеты. Рассмотрим выражение
int var = 2 * 3 + 1 ;
Если сложение выполнить перед умножением, то значением выражения будет 2 * 4 = 8. Если сперва выполнить умножение, то получим значение 6 + 1 = 7.
Приоритеты операций определяют порядок выполнения вычислений. Из табл. 3.1 видно, что приоритет операции умножения выше, чем сложения, т.е. результат всё же равен 7 ( приоритеты используются также в арифметике, и С++ следует именно им ).
А что происходит, когда в одном выражении используется два оператора с одинаковым приоритетом?
int var = 8 / 4 / 2 ;
Как в этом случае следует поступить: сначала 8 поделить на 4 или 4 на 2? Если в одном выражении присутствуют операции с одинаковыми приоритетами, они выполняются слева направо ( то же правило применяется и в арифметике ). Поэтому в предыдущем примере сперва делим 8 на 4, получая 2, а затем делим его на 2, получая ответ — 1.
В выражении
х / 100 + 32
х делится на 100 и к результату прибавляется 32. Но что, если программисту нужно поделить х на сумму 100 и 32? В таком случае ему придётся использовать скобки:
х / ( 100 + 32 )
При вычислении такого выражения х будет делиться на 132. Заметим, что начальное выражение х / 100 + 32 идентично следующему:
( х / 100 ) + 32
Почему это действительно так? Потому что С++ сначала выполняет операции с высшим приоритетом. А приоритет операций умножения и деления выше, чем сложения и вычитания. Поэтому скобки, указывающие на высокий приоритет данной операции, можно опустить.
На основе сказанного можно сделать вывод: в случае необходимости приоритет оператора можно повысить, используя скобки.
_________________
52 стр. Часть 1. Первое знакомство с С++
С арифметическими бинарными операторами вы неоднократно встречались уже с первого класса. О существовании же унарных операций вы могли и не подозревать, хотя наверняка одну из них использовали довольно часто ( имея дело с отрицательными числами ).
Унарными называются те операторы, которые имеют только один аргумент, например -а. Унарными математическими операторами являются +, -, ++ и --. Рассмотрим некоторые из них:
int var1 = 10 ;
int var2 = -var1 ;
Здесь в последнем выражении используется унарный оператор "-".
Оператор "минус" изменяет знак своего аргумента ( в примере это var1 ) на противоположный. Положительные числа становятся отрицательными и наоборот. Оператор "плюс" знака аргумента не изменяет и фактически вообще ни на что не влияет.
Операторы ++ и -- увеличивают или, соответственно, уменьшают на 1 значение аргумента и поэтому называются операторами инкремента и декремента ( от англ. ( увеличивать ) и ( уменьшать ). — Прим. перев. ). К действительным переменным применение этих операторов недопустимо. После выполнения приведённого ниже фрагмента значение переменной будет равно 11.
/* Инициализация переменной */
int var = 10 ;
/* Её увеличение; значение переменной равно 11 */
var++ ;
Операторы инкремента и декремента могут находиться либо перед аргументом ( префиксная форма ), либо после него ( постфиксная форма ). В зависимости от способа записи, выполнение операторов инкремента и декремента имеет свои особенности. Рассмотрим оператор инкремента ( принципы работы оператора декремента те же ).
Предположим, что переменная n имеет значение 5. Оба способа применения к n оператора инкремента ( ++n и n++ ) приведут к результату 6. Разница между ними состоит в том, что значение n в выражении ++n равно 6, в то время как в выражении с постфиксной формой записи оно равно 5. Это можно проиллюстрировать следующим примером:
/* объявляем три целые переменные */
int n1 , n2 , n3 ;
n1 = 5 ;
n2 = ++n1 ; /* обе переменные - n1 и n2 - получают значение 6 */
n1 = 5 ;
n3 = n1++ ; /* n1 принимает значение 6, а n3 - 5 */
Другими словами, переменной n2 присваивается уже увеличенное префиксным оператором инкремента значение n1, тогда как переменной n3 передаётся ещё не увеличенное постфиксным оператором значение n1.
Разработчики С++ заметили, что программисты прибавляют 1 чаще, чем любую другую константу. Учитывая это, в язык была добавлена соответствующая конструкция.
Кроме того, большинство процессоров способны выполнять команды инкремента быстрее, чем команды сложения. Учитывая мощность микропроцессоров, которые использовались во время создания С++, подобное нововведение было действительно важным.
_________________
53 стр. Глава 3. Выполнение математических операций
Операторы присвоения являются бинарными, изменяющими значения своих левых аргументов. Обыкновенный оператор присвоения "=" абсолютно необходим во всех языках программирования. Этот оператор сохраняет значение правого аргумента в левом. Однако причуды авторов языка привели к появлению и других операторов присвоения.
Создатели С++ заметили, что присвоение часто имеет вид
variable = variable # constant
Здесь # представляет собой какой-то бинарный оператор. Следовательно, чтобы увеличить целую переменную на два, программист может написать:
nVariable = nVariable + 2 ;
Из этой записи следует, что к значению переменной nVariable добавляется двойка и результат снова сохраняется в nVariable.
«Использование в левой и правой части выражения одной и той же переменной — весьма распространённое явление в программировании.»
[Советы]
Поскольку одна и та же переменная находится по обе стороны знака равенства, было решено просто добавить оператор, используемый при вычислении, к знаку присвоения. В таких специфических операторах присвоения допускается использование всех бинарных операторов. Поэтому указанное выше выражение можно сократить до
nVariable += 2 ;
Смысл этой записи таков: "значение переменной nVariable увеличено на 2".
«Модифицированные операторы присвоения используются не так часто, как обычные, но как правило повышают удобочитаемость программ.»
[Советы]
_________________
54 стр. Часть 1. Первое знакомство с С++
В этой главе...
►Зачем нужны логические операторы 55
►Использование простых логических операторов 55
►Выполнение побитовых логических операций 62
Наиболее распространённой синтаксической конструкцией С++ является выражение. Большинство используемых выражений содержит арифметические операторы сложения ( + ), вычитания ( - ) и умножения ( * ). В данной главе описаны все эти типы выражений.
Существует целый класс так называемых логических операторов. В отличие от арифметических, этот тип операторов многими не воспринимается как операторы.
Неправда, что люди не сталкиваются с логическими операторами. Значения операторов И и ИЛИ они вычисляют постоянно. Я не буду есть овсянки без молока И сахара. И закажу себе ром ИЛИ шотландский виски, ЕСЛИ кто-то заплатит за меня. Как видите, люди очень часто используют логические операторы, не осознавая этого.
Логические операторы бывают двух типов. Операторы И и ИЛИ называются простыми логическими операторами. Операторы второго типа, или побитовые операторы, уникальны, так как используются только в программировании. Этот тип операторов позволяет работать с любым битом в машинном представлении числа.
Программы на С++ должны уметь принимать решения. Программы, написанные без принятия решений, по сложности подобны приведённой в первой главе ( вспомните, что все выполняемые ею действия совершенно безальтернативны ). Программам очень часто приходится выполнять примерно следующее: "Сделай то, если переменная а меньше некоторого значения, а если не меньше — то сделай это". Для принятия решений ( правильных или нет ) в программах просто необходимо использовать логические операторы.
Простые логические операторы приведены в табл. 4.1. Они могут возвращать два значения: true ( истина ) и false ( ложь ).
_________________
55 стр. Глава 4. Выполнение логических операций
Таблица 4.1. Простые операторы из повседневной логики
_________________
Оператор — Значение
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
== — ( Равенство; истинно, когда значение левого аргумента совпадает со значением правого )
!= — ( Неравенство; противоположно равенству )
> , < — ( Больше, меньше; истинно, когда значение левого выражения больше ( или меньше ) значения правого )
>= , <= — ( Больше или равно, меньше или равно; истинно, если истиной является > или == ( соответственно < или == ) )
&& — ( И; истинно, если аргументы и слева и справа являются истиной )
|| — ( ИЛИ; истинно, если или левый, или правый аргумент являются истиной )
! — ( НЕ; истинно, если его аргумент принимает ложное значение )
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Первые шесть операторов табл. 4.1 являются операторами сравнения. Оператор равенства используется для проверки равенства двух значений. Например, следующее выражение истинно, если значением n является 0 , и ложно во всех других случаях:
n == 0 ;
«Не перепутайте оператор равенства == ( двойное равно ) с оператором присвоения = ( одинарное равно ). Эта ошибка очень распространена, к тому же компилятор С++ вообще не считает её ошибкой, что делает её вдвойне опасной!»
[Атас!]
n = 0 ; /* Программист хотел написать, что n == 0 */
Широко распространены в повседневной жизни операторы "больше" ( > ) и "меньше" ( < ). Приведём пример логического выражения, возвращающего значение true.
int n1 = 1 ;
int n2 = 2 ;
n1 < n2
«Операторы "больше" и "меньше" внешне очень похожи, и поэтому их легко перепутать. Чтобы этого не случилось, помните, что оператор-стрелочка принимает значение true в том случае, когда из двух сравниваемых значений он указывает на меньшее.»
[Советы]
С помощью операторов > и < можно найти случаи, когда n1 больше или меньше n2, однако при этом игнорируется возможность равенства их значений. Операторы "больше или равно" ( >= ), "меньше или равно" ( <= ), в отличие от только что рассмотренных, учитывают и эту возможность.
Так же широко используются операторы && ( И ) и || ( ИЛИ ). Эти операторы обычно сочетаются с другими логическими операторами:
/* истинно, если n2 больше n1 и меньше n3 */
( n1 < n2 )&&( n2 < n3 ) ;
В качестве ремарки: оператор "больше или равно" можно определить как[ 8 ]
n1 <= n2 эквивалентно ( n1 < n2 ) || ( n1 == n2 )
______________
8В качестве ещё одной ремарки: операторы сравнения вообще достаточно взаимозаменяемы. Так, например, ( a == b ) эквивалентно ( ! ( a>b )&&! ( a. — Прим. ред.
_________________
56 стр. Часть 1. Первое знакомство с С++
Результат логической операции может быть присвоен переменной типа bool:
int n1 = 1 ;
int n2 = 2 ;
bool b ;
b = ( n1 == n2 ) ;
Это выражение показывает разницу между операторами присвоения и сравнения. Его можно описать следующими словами: "Сравни содержимое переменных n1 и n2 и сохрани результат сравнения в переменной b".
«Оператор присвоения имеет наиболее низкий приоритет, поэтому оператор сравнения будет выполнен до присвоения. Скобки в этой ситуации излишни, и выражение b = n1 == n2 эквивалентно b = ( n1 == n2 ). Обратите также внимание на различие операторов присвоения и сравнения.»
[Советы]
Вот пример программы, демонстрирующей использование переменной типа bool:
/* BoolTest — сравнение данных, вводимых с клавиатуры, и сохранение результата в переменной типа bool */
#include
#include
#include
using namespace std ;
int main ( int nNumberofArgs , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать кириллицы */
cout << "Использование переменной типа bool\n" ;
/* Устанавливаем текстовый формат для вывода логических значений */
cout.setf( cout.boolalpha ) ;
/* Инициализируем аргументы */
int nArg1 ;
cout << "Введите значение 1: " ;
cin >> nArg1 ;
int nArg2 ;
cout << " Введите значение 2: " ;
cin >> nArg2 ;
bool b ;
b = nArg1 == nArg2 ;
cout << "Значение " << nArg1
<< " == " << nArg2
<< " равно " << b
<< endl ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ;
return 0 ;
}
Инструкция cout.setf( cout.boolalpha ) ; обеспечивает вывод логического значения в виде строки "true" или "false", а не в виде 1 или 0 , как принято по умолчанию.
_________________
57 стр. Глава 4. Выполнение логических операций
Программа запрашивает у пользователя два числовых значения и возвращает результат их сравнения. Вот пример работы программы:
Использование переменной типа bool
Введите значение 1: 5
Введите значение 2: 5
Значение 5 == 5 равно true
Press any key to continue...
«Специальное значение endl вставляет символ перехода на новую строку. Разница между endl и описанным в главе 2, "Премудрости объявления переменных", символом '\n' поясняется в главе 24, "Использование потоков ввода-вывода".»
[Советы]
С++ не всегда имел тип bool. Ранее для хранения логических значений в С++ использовался тип int. Значение 0 рассматривалось как false, а все остальные — как true. Все логические операторы генерировали целочисленные значения 0 и 1, соответствующие значениям false и true.
В С++ осталась высокая степень совместимости типов bool и int, обеспечивающая поддержку старых программ. Например, если в только что рассмотренной программе удалить инструкцию cout.setf( cout.boolalpha ) ;, вывод программы будет следующим:
Использование переменной типа bool
Введите значение 1: 5
Введите значение 2: 5
Значение 5 == 5 равно 1
Press any key to continue...
Переменные типов bool и int могут вместе использоваться в выражениях языка. Например, следующий код совершенно корректен:
int n1 = 1 ;
int n2 = 2 ;
int n ;
n = ( n1 == n2 ) ;
Тем не менее в своих программах желательно использовать для хранения логических значений переменные специально предназначенного для этого типа bool.
Переменные с плавающей точкой, как уже отмечалось, не могут использоваться для перечисления. Вы можете сказать: первый, второй, третий, четвёртый и т.д., так как соотношения между 1, 2, 3 абсолютно точно известны. Но нет никакого смысла говорить о номере 4.535887 в последовательности ( такой способ нумерации возможен лишь как обозначение чего-то между четвёртым и пятым, но не действительного значения номера, так как в любом сколь угодно малом отрезке их несчётное[ 9 ] множество ).
Тип float, представляющий в С++ действительные числа, не является перечислимым. Кроме того, в отличие от действительных чисел, числа с плавающей точкой имеют ограниченное количество разрядов, поэтому при использовании операторов сравнения с числами с плавающей точкой необходимо соблюдать осторожность. Рассмотрим следующий пример:
float f1 = 10.0 ;
float f2 = f1 / 3 ;
f1 == ( f2 * 3.0 ) ; /* Равны ли эти значения? */
__________________
9Более того, в данном случае это не красивое слово, а строгий математический термин. — Прим. ред.
_________________
58 стр. Часть 1. Первое знакомство с С++
Сравнивая начальное и полученное значения, мы не обязательно получим равенство. Действительные переменные, с которыми работает компьютер, не могут содержать бесконечное число значимых разрядов. Поэтому f2 равняется, например, 3.3333, а не 31/3. В отличие от математики, в компьютере число троек после точки ограничено. Умножив 3.3333 на 3, вы, вероятно, получите не 10.0, а 9.9999. Такой маленькой разницей может пренебречь человек, но не компьютер. Эта машина понимает под равенством исключительно точное равенство значений.
«Переменная типа float поддерживает точность около 6 значащих цифр, а double — 13. Я говорю "около", так как компьютер часто генерирует числа наподобие 3.3333347 из-за особенностей вычислений с плавающей точкой.»
В "чистой" математике количество троек после десятичной точки бесконечно, но компьютер не в состоянии работать с бесконечными числами. Поэтому при умножении 3.3333 на 3 мы получим 9.9999, а не 10, которое должно получаться при умножении 31/3 на 3 — так называемая ошибка округления. Такие малые отличия двух чисел несущественны для человека, но не для компьютера. Равенство означает в точности точное равенство ( неплохой каламбур? ).
Современные процессоры достаточно умны и зачастую могут корректно обрабатывать ошибки округления, но из программы С++ вы не в состоянии определить, окажется ли данный процессор настолько умным в данном конкретном случае.
Проблемы могут появиться и при совершенно простых вычислениях, например:
float f1 = 10.0 ;
float f2 = 100 % 30 ;
f1 == f2 ; /* истинно ли это выражение? */
Теоретически f1 и f2 должны быть равны ( об операции деления по модулю можно прочитать в главе 3, "Выполнение математических операций" ). Ошибка округления возникнуть вроде бы не должна. Однако и в этом нельзя быть уверенным: вам ведь неизвестно, как именно представляются числа с плавающей точкой внутри компьютера. Позвольте порекомендовать более безопасное сравнение:
float f1 = 10.0 ;
float f2 = f1 / 3 ;
float f3 = f2 * 3.0 ;
( f1 - f3 ) < 0.0001 && ( f3 - f1 ) < 0.0001 ;
Оно истинно в том случае, если разница между f1 и f2 меньше какого-то малого значения ( в нашем случае — 0.0001 ); при этом небольшие погрешности вычислений на правильность сравнения не повлияют.
Рассмотрим следующую конструкцию:
условие1 && условие2
Если условие1 ложно, то результат не будет истинным, независимо от истинности выражения условие2. В схеме
условие1 || условие2
в случае истинности выражения условие1 неважно, какое значение принимает условие2, — результат будет истинным.
Для экономии времени С++ вычисляет первым условие1, и в случае, если оно ложно ( для оператора && ) или истинно ( для оператора || ), выражение условие2 не вычисляется и не анализируется.
_________________
59 стр. Глава 4. Выполнение логических операций
Переменные хранятся в компьютере в виде так называемых двоичных, или бинарных, чисел, т.е. представлены в виде последовательности битов, каждый из которых может содержать два значения: 0 или 1. Скорее всего, вам не придётся оперировать с числами на битовом уровне, хотя существуют ситуации, когда обойтись без этого нельзя. С++ снабжён несколькими операторами для подобных целей.
«Вряд ли вам придётся часто работать с переменными на битовом уровне, поэтому остальную часть главы следует рассматривать как техническое отступление от основного повествования.»
Так называемые побитовые логические операторы работают с аргументами на битовом уровне. Для того чтобы понять принципы их работы, давайте рассмотрим, как компьютер хранит переменные.
Числа, которыми мы чаще всего пользуемся, называются десятичными, или числами по основанию 10. В большинстве случаев программисты на С++ тоже используют десятичные переменные. Например, мы говорим, что значение переменной var равно 123.
Число 123 можно представить в виде 1*100+2*10+3*1. При этом каждое из чисел 100, 10, 1 является степенью 10.
123 = 1 * 100 + 2 * 10 + 3 * 1,
что эквивалентно следующему:
123 = 1 * 102 + 2 * 101 + 3 * 100
Помните, что любое число в нулевой степени равняется 1.
Использование числа 10 в качестве основания нашей системы счисления объясняется, по всей вероятности, тем, что издревле для подсчётов человек использовал пальцы рук. Учитывая особенности нашей анатомии, удобной альтернативной системой счисления можно было бы выбрать двадцатеричную[ 10 ] ( т.е. с основанием 20 ).
Если бы наша вычислительная система была заимствована у собак, то она была бы восьмеричной ( ещё один "разряд", находящийся на задней части каждой лапы, не учитывается ). Эта система счисления работала бы не менее хорошо:
12310 = 1* 82 + 7* 81 + 3* 80 = 1738
Индексы 10 и 8 указывают систему счисления: 10 — десятичная, 8 — восьмеричная. Основанием системы счисления может быть любое положительное число.
У компьютеров, видимо, пальцев поменьше ( может быть, поэтому они такие недалёкие? ). Они предпочитают пользоваться двоичной системой счисления. Число 123 переводится в двоичную систему таким образом:
12310 = 0*27+ 1*26 + 1*25 + 1*24 + 1*23 + 0*22 + 1*21 + 1*20 = 0*128 + 1*64 + 1*32 + 1*16 + 1*8 + 0*4 + 1*2 + 1*1 = 011110112
______________________
10Что и было сделано у некоторых народов, например у майя или чукчей. — Прим. ред.
_________________
60 стр. Часть 1. Первое знакомство с С++
Существует соглашение, которое гласит, что в записи двоичных чисел используются 4, 8, 16, 32 или даже 64 двоичных цифр, даже если старшие цифры — нули. Внутреннее представление числа в компьютере строится именно таким образом.
Понятие разряда применяется к числам, кратным десяти, двоичный же разряд называется битом. Восемь битов составляют байт, а слово обычно представляется или двумя, или четырьмя байтами.
Поскольку основа двоичной системы счисления очень мала, для представления чисел необходимо использовать слишком большое количество битов. Для представления таких обычных чисел, как 12310, неудобно использовать выражения вида 011110112. Поэтому программисты предпочитают представлять числа блоками из четырёх битов.
С помощью одного четырёхбитового блока можно представить любое число от 0 до 15, и такая система счисления называется шестнадцатеричной ( hexadecimal ), т.е. системой по основанию 16. Часто употребляют её сокращённое название hex.
В шестнадцатеричной системе обозначения цифр от 0 до 9 остаются теми же, а числа от 10 до 15 представляются с помощью первых шести букв алфавита: А вместо 10, В вместо 11 и т.д. Следовательно, 12310 — это 7В16.
123 = 7 * 161 + В ( т.е. 11 ) * 160 = 7В16
Поскольку программисты предпочитают представлять числа с помощью 4, 8, 16 или 32 битов, шестнадцатеричные числа состоят соответственно из 1, 2, 4 или 8 шестнадцатеричных разрядов ( даже если ведущие разряды равны 0 ).
В заключение замечу, что, так как терминал не поддерживает нижний индекс, записывать шестнадцатеричные символы в виде 7В16 неудобно. Даже в том текстовом редакторе, который я использую сейчас, довольно неудобно всякий раз менять режимы шрифтов для ввода всего двух символов. Поэтому программисты договорились начинать шестнадцатеричные числа с 0х ( это странное обозначение было придумано ещё во время разработки языка С ). Таким образом, 7В16 равно 0x7В. Следуя этому соглашению, 0x7В равно 123, тогда как 0x123 равно 291.
К шестнадцатеричным числам можно применять все те же математические операторы, что и к десятичным. Просто нам трудно выполнить в уме умножение чисел 0хС * 0хЕ потому, что таблица умножения, которую мы учили в школе, применима только к десятичной системе счисления.
Интересно, что некоторые системы чисел значительно препятствовали развитию математики. К таким относится и так называемая римская система. Сложить два римских числа не очень сложно:
XIX + XXVI = XLV
Последовательность выполнения сложения такова:
а) IX+VI: I после V "уничтожает" I перед X, поэтому в результате получаем XV ;
б) X+XX=XXX, если добавить ещё один X, получим XXXX, или XL.
Сложность вычитания римских чисел приблизительно такая же. Однако чтобы умножить два римских числа, требуется диплом бакалавра математики ( у вас волосы станут дыбом от правила, которое объясняет, как добавить к X разряд справа так, чтобы X*IV равнялось XL ). А уж о делении римских чисел можно писать целые диссертации. Словом, хорошо, что мы пользуемся арабскими числами...
_________________
61 стр. Глава 4. Выполнение логических операций
Все числа С++ могут быть представлены в двоичном виде, т.е. с использованием только 0 и 1 в записи числа. В табл. 4.2 указаны операции, которые работают с числами побитово; отсюда и происходит название термина "побитовые операции".
Таблица 4.2. Побитовые операции
_________________
Оператор — Функция
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
~ — Каждый бит меняет своё значение на противоположное: 0 заменяется 1, 1 — нулём
& — Побитовое И: поочередно выполняет операцию И с парами битов левого и правого аргумента
| — Побитовое ИЛИ
^ — Побитовое исключающее ИЛИ
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
С помощью побитовых операций можно сохранять большое количество информации в маленьком участке памяти. В мире существует множество вещей, которые имеют только два состояния ( или, максимум, четыре ). Вы или женаты ( замужем ), или нет ( хотя можете быть в разводе или ещё не женаты ). Вы или мужчина, или женщина ( по крайней мере, так сказано в моих водительских правах ). В С++ каждую такую характеристику вы можете сохранить в одном бите. Таким образом, поскольку для хранения целого числа выделяется 4 байта, в тип int можно упаковать значения 32 разных свойств.
Кроме того, побитовые операции выполняются крайне быстро. Хранение 32 характеристик в одном типе не приводит ни к каким дополнительным затратам.
«Хотя память становится всё дешевле, она всё равно не безгранична. Иногда, при работе с большими объёмами данных, такая экономия с использованием битовых представлений весьма существенна.»
[Советы]
Побитовые операторы &, ^, | и ~ выполняют логические операции над отдельными битами числа. Если рассматривать 0 как false и 1 как true ( так принято, хотя можно ввести и другие обозначения ), то для оператора ~ справедливо следующее:
~1( true ) равно 0( false )
~0( false ) равно 1( true )
Оператор & определяется так:
1( true ) & 1 ( true ) равно 1( true )
1( true ) & 0 ( false ) равно 0( false )
0( false ) & 0 ( false ) равно 0( false )
0( false ) & 1 ( true ) равно 0( false )
Для оператора |:
1( true ) | 1 ( true ) равно 1( true )
1( true ) | 0 ( false ) равно 1( true )
0( false ) | 0 ( false ) равно 0( false )
0( false ) | 1 ( true ) равно 1( true )
_________________
62 стр. Часть 1. Первое знакомство с С++
Таблица 4.3. Таблица истинности оператора &
& 1 0
1 1 0
0 0 0
В таблице столбцы соответствуют значению одного аргумента, а строки — второго; результат операции находится на пересечении соответствующих строки и столбца. Так, из таблицы видно, что получить в результате операции & можно только если оба операнда равны 1. Далее в табл. 4.4 приведена таблица истинности оператора | ( ИЛИ ).
Таблица 4.4. Таблица истинности оператора |
| 1 0
1 1 1
0 1 0
Для последнего логического оператора, называемого "исключающее или" ( XOR ), прямой аналог в повседневной жизни найти труднее. Он возвращает значение true, если истинным является какой-то один ( но не оба! ) из его аргументов. Таблица истинности этого оператора представлена ниже ( табл. 4.5 ).
Таблица 4.5. Таблица истинности оператора ^
^ 1 0
1 0 1
0 1 0
Теперь, зная, как работают эти операторы с отдельными битами, рассмотрим их применение к двоичным числам.
Побитовые операторы работают отдельно с каждым битом.
Побитовые операторы выполняются подобно любым другим арифметическим операторам. Самым лёгким для понимания является оператор ~ . Выполнить операцию ~ над числом означает выполнить её над каждым битом числа.
~01102 ( 0х6 )
10012 ( 0x9 )
Таким образом получаем, что ~0x6 равно 0x9. В следующем примере продемонстрировано выполнение оператора &:
01102
&
00112
00102
Вычисляем, начиная со старших битов: 0 & 0 равно 0. В следующем бите 1 & 0 равно 0. В третьем бите 1 & 1 даёт 1, а в последнем бите 0 & 1 даёт 0.
_________________
63 стр. Глава 4. Выполнение логических операций
Те же вычисления могут быть выполнены в шестнадцатеричной системе. Для этого нужно преобразовать числа в двоичное представление, выполнить операцию и преобразовать результат обратно.
0x06 01102
& &
0x03 00112
0x02 00102
Расписав числа таким образом, мы получили, что 0x6 & 0x3 равно 0x2. ( Попробуйте подсчитать значение выражения 0х6 | 0x3. Если вам это удастся, вы почувствуете себя на седьмом небе. Иначе очутитесь на первой из семи ступенек в преисподнюю. У меня на это ушло чуть меньше восьми минут. )
Следующая программа иллюстрирует работу побитовых операторов. В ней инициируются две переменные, к которым применяются операции &, |, ~, ^. Результаты вычислений выводятся на экран.
/* BitTest — инициируются две переменные */
/* выводятся результаты выполнения */
/* операторов |, ^, ~ и & */
#include
#include
#include
using namespace std ;
int main( int nNumberofArgs , char* pszArgs[ ] )
{
/* отмена формата по умолчанию( десятичного ), или можно и так отменить cout.unsetf( cout.dec ) */
cout.unsetf( ios::dec ) ;
/* теперь можно установить вывод переменных в шестнадцатеричном виде */
/* установка вывода переменных в шестнадцатеричном виде, или можно и так cout.setf( cout.hex ) */
cout.setf( ios::hex ) ;
/* инициализация двух аргументов */
int nArg1 ;
nArg1 = 0x1234 ;
int nArg2 ;
nArg2 = 0x00ff ;
/* Выполнение логических операций */
/* Сначала применяем унарный оператор NOT */
cout << "Arg1 = 0x" << nArg1 << "\n" ;
cout << "Arg2 = 0x" << nArg2 << "\n" ;
cout << "~nArg1 = 0x" << ~nArg1 << "\n" ;
cout << "~nArg2 = 0x" << ~nArg2 << "\n" ;
/* Теперь — бинарные операторы */
cout << "nArg1 & nArg2 = 0x"
<< ( nArg1 & nArg2 )
<< "\n" ;
cout << "nArg1 | nArg2 = 0x"
<< ( nArg1 | nArg2 )
<< "\n" ;
cout << "nArg1 ^ nArg2 = 0x"
<< ( nArg1 ^ nArg2 )
<< "\n" ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ;
return 0 ;
}
_________________
64 стр. Часть 1. Первое знакомство с С++
Первая инструкция нашей программы ( та, которая следует за ключевым словом main ) — cout.setf( ios::hex ) ; — меняет используемый по умолчанию десятичный формат вывода на шестнадцатеричный ( поверьте мне, это сработает ).
В остальном программа очевидна. Присвоив значения аргументам nArg1, nArg2, она выводит все варианты побитовых вычислений над этими переменными. Результат выполнения программы будет выглядеть следующим образом:
Arg1 = 0x1234
Arg2 = 0xff
~nArg1 = 0xffffedcb
~nArg2 = 0xffffff00
nArg1 & nArg2 = 0x34
nArg1 | nArg2 = 0x12ff
nArg1 ^ nArg2 = 0x12cb
Press any key to continue...
На первый взгляд описанные операторы кажутся оторванными от жизни, но поверьте, в программировании они используются очень часто. Потерпите до следующей главы — и вы поймёте всю важность логических вычислений в программах.
_________________
65 стр. Глава 4. Выполнение логических операций
В этой главе...
►Управление ходом программы с помощью команд ветвления 66
►Вложенные команды управления 76
Простые программки, которые появлялись в первых четырёх главах, обрабатывали фиксированное количество вводов и выводов результатов вычислений, после чего завершали работу. Эти приложения были лишены какого бы то ни было контроля над работой программы, в частности не выполняли никаких проверок. Но компьютерные программы могут принимать решения. Вспомните: когда пользователь нажимает клавиши, компьютер реагирует на это выполнением соответствующих команд.
Например, если пользователь нажимает
Команды, управляющие ходом программы, указывают на то, какие действия она должна выполнить в зависимости от результата вычисления какого-либо логического выражения ( о которых шла речь в главе 4, "Выполнение логических операций" ). Существует три типа управляющих инструкций: операторы ветвления ( или условного перехода ), цикла и выбора.
Проще всего управлять ходом программы с помощью инструкции ветвления, которая позволяет программе, в зависимости от результата логического выражения, решить, по какому из двух возможных путей выполнения инструкций следует двигаться дальше. В С++ оператор условного перехода реализуется с помощью инструкции if:
if ( m > n )
{
/* 1-я последовательность операторов. Инструкции, которые должны быть выполнены, если m больше n */
}
else
{
/* 2-я последовательность операторов. Инструкции, которые нужно выполнить в противном случае */
}
_________________
66 стр. Часть 1. Первое знакомство с С++
Прежде всего вычисляется логическое выражение m > n. Если его значение — true, программа выполняет первую последовательность операторов. Если же выражение ложно, управление передаётся второй последовательности. Оператор else не обязателен: если он опущен, С++ считает, что он существует, но является пустым.
«Если в текущей ветви оператора if имеется только одна инструкция, скобки использовать необязательно. Однако очень легко сделать ошибку, которую без скобок, определяющих структуру операторов, компилятор С++ обнаружить не сможет.»
[Атас!]
Поэтому намного безопаснее включать скобки всегда. Если друзья будут уговаривать вас не использовать скобки, не поддавайтесь!
Работу оператора if можно рассмотреть на следующем примере:
/* BranchDemo — введите два числа. */
/* Если первый аргумент больше, выполняем операторы первой ветви, если меньше — второй. */
#include
#include
#include
using namespace std ;
int main( int argc , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
/* вводим первый аргумент... */
int arg1 ;
cout << "Введите arg1: " ;
cin >> arg1 ;
/* ... второй */
int arg2 ;
cout << "Введите arg2: " ;
cin >> arg2 ;
/* теперь решаем, что делать: */
if ( arg1 > arg2 )
{
cout << "Аргумент 1 больше, чем аргумент 2"
<< endl ;
}
else
{
cout << "Аргумент 1 не больше, чем аргумент 2"
<< endl ;
}
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
__________________
67 стр. Глава 5. Операторы управления программой
Программа считывает два целых числа, вводимых с клавиатуры, и сравнивает их. Если выражение "arg1 больше arg2" истинно, то выполняется инструкция cout << "Аргумент 1 больше, чем аргумент 2 " ;. Если же нет, то управление переходит к последовательности операторов, соответствующей условию else: cout << "Аргумент 1 не больше , чем аргумент 2 " ;. Вот пример работы программы:
Введите arg1: 5
Введите arg2: 6
Аргумент 1 не больше, чем аргумент 2
Press any key to continue...
Оператор перехода позволяет управлять работой программы, когда существуют альтернативные пути её выполнения. Это усовершенствование языка хотя и весьма значительное, но всё же не достаточное для написания полнофункциональных программ.
Рассмотрим проблему обновления экрана компьютера. При перерисовывании содержимого типичного дисплея компьютеру необходимо выводить на экран тысячи пикселей. Если программа не умеет повторно выполнять один и тот же фрагмент кода, вы будете вынуждены тысячи раз записывать одно и то же множество инструкций.
Для решения этой проблемы необходим способ, который позволит многократно выполнять одни и те же последовательности инструкций. Операторы цикла предоставляют возможность решить эту задачу.
Самый простой цикл можно организовать с помощью оператора while. Он выглядит таким образом:
while ( условие )
{
/* Этот код выполняется повторно, пока условие остаётся истинно */
}
Сначала проверяется условие. Условием могут быть выражения вида var > 10 , var1 == var2 или любые другие. Если условие истинно, выполняются инструкции в скобках. Дойдя до закрывающей скобки, компилятор передаёт управление в начало цикла, и всё повторяется вновь. Таким образом, смысл оператора while в том, что программный код в скобках повторно выполняется до тех пор, пока не нарушится условие ( этот процесс напоминает мне утренние прогулки с собакой вокруг дома, пока она не... ну а потом мы возвращаемся ).
Если условие сначала было справедливо, тогда что может заставить его стать ложным? Рассмотрим следующий пример программы:
/* WhileDemo — введите счётчик цикла. Программа выводит количество выполненных циклов while */
#include
#include
#include
using namespace std ;
int main( int argc , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
_________________
68 стр. Часть 1. Первое знакомство с С++
/* Ввод счётчика цикла */
int loopCount ;
cout << "Введите loopCount: " ;
cin >> loopCount ;
/* Теперь в цикле выводим значения */
while ( loopCount > 0 )
{
loopCount = loopCount - 1 ;
cout << "Осталось выполнить "
<< loopCount << " циклов( a )\n" ;
}
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
Программа WhileDemo получает от пользователя значение счётчика цикла, которое сохраняется в переменной loopCount. Затем программа выполняет цикл while. Сначала проверяется значение переменной loopCount. Если оно больше нуля, программа входит в тело цикла ( телом цикла называется код между скобками ), где loopCount уменьшается на 1, и результат выводится на экран. Затем программа возвращается к началу цикла и проверяет, осталась ли переменная loopCount положительной.
Ниже представлены результаты выполнения программы, выведенные на экран. Нетрудно догадаться, что введённый мною счётчик цикла равен 5. Программа пять раз выполнила цикл, каждый раз выводя результат на экран:
Введите loopCount: 5
Осталось выполнить 4 циклов( а )
Осталось выполнить 3 циклов( а )
Осталось выполнить 2 циклов( а )
Осталось выполнить 1 циклов( а )
Осталось выполнить 0 циклов( а )
Press any key to continue...
Если пользователь введёт отрицательное значение для счётчика цикла, условие окажется ложным и тело цикла не будет выполнено ни разу. Если пользователь введёт очень большое число, на выполнение программы уйдёт очень много времени.
Реже используется другая версия цикла while, известная как do ... while. Она работает аналогично, но условие завершения проверяется в конце, после выполнения тела цикла.
do
{
// Тело цикла
}
while ( условие ) ;
Поскольку условие проверяется только в конце, тело оператора do ... while выполняется всегда хотя бы один раз.
«Условие завершения цикла проверяется только в начале оператора while или в конце оператора do . . . while. Даже если в какой-то момент оно перестанет быть справедливым, программа продолжит выполнение цикла до следующей проверки условия.»
[Атас!]
_________________
69 стр. Глава 5. Операторы управления программой
Очень часто для какого-либо подсчёта в циклах программисты используют операторы инкремента или декремента. Заметим, что в следующем фрагменте программы WhileDemo для уменьшения значения счётчика используются операторы присвоения и вычитания:
while ( loopCount > 0 )
{
loopCount = loopCount - 1 ;
cout << "Осталось выполнить "
<< loopCount << " циклов\n" ;
}
Используя оператор декремента, этот цикл можно записать более компактно:
/* В цикле выводим значения */
while ( loopCount > 0 )
{
loopCount-- ;
cout << "Осталось выполнить "
<< loopCount << " циклов\n" ;
}
Смысл этого варианта цикла полностью совпадает со смыслом оригинала. Единственная разница — в способе записи.
Поскольку оператор декремента одновременно уменьшает аргумент и возвращает его значение, он может включаться в условие цикла while. В частности, допустима следующая версия цикла:
/* В цикле выводим значения */
while ( loopCount-- > 0 )
{
cout << "Осталось выполнить "
<< loopCount << " циклов\n" ;
}
Хотите — верьте, хотите — нет, но большинство программистов на С++ используют именно этот вариант записи. И не потому, что им нравится быть остроумными; хотя почему бы и нет? Использование в логических сравнениях операторов инкремента и декремента делает программный код легко читаемым и более компактным. И вряд ли вы, исходя из своего опыта, сможете предложить достойную альтернативу.
«И в выражении loopCount--, и в --loopCount значение loopCount уменьшается; однако первое возвращает значение переменной loopCount перед его уменьшением на 1, а второе — после.»
[Помни!]
Сколько раз будет выполняться декрементированный вариант WhileDemo, если пользователь введёт число 1? Если использовать префиксный вариант, то значение --loopCount равно 0 и тело цикла никогда не выполнится. В постфиксном варианте loopCount-- возвратит 1 и программа передаст управление в начало цикла.
У вас может сложиться ошибочное мнение, что программа, в которой используются операторы декремента, выполняется быстрее, так как содержит меньше инструкций. Однако это не совсем так. Время выполнения программы не зависит от того, с какой из представленных выше операций декремента вы работаете, так как современные оптимизирующие компиляторы используют минимально необходимое количество инструкций машинного языка, независимо от применяемых для декремента операторов.
_________________
70 стр. Часть 1. Первое знакомство с С++
Другой разновидностью циклов является цикл for. Его часто предпочитают более простому циклу while. Цикл for имеет следующий вид:
for ( инициализация ; условие ; увеличение )
{
// ...тело цикла
}
Выполнение цикла for начинается с инициализации. В ней обычно находится оператор присвоения, используемый для установки начального значения переменной цикла. Условие инициализации выполняется только один раз, при первом входе в цикл for.
Затем проверяется условие. Подобно циклу while, цикл for выполняется до тех пор, пока условие не станет ложным.
После того как выполнится код тела цикла, управление получит следующий параметр цикла for ( увеличение[ 11 ]) и значение счётчика изменится. Затем опять будет выполнена проверка условия, и процесс повторится. В этом параметре обычно записывают инкрементное или декрементное выражение, которое определяет характер изменения переменной цикла на каждой итерации, но в принципе ограничений на используемые здесь операторы нет. Цикл for можно заменить эквивалентным ему циклом while:
инициализация ;
while ( условие )
{
{
// ...тело цикла
}
увеличение ;
}
Все три параметра цикла for являются необязательными. С++ игнорирует отсутствие части инициализации или увеличения цикла, а если опущено условие, С++ будет выполнять цикл for вечно ( или пока какой-либо другой оператор не передаст управление за пределы цикла ).
Для лучшего понимания цикла for рассмотрим пример. Приведённая ниже программа ForDemo выполняет то же, что и WhileDemo, но вместо while использует цикл for.
/* ForDemo1. Вводится счётчик цикла. На экран выводится количество выполненных циклов for */
#include
#include
#include
using namespace std ;
int main( int nNumberofArgs , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
/* Ввод счётчика цикла */
int loopCount ;
cout << "Введите loopCount: " ;
cin >> loopCount ;
/* Работаем loopCount раз */
for ( ; loopCount > 0 ; )
{
loopCount = loopCount - 1
cout << "Осталось выполнить "
<< loopCount << " циклов\n" ;
}
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
_______________
11Увеличение в данном случае — достаточно условное название. Чаще всего здесь действительно выполняется увеличение счётчика цикла, однако это может быть любая инструкция С++, в том числе и пустая. — Прим. ред.
_________________
71 стр. Глава 5. Операторы управления программой
Программа ForDemo1 считывает вводимое при помощи клавиатуры значение в переменную loopCount. Управление передаётся циклу, если введённое значение больше нуля. Внутри цикла программа уменьшает значение счётчика и выводит получившееся значение, после чего управление вновь передаётся оператору for. Как только значение loopCount становится равным 0 , работа цикла завершается.
«Все три инструкции цикла for могут быть пустыми. Пустые инициализация и увеличение ничего не делают и никак не влияют на работу цикла, а пустое условие рассматривается как возвращающее значение true.»
[Советы]
Приведённый цикл for имеет две небольшие проблемы. Во-первых, он деструктивен — не в том смысле, что может сжечь ваш монитор или откусить шнур мыши — а в том плане, что он изменяет значение loopCount, которое, таким образом, "уничтожается". Во-вторых, цикл получился "нисходящий", т.е. значения переменной цикла идут от больших к меньшим. Всего этого легко избежать, если добавить специальную переменную — счётчик цикла.
/* ForDemo2 — Вводится счётчик цикла. На экран выводится количество выполненных циклов for */
#include
#include
#include
using namespace std ;
int main( int nNumberofArgs , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
/* Ввод количества циклов */
int loopCount ;
cout << "Введите loopCount: " ;
cin >> loopCount ;
/* Цикл до достижения значения loopCount */
for ( int i = 1 ; i <= loopCount ; i++ )
{
cout << "Выполнено " << i << " циклов( a )\n" ;
}
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
_________________
72 стр. Часть 1. Первое знакомство с С++
Программа ForDemo2 выполняет те же действия, что и ранее рассмотренная WhileDemo. Однако вместо изменения переменной loopCount в этом варианте программы введена специальная переменная цикла.
Выполнение цикла начинается с объявления переменной i и инициализации её значением переменной loopCount. Затем проверяется, является ли переменная i положительной. Если переменная положительна, то программа выводит уменьшенное на 3 значение i и возвращается к началу цикла.
«Согласно последнему стандарту языка индексная переменная, объявленная в части инициализации цикла for, известна только в пределах этого цикла. Программисты на С++ в этом случае говорят, что область видимости переменной ограничена циклом for. Например, в инструкции return рассмотренного выше примера, т.е. за пределами цикла, переменная i недоступна и не может использоваться. Однако этого новейшего правила придерживаются далеко не все компиляторы, и вам нужно протестировать свой компилятор, чтобы узнать, как он действует в этом случае. Dev-C++ при использовании i вне цикла выводит предупреждение, но позволяет программисту поступить по своему усмотрению.»
[Атас!]
Бесконечным называют такой цикл, который выполняется вечно. Бесконечный цикл создаётся каждый раз, когда условие выполнения цикла выполняется всегда, обычно вследствие какой-то ошибки в коде.
В следующей разновидности рассмотренного ранее цикла
while ( loopCount > 0 )
{
cout << "Осталось выполнить" << loopCount << " циклов\n"
}
программист забыл уменьшить переменную loopCount, как это делалось в предыдущих версиях. В результате получен счётчик цикла, значение которого никогда не изменяется. При этом условие выполнения цикла будет либо всегда истинно, либо всегда ложно. Выполнение такой программы никогда не закончится естественным путём, т.е., как говорят, программа зацикливается.
«Но... ничто не вечно под Луной! В конечном счёте электричество отключится, компьютер поломается, Microsoft обанкротится... Или цикл прервётся, и вам не о чём будет тревожиться. Существует гораздо больше способов создания бесконечных циклов, чем показано здесь; но обычно они слишком сложны для того, чтобы приводить их в такой простой книжке.»
В С++ определены две специальные управляющие программой команды — break и continue. Может случиться, что условие работы цикла нарушится не в начале или в конце, а где-то посередине цикла. Рассмотрим программу, которая суммирует введённые пользователем значения. Цикл прерывается, когда пользователь вводит отрицательное число.
Проблема в том, что программа не сможет выйти из цикла, пока пользователь не введёт число, но должна сделать это перед тем, как значение будет добавлено к сумме.
_________________
73 стр. Глава 5. Операторы управления программой
В таких случаях используют определёную в С++ команду break. Она немедленно передаёт управление в конец текущего цикла. После инструкции break программа будет выполнять инструкцию, следующую сразу же после закрывающей скобки цикла. Схематически работу команды break можно проиллюстрировать так:
while ( условие )
{
if ( какое-то другое условие )
{
break ; /* выход из цикла */
}
} /* когда программа встретит break, управление будет передано этой строке */
Вооружась новой командой break, я решил в программе BreakDemo проблему последовательного накопления суммы чисел.
/* BreakDemo — вводим множество чисел. */
/* Суммируем эти числа, пока пользователь не введёт отрицательное число */
#include
#include
#include
using namespace std ;
int main( int argc , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
/* Введите счётчик цикла */
int accumulator = 0 ;
cout << "Эта программа суммирует числа, "
<< "введённые пользователем\n" ;
cout << "Выполнение цикла "
<< "заканчивается после "
<< "ввода отрицательного числа\n" ;
/* Бесконечный цикл */
for ( ; ; )
{
/* Ввод следующего числа */
int value = 0 ;
cout << "Введите следующее число: " ;
cin >> value ;
/* если оно отрицательно... */
if ( value < 0 )
{
/* ...тогда выходим из цикла */
break ;
}
/* ...иначе добавляем число к общей сумме */
accumulator = accumulator + value ;
}
/* После выхода из цикла выводим результат суммирования */
cout << "\nОбщая сумма равна"
<< accumulator
<< "\n" ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ;
return 0 ;
}
_________________
74 стр. Часть 1. Первое знакомство с С++
После объяснения правил пользователю ( какие данные нужно ввести, чтобы выйти из цикла, и что делает эта программа ) управление получает нечто подобное бесконечному циклу for. Сначала на экран выводится запрос на введение числа с клавиатуры. Проверить его на соответствие критерию выхода из цикла можно только после того, как оно считано программой. Если введёное число отрицательно, управление переходит к оператору break и программа выходит из цикла. Если же число оказывается положительным, программа пропускает команду break и к накопленной сумме добавляет новое значение. После выхода из цикла программа выводит значение общей суммы всех введённых чисел и завершает работу.
«При повторяющемся выполнении операций над переменной в цикле проследите, чтобы инициализация переменной осуществлялась ещё до входа в цикл. В нашем случае программа обнулила переменную accumulator перед входом в цикл, в котором к ней добавляются новые числа. Вот пример результата работы программы:»
[Советы]
Эта программа суммирует числа, введённые пользователем
Выполнение цикла заканчивается после ввода отрицательного числа
Введите следующее число: 1
Введите следующее число: 2
Введите следующее число: 3
Введите следующее число: -1
Общая сумма равна 6
Press any key to continue...
Несколько реже используется команда continue. Столкнувшись с ней, программа немедленно возвращается к началу цикла. Остальные инструкции цикла игнорируются.
«В следующем фрагменте программы отрицательные числа, которые может ввести пользователь, игнорируются. Завершает цикл ввод нулевого значения. Полностью данную программу под именем ContinueDemo вы найдёте на прилагаемом компакт-диске.»
[Диск]
while ( 1 )
{
/* Ввод значения */
cout << "Введите значение" ;
cin >> inputVal ;
/* Если число отрицательное */
if ( inputVal < 0 )
{
/* Выводим сообщение об ошибке */
cout << "Не допускается ввод "
<< "отрицательных чисел\n" ;
/* Возвращаемся к началу цикла */
continue ;
}
/* Введено приемлемое значение */
}
_________________
75 стр. Глава 5. Операторы управления программой
Возвратимся к проблеме перерисовывания экрана монитора. Конечно, некоторые типы структур циклов можно использовать для прорисовки каждого пикселя линии слева направо ( или для евреев и арабов сканирование экрана осуществляется справа налево? ). Но как реализовать повторяющееся перерисовывание каждой сканируемой линии сверху вниз? ( Для австралийских экранов, наверное, снизу вверх? ) Для этого нужно включить цикл, сканирующий пиксели слева направо, внутрь цикла, сканирующего линии сверху вниз.
В случае если команды одного цикла находятся внутри другого, цикл называют вложенным. В качестве примера вы можете модифицировать программу BreakDemo в программу, которая накапливает сумму чисел любого количества последовательностей. В этой программе, названной NestedDemo, во внутреннем цикле суммируются числа, введённые с клавиатуры, пока пользователь не введёт отрицательное значение. Внешний цикл суммирует полученные суммы значений последовательностей до тех пор, пока накопленная сумма примет отличное от нуля значение.
/* NestedDemo — вводится последовательность чисел. */
/* Числа суммируются, пока пользователь не введёт отрицательное число. Этот процесс будет повторяться, пока общая сумма не станет равной 0. */
#include
#include
#include
using namespace std ;
int main( int argc , char* pszArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */
cout << "Эта программа суммирует "
<< "последовательности чисел.\n Ввод каждой "
<< "последовательности завершается "
<< "вводом отрицательного числа \n"
<< "Чтобы завершить ввод последовательностей,"
<< "нужно ввести\nдва отрицательных числа подряд\n" ;
/* Внешний цикл работает с последовательностями чисел */
int accumulator ;
do
{
/* Начинаем ввод очередной последовательности чисел */
accumulator = 0 ;
cout << "\nВведите очередную последовательность\n" ;
/* Бесконечный цикл */
for ( ; ; )
{
/* Введение очередного числа */
int value = 0 ;
cout << "Введите очередное число: " ;
cin >> value ;
/* Если оно отрицательное... */
if ( value < 0 )
{
/* ...выходим из цикла */
_________________
76 стр. Часть 1. Первое знакомство с С++
break ;
}
/* ...иначе добавляем число к общей сумме */
accumulator = accumulator + value ;
}
/* Вывод результата вычислений... */
cout << "\nОбщая сумма равна "
<< accumulator
<<"\n" ;
/* ... если накопленная общая сумма чисел последовательности не равна нулю, начинаем работать со следующей последовательностью */
} while ( accumulator != 0 ) ;
cout << "Программа завершена\n" ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ;
return 0 ;
}
Последняя управляющая инструкция эффективна, если существует необходимость выбора при ограниченном количестве возможных вариантов. Она похожа на усложнённую инструкцию if, которая вместо проверки одного условия анализирует множество разных возможностей:
switch ( выражение )
{
case c1 :
/* Переходим сюда, если выражение == c1 */
break ;
case c2 :
/* Переходим сюда, если выражение == c2 */
break ;
default :
/* Если ни одно условие не выполнено, переходим сюда */
}
Значением выражения должно быть целое число ( int, long или char ); c1, с2, с3 должны быть константами. Инструкция switch выполняется следующим образом: сначала вычисляется значение выражения, а затем оно сравнивается с константами, указанными после служебного слова case. Если константа соответствует значению выражения, то программа передаёт управление этой ветви. Если ни один вариант не подходит, выполняется условие default.
_________________
77 стр. Глава 5. Операторы управления программой
Рассмотрим для примера следующий фрагмент программы:
cout << "Введите 1, 2 или 3:" ;
cin >> choice ;
switch ( choice )
{
case 1 :
/* Обработка случая "1" */
break ;
case 2 :
/* Обработка случая "2" */
break ;
case 3 :
/* Обработка случая "3" */
break ;
default :
cout << "Вы ввели не 1, не 2 и не 3\n"
}
Ещё раз напомню, что инструкция switch эквивалентна усложнённой инструкции if ( с вложенными if-инструкциями ) ; однако, если рассматривается более двух-трёх случаев, структура switch оказывается нагляднее.
«Для выхода из инструкции switch необходимо использовать команды break, иначе управление будет переходить от одного случая к следующему.»
[Советы]
_________________
78 стр. Часть 1. Первое знакомство с С++