Глава 4. Введение в С и C++ 

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

Приводимые далее примеры можно набирать и запускать в среде MicrosoftVisualC++. Подразумевается, что в предыдущей главе вы научились выполнять элементарные операции по компиляции и отладке программ.

Из истории языка С

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

Операционная система UNIX была разработана в 1969 г. на маломощном, по современным представлениям, компьютере DEC PDP-7 в компании BellLaboratories, город Мюррей Хилл, штат Нью-Джерси. Система была полностью написана на языке ассемблера для PDP-7 и претендовала на звание "дружественной для программистов", поскольку содержала довольно мощный набор инструментов разработки и являлась достаточно открытой средой. Вскоре после создания UNIX Кен Томпсон (KenThompson) написал компилятор нового языка В. С этого момента мы можем начать отслеживать историю языка С, поскольку язык В Кена Томпсона был непосредственным его предшественником. Рассмотрим родословную языка С:

Язык         Год и место создания

Algol 60      Разработан международным комитетом в 1960 г.

CPL           Combined Programming Language — комбинированный язык программирования.
Разработан в 1963 г. группой программистов из Кембриджского и Лондонского университетов

BCPL         BasicCombinedProgrammingLanguage — базовый комбинированный язык про­граммирования. Разработан в Кембридже Мартином Ричардсом (MartinRichards) в 1967 г.

В                Разработан в 1970 г. Кеном Томпсоном, компания BellLabs

С                Разработан в 1972 г. Деннисом Ритчи (DennisRitchie), компания BellLabs

Позже, в 1983 г., при Американском институте национальных стандартов (American National Standards Institute — ANSI) был создан специальный комитет с целью стандартизации языка С, в результате чего был разработан стандарт ANSI С.

Язык Algol 60 появился всего на пару лет позже языка FORTRAN. Это был значительно более мощный язык, который во многом предопределил пути дальнейшего развития большинства последующих языков программирования. Его создатели уделили много внимания логической целостности синтаксиса команд и модульной структуре программ, с чем, собственно, впоследствии и ассоциировались все языки высокого уровня. К сожалению, Algol 60 не стал популярным в США. Многие считают, что причиной тому была определенная абстрактность этого языка.

Разработчики языка CPL попытались приблизить "возвышенный" Algol 60 к реалиям конкретных компьютеров. Тем не менее, данный язык остался таким же трудным для изучения и практического применения, как и Algol 60, что предопределило его судьбу. В наследство от CPL остался язык BCPL, представляющий собой упрощенную версию CPL, сохранившую лишь основные его функции.

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

Например, когда Кен Томпсон закончил создание языка В, появился новый компьютер PDP-11. Система UNIX и компилятор языка В были сразу же модернизированы под новую машину. Хотя PDP-11 был, несомненно, мощнее, чем его предшественник PDP-7, возможности этого компьютера оставались все еще слишком скромными по сравнению а современными стандартами. Он имел только 24 Кб оперативной памяти, из которых 16 Кб отводились операционной системе, и 512 Кб на жестком диске. Возникла идея переписать UNIX на языке В. Но В работал слишком медленно, поскольку оставался языком интерпретирующего типа. Была и другая проблема: язык В ориентировался на работу со словами, тогда как компьютер PDP-11 оперировал байтами. Стала очевидной необходимость усовершенствования языка В. Работа над более совершенной версией, которую назвали языком С, началась в 1971 г.

Деннис Ритчи, который известен как создатель языка С, частично восстановил независимость языка от конкретного оборудования, что было во многом утеряно в языках BCPL и В. Так, были успешно введены в практику типы данных и в то же время сохранена возможность прямого доступа к оборудованию — идея, заложенная еще в языке СPL.

Многие языки программирования, разработанные отдельными авторами (С, Pascal, Lisp и APL), отличались большей целостностью, чем те, над которыми трудились группы разработчиков (Ada, PL/I и Algol 60). Кроме того, для языков, разработанных одним автором, характерна большая специализированность в решении тех вопросов, в которых автор разбирался лучше всего. Деннис Ритчи был признанным специалистом в области системного программирования, а именно: языков программирования, операционных систем и генераторов программ. Учитывая профессиональную направленность автора, нетрудно понять, почему С был признан прежде всего разработчиками системных программ. Язык С представлял собой язык программирования относительно низкого уровня, что позволяло контролировать каждую мелочь в работе алгоритма и достигать максимальной эффективности. Но в то же время в С заложены принципы языка высокого уровня, что позволяло избежать зависимости программ от особенностей архитектуры конкретного компьютера. Это повышало эффективность процесса программирования.

Отличия С от других ранних языков высокого уровня

Вам, конечно, интересно узнать, какое место занимает С в ряду других языков программирования. Чтобы ответить на этот вопрос, рассмотрим примерную иерар­хию языков, показанную на рис. 4.1, где точками представлены промежуточные этапы развития. К примеру, давние предки компьютеров, такие как станок Джакарда (Jacquard'sloom) (1805 г.) и счетная машина Чарльза Беббиджа (CharlesBabbage) (1834 г.)-, программировались механически для выполнения строго определенных функций. Не исключено, что в скором будущем мы сможем управлять компьютерами, посылая нейронные импульсы непосредственно из коры головного мозга, например подключив воспринимающие контакты к височной области мозга (участвующей в запоминании слов) или к области Брока (управляющей функцией речи).

Нейронные системы

Искусственный интеллект

Командные языки операционных систем

Проблемно-ориентированные языки

Машинно-ориентированные языки

Языки ассемблера

Механическое программирование оборудования

Рис. 4.1. Теоретические этапы развития языков программирования

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

Первые языки высокого уровня, например FORTRAN, разрабатывались как альтернатива языкам ассемблера. Они обеспечивали определенный уровень абстракции от аппаратной среды и позволяли строить алгоритм с точки зрения решаемой задачи, а не работы компьютера. К сожалению, их создатели не учли динамики развития компьютерной техники и в погоне за упрощением процесса программиро­вания упустили ряд существенных моментов. Языки FORTRAN и Algol оказались слишком абстрактными для системных программистов. Эти языки были проблемно-ориентированными, рассчитанными на решение общих инженерных, научных и экономических задач. Программистам, занимающимся разработкой новых системных продуктов, по-прежнему приходилось полагаться только на старые языки ассемблера.

Чтобы разрешить эту проблему, разработчикам языков пришлось сделать шаг назад и создать класс машинно-ориентированных языков. К таким языкам низкого уровня относились BCPL и В. Но при работе с ними возникла другая проблема: они были приспособлены только для компьютеров определенной архитектуры. Язык С оказался шагом вперед по сравнению с машинно-ориентированными языками, сохранив при этом достаточно "низкий" уровень программирования в сравнении с большинством проблемно-ориентированных языков. Язык С позволяет контролировать процесс выполнения программы компьютером, игнорируя в то же время особенности аппаратной среды. Вот почему С рассматривается одновременно как язык программирования высокого и низкого уровней.

Достоинства языка С

Язык программирования часто можно определить, просто взглянув на исходный текст программы. Так, программа на языке APL напоминает иероглифы, текст на языке ассемблера представляется столбцами мнемоник, язык Pascal выделяется своим читабельным синтаксисом. А что можно сказать о языке С? Многие программисты, впервые столкнувшиеся с ним, находят его слишком замысловатым и пугающим. Конструкции, напоминающие выражения на английском языке, которые характерны для многих языков программирования, в С встречаются довольно редко. Вместо этого программист сталкивается с необычного вида операторами и обилием указателей. Многие возможности языка уходят своими корнями к особенностям программирования на компьютерах, существовавших на момент его появления. Ниже рассматриваются некоторые сильные стороны языка С.

Оптимальный размер программы

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

Сокращенный набор ключевых слов    

Первоначально, в том виде, в каком его создал Деннис Ритчи, язык С содержал всего 27 ключевых слов. В ANSI С было добавлено несколько новых зарезервированных слов. В Microsoft С набор ключевых слов был еще доработан, и общее их число превысило 50.

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

Быстрое выполнение программ

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

Упрощенный контроль за типами данных

В отличие от языка Pascal, в котором ведется строгий контроль типов данных, в С понятие типа данных трактуется несколько шире. Это унаследовано от языка В, который так же свободно обращался с данными разных типов. Язык С позволяет в одном месте программы рассматривать переменную как символ, а в другом месте — как ASCII-код этого символа, от которого можно отнять 32, чтобы перевести символ в верхний регистр.

Реализация принципа проектирования "сверху вниз"

Язык С содержит все управляющие конструкции, характерные для современных языков программирования, в том числе инструкции for, if/else, switch/case, while и другие. На момент появления языка это было очень большим достижением. Язык С также позволяет создавать изолированные программные блоки, в пределах которых переменные имеют собственную область видимости. Разрешается создавать локальные переменные и передавать в подпрограммы значения параметров, а не сами параметры, чтобы защитить их от модификации.

Модульная структура

Язык С поддерживает модульное программирование, суть которого состоит в возможности раздельной компиляции и компоновки разных частей программы. Например, вы можете выполнить компиляцию только той части программы, которая была изменена в ходе последнего сеанса редактирования. Это значительно ускоряет процесс разработки больших и даже среднего размера проектов, особенно если приходится работать на медленных машинах. Если бы язык С не поддерживал модульное программирование, то после внесения небольших изменений в программный код пришлось бы компилировать всю программу целиком, что могло бы занять слишком много времени.

Возможность использования кодов ассемблера

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

Возможность управления отдельными битами данных

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

Наличие указателей

Язык, используемый для системного программирования, должен предоставлять возможность обращаться к области памяти с заданным адресом, что значительно повышает скорость выполнения программы. В С эта возможность реализуется за счет использования указателей. Хотя указатели применяются и во многих других языках программирования, только С позволяет выполнять над указателями арифметические операции. Например, если переменная student_record__ptr указывает на первый элемент массива student_records, то выражение student_record__ptr + 1 будет указывать на второй элемент массива.

Возможность гибкого управления структурами данных

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

Эффективное использование памяти

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

Возможность кросс-платформенной переносимости

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

Наличие мощных библиотек готовых функций

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

Недостатки языка С

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

Упрощенный контроль за типами данных!

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

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

Ограниченные средства управления ходом выполнения программы

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

Язык С — не для любителей!

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

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

Стандарт ANSI С

Специальный комитет Американского института национальных стандартов (ANSI) занимался разработкой стандартов языка С. Давайте рассмотрим, какие изменения были предложены и внесены в язык программирования в результате работы этого комитета. Основные цели внесения изменений заключались в том, чтобы повысить гибкость языка и стандартизировать предлагаемые различными компиляторами возможности.

Ранее единственным стандартом языка С была книга Б. Кернигана и Д. Ритчи "Язык программирования С" (изд-во PrenticeHall, 1978 г.). Но эта книга не опускалась до описания отдельных технических деталей языка, что не гарантировало стандартности компиляторов С, создаваемых разными фирмами. Стандарт ANSIдолжен был устранить эту неоднозначность. Хотя некоторые из предложенных изменений могли привести к возникновению проблем при выполнении ранее напи­санных программ, ожидалось, что негативный эффект не будет существенным.

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

Комитет ANSI также подготовил ряд положений в виде меморандума, которые выражали "дух языка С". Вот некоторые из этих положений.

В дополнение были предприняты усилия для того, чтобы обеспечить соответствие стандарта ANSI (американского) стандарту, предложенному ISO (InternationalStandardOrganization — Международная организация по стандартизации). Благодаря этим усилиям язык С оказался, пожалуй, единственным языком программирования, который эффективно применяется в разноязычных средах с различными кодовыми таблицами. В табл. 4.1. перечислены некоторые аспекты языка С, стандартизированные комитетом ANSI.

Таблица 4.1. Рекомендации комитета ANSI разработчикам компиляторов языка С
Аспект Предложенные стандарты
Типы данных Четыре: символьный, целочисленный, с плавающей запятой и перечисление
Комментарии / * — начало, * / — конец; добавлен It:любой набор символов в строке справа будет игнорироваться компилятором
Длина идентификатора 31 символ; этого достаточно для обеспечения уникальности идентификатора
Стандартные идентификаторы и файлы заголовков Разработан минимальный набор идентификаторов и файлов заголовков, необходимый для осуществления базовых операций, например ввода/вывода
Директивы препроцессора Значку #, с которого начинается директива препроцессора, может предшествовать отступ (любая комбинация пробелов и символов табуляции), помогающий отличить директиву от остального программного кода; в некоторых ранних компиляторах существовало требование помещать директивы препроцессора только начиная с первой позиции строки
Новые директивы препроцессора Определена конструкция if условие (выражение) #elif (выражение)
Запись выражений в несколько строк Комитет принял решение, что смежные литералы должны объединяться; таким образом, выражение с оператором #define может быть записано в две строки
Стандартные библиотеки В предложенном стандарте ANSI определен базовый набор внешних и системных функций, таких как read () и write ()
Управление выводом Был согласован набор управляющих последовательностей, включающий символы форматирования, такие как разрыв строки, разрыв страницы и символ табуляции
Ключевые слова Был согласован минимальный набор ключевых слов, необходимых для построения работоспособных выражений на языке С
size of() Комитет пришел к выводу, что оператор sizeof () должен возвращать значение типа size_t вместо системно-зависимой целочисленной переменной
Прототипы функций Комитет постановил, что все компиляторы языка С должны поддерживать программы, как использующие, так и не использующие прототипы функций
Аргументы командной строки Был согласован и утвержден единый синтаксис использования аргументов командной строки
Тип данных void Ключевое слово void может использоваться в функциях, не возвра­щающих значения; для функции, возвращающей значение, результат может быть приведен к типу void: это служит указанием компилятору, что возвращаемое значение умышленно игнорируется
Использование структур Отменено требование уникальности имен членов структур и объединений; структуры могут передаваться в виде аргументов функций и возвращаться функциями, а также присваиваться другим структурам того же типа
Объявления функций Объявление функции может включать список типов аргументов, на основании которого компилятор определяет число и тип аргументов
Шестнадцатеричные числа Шестнадцатеричное число должно начинаться с обозначения \х, за которым следует несколько шестнадцатеричных цифр (0-9, a-f, A-F); например, десятичному числу 16 соответствует Шестнадцатеричное \х!0 (допускается также запись 0x10)
Триграммы Триграммами называются последовательности символов, которые представляют клавиши, отсутствующие в некоторых клавиатурах; например, комбинацию символов ??<можно использовать вместо символа (

Переход от С к C++ и объектно-ориентированному программированию

Язык C++ можно рассматривать как надмножество для языка С. C++ сохранил все возможности, предоставляемые языком С, дополнив их средствами объектно-ориентированного программирования. Он позволяет решать задачи на достаточно высоком уровне абстракции, превосходя в этом отношении даже язык Ada, поддерживает модульное программирование, как в Modula-2, но сохраняет при этом простоту, компактность и эффективность языка С.

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

Из истории языка C++

Вряд ли вас удивит тот факт, что своими корнями C++ восходит к языку С. В то же время C++ впитал в себя многие идеи, реализованные не только в языках BCPLи Algol 60, но и в Simula 67. Возможность перегрузки операторов и объявления переменных непосредственно перед их первым использованием сближает C++ с языком Algol 60. Концепция подклассов (или производных классов) и виртуальных функций была заимствована из Simula 67. Впрочем, все популярные языки програм­мирования представляют собой набор усовершенствованных средств и функций, взятых из других, более ранних языков программирования. Но, безусловно, ближе всего язык C++ стоит к языку С.

Язык C++ был разработан в начале 80-х в BellLabs Бьярном Страуструпом (BjarneStroustrup). Сам доктор Страуструп утверждает, что название C++ было предложено Риком Масситти (RickMascitti). C++ изначально был создан для целей разработки некоторых высокоточных событийных моделей, поскольку необходимая эффективность не могла быть достигнута с помощью других языков программирования.

Впервые C++ был использован вне стен лаборатории доктора Страуструпа в 1983 г., но еще до лета 1987 г. шли работы по отладке и совершенствованию этого языка.

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

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

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

Эффективность объектно-ориентированного подхода

Остановимся ненадолго на объектно-ориентированном программировании, чтобы на примере показать, как использование абстрактных объектов языка C++ может облегчить решение прикладных задач по сравнению с более старыми процедурными языками. Предположим, например, что нам нужно написать программу на языке FORTRAN для оперирования таблицей успеваемости студентов. Чтобы решить эту задачу, необходимо будет создать ряд массивов, представляющих различные поля таблицы. Все массивы должны быть связаны посредством общего индекса. Для создания таблицы из десяти полей необходимо запрограммировать доступ к десяти массивам с помощью единого индекса, чтобы данные из разных массивов возвращались как единая запись таблицы.

В C++ решение этой задачи сводится к объявлению простого объекта student_database, способного принимать сообщения add_student, delete_student, access_student и display_ student для оперирования данными, содержащимися в объекте. Обращение к объекту student_database реализуется очень просто. Допустим, нужно добавить новую запись в таблицу. Для этого достаточно ввести в программу следующую строку:

student_database . add_student (new__recruit)

В данном примере функция add__student () является методом класса, связанного с объектом student_database, а параметр new_recruit представляет собой набор данных, добавляемый в таблицу. Обратите внимание на то, что класс объектов student_database не является встроенным типом данных языка C++. Наоборот, мы расширили язык программирования собственными средствами, предназначенными для решения конкретной задачи. Возможность создавать новые классы или модифицировать существующие (порождать от них подклассы) позволяет программисту естественным образом устанавливать соответствие между понятиями предметной области и средствами языка программирования.

Незаметные различия между С и C++

Ниже мы рассмотрим некоторые отличия языка C++ от языка С, не связанные с объектным программированием.

Синтаксис комментариев

В C++ комментарии могут вводиться с помощью конструкции //,хотя старый метод маркирования блока комментариев посредством символов /* и */ по-прежнему допустим.

Перечисления

Имя перечисления является названием типа данных. Это упрощает описание перечислений, поскольку устраняет необходимость в указании ключевого слова enum перед именем перечисления.

Структуры и классы

Имена структур и классов являются названиями типов данных. В языке С понятие класса отсутствует. В C++ нет необходимости перед именами структур и классов указывать ключевое слово struct или class.

Область видимости в пределах блока

Язык C++ дает возможность объявлять переменные внутри блоков программного кода, т.е. непосредственно перед их использованием. Переменная цикла может объявляться даже непосредственно внутри инициализатора цикла, как в следующем примере:

.// Объявление переменной непосредственно в месте ее использования
for(int index = 0; index < MAX_ROWS; index++)

Оператор расширения области видимости

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

Ключевое слово const

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

Безымянные объединения

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

Явные преобразования типов

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

Уникальные особенности функций

Язык C++ понравится программистам, работавшим ранее с языками Pascal, Modula-2 и Ada, поскольку позволяет задавать имена и типы параметров функции прямо внутри круглых скобок, следующих за именем функции. Например:

void* vfunc(void *dest,   int  с,  unsigned count)
{
      .
      .
      .
}

В языке С после принятия стандарта ANSI также появилась возможность использования таких выражений. Таким образом, стандарт ANSI оказал влияние на создателей языка C++.

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

Перегрузка функций

В C++ можно использовать одинаковые имена для нескольких функций. Обычно разные функции имеют разные имена. Но иногда требуется, чтобы одна и та же функция выполняла сходные действия над объектами различных типов. В этом случае имеет смысл определить несколько функций с одинаковым именем, но разным телом. Такие функции должны иметь отличающиеся наборы аргументов, чтобы компилятор мог различать их. Ниже показан пример объявления перегруженной функции с именем total(),принимающей в качестве аргументов массивы чисел типа int, float и double.

int total(intisize, int iarray[]);

float total(intisize, float farray!]);

double total(int isize, double darray[]); 
      .
      .    

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

total(isize, iarray);
total(isize, farray);
total(isize, darray);

Стандартные значения параметров функций

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

Списки аргументов переменного размера

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

Во вторую редакцию языка С данная возможность также была включена. В этом смысле язык C++ оказал влияние на язык С.

Использование ссылок на аргументы функций

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

int i;

increment(i);   

void increment(int &variable_reference) 

{

variable_reference++;

}

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

Макроподстановка функций

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

Оператор new и delete

Новые операторы new и delete, добавленные в язык C++, позволяют выделять и удалять в программе динамические области памяти.

Указатели типа void

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

Ключевые различия между С и С++

Наиболее существенное отличие C++ от языка С состоит в использовании концепции объектно-ориентированного программирования. Ниже рассмотрены связанные с этим новые средства языки C++.

Классы и инкапсуляция данных

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

Структуры в роли классов

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

Конструкторы и деструкторы

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

Сообщения

Основным средством манипуляции объектами являются сообщения. Сообщения посылаются объектам (переменным типа класса) посредством особого механизма, напоминающего вызов функции. Набор сообщений, которые могут посылаться объектам класса, задается при описании этого класса. Каждый объект отвечает на полученное сообщение, выполняя определенные действия. Например, если есть объект Palette_Colors, для которого задан метод SetNumColors_Method, требующий указания одного целочисленного параметра, то передача объекту сообщения будет выглядеть следующим образом:

Palette_Colors.SetNumColors_Method(16);

Дружественные функции

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

Перегрузка операторов

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

Производные классы

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

Полиморфизм и виртуальные функции

Чтобы понять смысл термина полиморфизм, рассмотрим древовидную иерархию родительского класса и порожденных от него подклассов. Каждый подкласс в этой структуре может получать сообщения с одинаковым именем. Когда объект какого-либо подкласса принимает такое сообщение, он на основании типа и количества переданных параметров определяет, к какому классу относится данное сообщение, и предпринимает соответствующие действия. Если сообщения сразу нескольких классов в иерархии имеют одинаковый формат, считается, что сообщение относится к ближайшему классу в иерархии. Методы родительского класса, которые могут переопределяться в подклассах, называются виртуальными и создаются с указанием ключевого слова virtual.

Потоковые классы

Язык C++ содержит дополнительные средства ввода/вывода. Три объекта cin, cout и cerr, подключаемые к программе посредством файла IOSTREAM.H, служат для выполнения операций консольного ввода и вывода. Все операторы классов этих объектов могут быть перегружены в производных классах, создаваемых пользователем. Благодаря этой возможности операции ввода/вывода можно легко настраивать в соответствии с особенностями работы конкретного приложения.

Основные компоненты программ на языках C/C++

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

Простейшая программа на языке С

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

/*

*       simple.с.

*      Ваша первая программа на С.

*/

#include <stdio.h>

int main ()

{

printf("Здравствуй, мир! ");

return(0);

}

В этом маленьком тексте программы скрыто много интересного. Начнем с блока комментариев:

/*  

*       simple.с

*  Ваша первая программа на С.

*/

Любая программа, написанная профессиональным программистом, всегда начинается с комментариев. В языке С блок комментариев Начинается символами / *, а заканчивается символами */. Все, что находится между ними, игнорируется компи­лятором.

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

#include <stdio.h>

Директивы препроцессора — это своего рода команды компилятора. В данном случае компилятор получает указание поместить в этом месте программы код, хранящийся в библиотечном файле STDIO.H. Файлы с расширением Н называются файлами заголовков и обычно содержат объявления различных констант и идентификаторов, а также прототипы функций. Хранение такого рода информации в отдельном файле облегчает доступ к ней из разных программ и улучшает структурированность программы.

Вслед за директивой препроцессора расположен блок описания функции:

int main ()

return(0); /* или return 0; */

Все программы на языке С обязательно содержат функцию main ( ) . С нее начинается выполнение программы, которое завершается вызовом инструкции return. Ключевое слово intслева от имени функции указывает на тип возвращаемых ею значений. В нашем случае возвращается целое число. Значение 0 в инструкции return будет воспринято как признак успешного завершения программы. Допускается использование инструкции return без круглых скобок.

Тело функции main () расположено между символами { и } , называемыми фигурными скобками. Фигурные скобки широко применяются для выделения в программе блоков инструкций. Это может быть тело функции, как в данном примере, тело цикла, например for или while, либо операторная часть условных конструкций if/else или switch/case.

В нашем случае тело функции main ( ) , помимо стандартного вызова инструкции return, состоит из единственной команды, осуществляющей вывод на экран строки приветствия:

printf("Здравствуй, мир! ");

Прототип функции printf () описан в файле STDIO.H.

Простейшая программа на языке C++

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

//
//   simple.cpp
//  Ваша первая программа на C++

#include <iostream.h>

int main ()

{

cout  <<   "   Здравствуй,   мир!   ";

return(0);

} 

Имеется три различия между этой программой и той, что мы рассмотрели ранее. Во-первых, комментарии выделены не парой символов /* */, а символами //, расположенными в каждой строке блока комментариев. Во-вторых, имя файла в директиве finclude было изменено на IOSTREAM.H. И наконец, вывод дайных осуществляется посредством объекта cout, который появился в программе "благодаря" файлу IOSTREAM.H. В следующих примерах книги мы будем обращать ваше внимание на другие отличия языка C++ от С.

Получение данных от пользователя в языке С

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

/*
*  ui.c
*  Данная программа предлагает пользователю ввести длину в футах, после
*  чего переводит полученное значение в метры и сантиметры.
*/

#include <stdio.h>

int main ()

{

float feet, meters, centimeters;

printf("Введите количество футов: ") ;

scanf("%f,Sfeet);

while(feet > 0) {

centimeters = feet * 12 * 2.54;
meters = centimeters/100; printf("%8.2f(футы) равняется\n", feet);
printf("%8.2f(метры) \n",meters);
printf("%8.2f(сантиметры) \n",centimeters);
printf("\nВведите другое значение \n");
printf("(О- конец программы): ") ;
scanf("%f", &feet);

}
printf(">>>До свидания! <<<") ;

return(0);

}

Объявление переменных

Первое, что бросается в глаза в этом примере, — объявление переменных:

float feet, meters, centimeters;

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

Ввод данных пользователем

Следующие строки, вид которых может показаться довольно необычным, обес­печивают ввод данных пользователем:

printf("Введите количество футов: ");
scanf("%f",&feet);

Функция scant () должна содержать строку форматирования, которая определяет, в каком порядке будут вводиться внешние данные и как они будут интерпрети­роваться программой. Заключенный в кавычки параметр %f указывает, что вводимые данные будут приведены к типу float. В языках С и C++ для значений этого типа отводится 4 байта. (Более подробно о различных типах данных, существующих в C/C++, см. в следующей главе.)

Оператор взятия адреса

Обратите внимание, что.в рассмотренном выше примере переменной feet в функции scanf () предшествует символ амперсанда (&). Это оператор взятия адреса. Всюду, где имени переменной предшествует этот оператор, компилятор будет использовать вместо значения переменной ее адрес. Особенность функции scanf () заключается в том, что она ожидает именно адрес переменной, которой будет присвоено новое значение.

Простейший цикл while

Один из самых простых способов создания цикла в программе на языке С заключается в использовании инструкции while:

while(feet  >  0)   

{

}
Это так называемый цикле предусловием. Он начинается с ключевого слова whi1е, за которым следует логическое выражение, возвращающее TRUE или FALSE. Фигурные скобки необходимы, когда цикл содержит более одной команды. В противном случае скобки необязательны. Строки, заключенные между фигурными скобками, формируют тело цикла.

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

Вывод данных на экран

В рассматриваемом примере мы повстречались с более сложным способом вывода информации на экран:

printf("%8.2f(футы) равняется\n",feet);

printf("%8.2f(метры) \n", meters);

printf("%8.2f(сантиметры) \n",centimeters);

printf("\nВведите другое значение' \n");

printf("(О— конец программы): ");  

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

Давайте рассмотрим элементы строки форматирования первой из функций printf ().

Элемент строки      Назначение форматирования
%8.2f  Задает интерпретацию переменной feet   как числа типа float   в
следующем формате: 8 символов до запятой и 2 символа после
(футы)  равняется После вывода значения переменной feet будет сделан пробел и отображена строка (футы) равняется
\n Символ новой строки
, Запятая отделяет строку форматирования от списка обозначенных в ней переменных

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

Введите количество футов: 10 10.00 (футы) равняется

3.05(метры)
304.80 (сантиметры) 

Введите другое значение (0 — конец программы): 0

При выводе данных можно в строках форматирования указывать так называемые управляющие последовательности наподобие символа новой строки в рассматриваемом примере. Все они перечислены в табл. 4.2. В случае записи ASCII-кода символа в восьмеричном/шестнадцатеричном представлении ведущие нули игнорируются компилятором, а сама последовательность завершается, когда встречается не используемый в данной системе счисления символ или четвертый/третий подряд восьмеричный/шестнадцатеричный символ, не считая ведущих нулей.

Таблица 4.2. Управляющие последовательности
Последовательность  Что обозначает
Предупреждающий звуковой сигнал
\b Стирание предыдущего символа
\f Перевод страницы
\n Новая строка
\r Возврат каретки
\t Горизонтальная табуляция
\v Вертикальная табуляция
\? Знак вопроса
\' Одинарная кавычка
\" Двойная кавычка
\\ Обратная косая черта
\ddd ASCII-код символа в восьмеричной системе
\xdd ASCII-код символа в шестнадцатеричной системе

Получение данных от пользователя в языке C++

Ниже показана версия предыдущего примера, переписанная в соответствии с синтаксисом языка C++:

// 

//ui.cpp

//Данная программа предлагает пользователю ввести длину в футах, после

//чего переводит полученное значение в метры и сантиметры.

//

#include <iostream.h>
#include <iomanip.h>
int main()

float feet, meters, centimeters;

cout << "Введите количество футов: ";

cin >> feet;

while(feet > 0)

{

centimeters = feet * 12 * 2.54;

meters = centimeters/100;

cout << setw(8) << setprecision(2)

<< setiosflags(ios: : fixed) << feet

<< " (футы) равняется \n"; cout << setw(8) 

<< meters << " (метры) \n"; cout << setw(8)

<< centimeters << " (сантиметры) \n";

cout << "\nВведитедругоезначение\n";

cout << "(0— конец программы): ";

cin >> feet;

}

cout << ">>> До свидания! <<<";

return(0);

Можно обнаружить пять основных различий между показанной программой на языке C++ и ее аналогом на С. Первые два состоят в применении объектов cin и cout вместо функций scanf () и printf (). В выражениях с ними также используются операторы << (для вывода) и >> (для ввода) классов ostream и istream, подключаемых в файле IOSTREAM. Н. Оба этих оператора являются перегруженными и поддерживают ввод/вывод данных всех базовых типов. Их можно также перегрузить для работы с пользовательскими типами данных.

Оставшиеся три различия связаны с особенностями форматированного вывода в C++. Чтобы добиться такого же формата вывода, какой был получен с помощью простой строки форматирования %8. 2f в программе на языке С, в C++ потребуются три дополнительных выражения. Файл IOMANIP.H, подключаемый в начале программы, содержит объявления трех функций, являющихся членами класса ios (базовый в иерархии классов ввода/вывода): setw(),setprecision () и setios-flags (). Функция setw () задает минимальную ширину (в символах) выводимого поля. Функция setprecision () задает число цифр после десятичной точки при выводе чисел с плавающей запятой. Функция setw () вынуждена повторяться три раза, поскольку определяет формат вывода только следующей за ней переменной, после чего все сделанные установки сбрасываются. В отличие от нее функция setiosflags () вызывается один раз, устанавливая флаг fixed, который задает вывод чисел с плавающей запятой в фиксированном формате, т.е. без экспоненты. Те программисты, которые работают с языком C++, но хотят использовать привычную функцию printf (), могут подключить библиотеку STDIO.H.

Файловый ввод-вывод

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

/*

* filel.c

*   Эта программа на языке С демонстрирует использование файлов как для

*   ввода, так и для вывода данных. Программа читает значение переменной

*   forder_price из файла customer.dat, вычисляет значение переменной

*   fbilling_price и записывает его в файл billing.dat.

*/

#include <stdio.h> #define MIN_DISCOUNT .97

#define MAX_DISCOUNT .95

int main ()

{

float forder_price, fbilling_price;

FILE *fin,*fout;

fin = fopen("customer.dat","r"); fout = fopen("billing.dat","w");

while (fscanf(fin,"%f",&forder_price) != EOF) {

fprintf(fout,"Для заказа на сумму \t$%8.2f\n",

forder_price); if (forder_price < 10000)

fbilling_price = forder_price * MIN_DISCOUNT;

else fbilling_price = forder_price * MAX_DISCODNT;

fprintf(fout,"цена со скидкой равна \t$%8.2f\n\n",

fbilling_price);

}

return(0);

}

Каждому файлу в программе соответствует свой указатель типа file. Структура file, описанная в библиотеке STDIO.H, содержит всевозможную информацию о файле, включая путь к нему, имя и атрибуты. В приводимом ниже выражении создаются два указателя файла:

FILE   *fin,   *fout;

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

fin = fopen ("customer.dat","r"); fout = fopen("billing.dat","w");

Каждая функция fopen () возвращает инициализированный указатель файла. В процессе выполнения программы их значения не должны меняться вручную.

Второй параметр функции fopen (} определяет режим доступа к файлу (табл. 4.3). Файл может быть также открыт в следующих режимах: текстовом (включается путем добавления символа t к обозначению режима доступа) и двоичном (включается путем добавления символа Ь). В текстовом режиме компилятор языка С в процессе ввода данных заменяет пары символов возврата каретки и перевода строки одним символом новой строки. При выводе выполняется обратное преобразование. В двоичных файлах эти символы никак не обрабатываются.

Таблица 4.3. Режимы доступа к файлу в языке С
Режим доступа Описание
а  Файл открывается для добавления данных; если файл не существует, он
создается; все новые данные добавляются в конец файла
а+  Аналогичен предыдущему режиму, но допускает считывание данных
r Открывает файл только для чтения; если файл не существует, открытия
файла не происходит
r+ Открывает файл как для чтения, так и для записи; если файл не
существует, открытия файла не происходит
w Открывает пустой файл для записи; если файл существует, его содержимое стирается
w+   Открывает пустой файл как для записи, так и для чтения; если файл
существует, его содержимое стирается

Режимы r+, w+ и а+ позволяют как читать данные из файла, так и осуществлять их запись. При переходе от считывания к записи не забудьте модифицировать текущую позицию указателя файла с помощью функций fsetpos (), fseek()или rewind() .

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

/*

*   filel.c

*   Эта программа на языке С демонстрирует использование файлов как для

*  ввода, так и для вывода данных. Программа читает значение переменной

*   forder_price из файла customer.dat, вычисляет значение переменной

*   fbilling_price и записывает его в файл billing.dat.

*/

#include <stdio.h>
#define MIN_DISCOUNT .97
#define MAX_DISCOUNT .95

int main() {

float forder_price, fbilling_price;

FILE *fin,*fout;

fin = fopen ("customer.dat","r"); fout = fopen("billing.dat","w");

while (fscanf (fin,"%f",&forder_price) != EOF)

{ fprintf(fout,"Для заказа на сумму \t$%8.2f\n",

forder_price); if (forder_price < 10000)

fbilling_price = forder_price * MIN_DISCOUNT;
else fbilling_price = forderjirice * MAX_DISCOUNT; fprintf(fout,"цена со скидкой равна \t$%8.2f\n\n", fbilling_price);}

fclose(fin); fclose(fout) ;

return(0);}

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

//
//       file2.cpp 
//       Эта программа на языке C++ демонстрирует использование  файлов  как для
//       ввода,   так и для  вывода  данных.   Программа  читает  значение  переменной
//       forder_price из файла customer.dat,   вычисляет  значение  переменной
//       fbilling_price и записывает его в файл billing.dat.
//

#include <fstream.h>
#include <iomanip.h>
#define MIN_DISCOUNT .97
#define MAX DISCOUNT .95

int main( ) {

float forder_price, fbilling_price;

ifstream fin("customer.dat");

ofstream fout("billing.dat");

fin >> forder_price; while (Ifin.eofO){

fout << setiosflags(ios::fixed); fout << "Для заказа на сумму\t\t$" << setprecision(2)

<< setw(8)<< forder_price << "\n";

if (forder_price < 10000)

fbilling_price = forder_price * MIN_DISCOUNT; else fbilling_price = forder_price * MAXDISCOUNT;
fout << "цена со скидкой равна\t$"

<< setw(8) << fbilling_price << "\n\n"; fin >> forder_price;

fin. close () ;
fout .close () ;

return(0);

Операции записи данных в файл и считывания информации из файла отличаются в языках C++ и С. Как видно из сравнения двух примеров, вместо вызова функции fopen () происходит создание двух объектов классов ifstream (содержит функции файлового ввода) и ofstream (содержит функции файлового вывода). Далее работа выполняется с помощью уже знакомых нам операторов >> и <<, а также функций форматирования setw(),setprecision () и setiosflags(). В целом в C++ для ввода/вывода данных посредством консолей и файлов используются те же операторы с тем же синтаксисом, что существенно упрощает программирование операций ввода/вывода — область программирования, всегда считавшаяся сложной и чреватой ошибками.