Астраханскийгосударственный технический университет
Кафедра «Информационных
технологийи коммуникаций»
Конспектлекций по дисциплине
«Основыалгоритмического языка С++»
для специальности 220200.
Астрахань2000 г.
1. Переменныеи операции языка С++… 4
ИЗУЧАЕМЫЕПОНЯТИЯ… 5
Буквы и цифры… 6
Пробельныесимволы… 6
Знакипунктуации и специальные символы… 6
ESC-последовательности… 8
Операции… 9
Константы… 11
Целыеконстанты… 11
Константыс плавающей точкой… 13
Константа-символ… 14
Строковыелитералы… 14
Идентификаторы… 15
Ключевыеслова… 15
Комментарии… 16
Лексемы… 17
ИСХОДНЫЕТЕКСТЫ ПРИМЕРОВ… 17
2. Конструкциипринятия решений и циклы… 23
ВОПРОСЫ И ОТВЕТЫ… 23
СТРУКТУРА ПРОГРАММЫ… 26
Исходная программа… 26
ОБЪЯВЛЕНИЯ… 27
Спецификаторы типов… 28
Деклараторы… 30
Деклараторы массивов, функций и указателей… 30
Составные деклараторы… 31
Об«явления переменной… 33
Объявление простой переменной… 34
Объявление перечисления… 34
Объявления структур… 36
Битовые поля… 37
Об»явление совмещений… 38
Об«явление массива… 39
Об»явление функций… 42
Классы памяти… 45
Об«явления переменной на внешнем уровне… 45
Об»явление переменной на внутреннем уровне… 48
Об«явление функции на внешнем и внутреннем уровнях… 49
Инициализация… 50
Базовые типы и типы указателей… 50
Составные типы… 51
Строковые инициализаторы… 53
Об»явления типов… 54
Типы структур, совмещений и перечислений… 54
Об«явления typedef… 55
Имена типов… 56
КОНТРОЛЬНЫЕ ВОПРОСЫ:… 57
Функции… 57
Объявление и определение функций… 58
ТИПОВЫЕ ВОПРОСЫ С ОТВЕТАМИ… 62
ПРАКТИКУМ… 62
Контрольные вопросы… 62
Массивы… 65
СОРТИРОВКА массива — ПРИМЕР в файле list6_4cpp… 67
- ПОИСК в массиве… 67
БИБЛИОТЕЧНЫЕ ФУНКЦИИ ПОИСКА и СОРТИРОВКИ в непрерывныхмассивах: 68
Строкии управление вводом/выводом… 69
Форматированныйпотоковый вывод… 70
Листинг1. Исходный текст программы OUT1.CPP… 70
Функцияprintf… 71
Функцияprintf… 72
Таблица1. Еsс — последовательности… 72
Таблица7.2. Значения флагов строки формата функции printf… 72
Таблица3. Символы типов данных строки формата функции printf… 73
Листинг3. Исходный текст программы OUT2.CPP в файле List7-3.CPP… 74
Таблица4. Результат действия спецификаций форматирования в функции printf из строки 13 75
Вводстрок… 76
Функцияgetline… 76
Присвоениезначений строкам… 77
Инициализациястроки… 77
Функцияstrcpy… 77
Функцияstrdup… 77
Функцияstrncpy… 78
Определениедлины строки… 78
Функцияstrlen… 78
Функцияstrcat… 78
Функцияstrncat… 79
Сравнениестрок… 79
Функцияstrcmp… 79
Пример… 80
Функцияstricmp… 80
Пример… 80
Функцияstrncmp… 80
Пример… 80
Пример… 81
(см.List7_5.cpp — Исходный текст программы STRING2.CPP)… 81
Преобразованиестрок… 81
Функцияstrlwr… 81
Пример… 81
Функцияstrupr… 81
Пример… 81
Обращениестрок… 82
Функцияstrrev… 82
Поисксимволов… 82
Функцияstrchr… 82
Функцияstrrchr… 82
Пример… 83
ФункцияStrspn… 83
Пример… 83
Функцияstrcspn… 83
Пример… 83
Функцияstrpbrk… 83
Пример… 84
Поискстрок… 84
Функцияstrstr… 84
Пример… 84
Функцияstrtok… 84
Пример… 84
Основыобъектно-ориентированного программирования СИНТАКСИС ОСНОВНЫХ КОНСТРУКЦИЙ… 85
Объявлениебазовых классов… 85
Конструкторы… 88
Деструкторы… 90
Объявлениеиерархии классов… 91
Виртуальныефункции… 92
Дружественныефункции… 95
Операциии дружественные операции… 96
Виртуальныефункции… 97
Правиловиртуальной функции… 99
Операциии дружественные операции… 101
ИСХОДНЫЕТЕКСТЫ ПРИМЕРОВ… 103
ВОПРОСЫИ ОТВЕТЫ… 103
Контрольныевопросы… 104
ФАЙЛОВЫЕОПЕРАЦИИ ВВОДА/ВЫВОДА… 105
Stream-библиотекаC++… 105
ОБЩИЕФУНКЦИИ ПОТОКОВОГО ВВОДА/ВЫВОДА… 106
Функция-компонентopen… 106
Функция-компонентclose… 107
ПОСЛЕДОВАТЕЛЬНЫЙТЕКСТОВЫЙ ПОТОК ВВОДА/ВЫВОДА… 107
Функция-элементgetline… 108
ПОСЛЕДОВАТЕЛЬНЫЙДВОИЧНЫЙ ФАЙЛОВЫЙ ВВОД/ВЫВОД… 109
Функция-элементwrite… 110
Функция-элементread… 110
Файловыйввод/вывод с прямым доступом… 113
Функция-элементseekg… 113
Заключение… 115
Вопросыи ответы… 115
Практикум… 116
Контрольныевопросы… 116
Упражнение… 1161. Переменные иоперации языка С++
Здесь представлены базовые компоненты программ на С++. В их число
входяттипы данных, переменные, константы и выражения.ИЗУЧАЕМЫЕ ПОНЯТИЯ
- Предопределенные типы данных в С++ включают в себя типы int, char,
float,double и void. В языке С++ гибкость типов данных увеличивается
благодаряприменению модификаторов типов. Эти модификаторы изменяют
точностьпредставления и диапазон значений переменных. Модификаторами
типаявляются signed, unsigned, short и long.
- Идентификаторы в С++ могут иметь длину до 32 символов и
должныначинаться с буквы или подчеркивания. Последующие символы
идентификаторамогут быть буквой, цифрой или подчеркиванием. Иден-
тификаторыС++ чувствительны к регистру. Ограничение на 32 символа
можетбыть, однако, изменено путем установки опций компилятора.
- Директива #include является специальной командой компилятора. Она
предписываеткомпилятору включить в программу содержимое опреде-
ленногофайла, как если бы вы сами ввели его в текущий исходный
файл.
- Объявление констант предусматривает использование директивы #define
дляобъявления констант, определенных при помощи макросов, или ис-
пользованиеключевого слова const для объявления формальных кон-
стант.Формальные константы требуют от вас определения их типа
(значениемпо умолчанию является int), имени и ассоциированного с
нимизначения.
- Объявление переменной требует, чтобы вы задали ее тип и имя, С++
даетвам возможность инициализировать переменную при ее объявлении.
Выможете объявить несколько переменных в одном операторе объявле-
ния.
- Арифметическими операциями являются +, -, *, / и % (деление по
модулю).
- Арифметические выражения различаются по сложности. Самое простое
выражениесодержит единственный элемент данных (литерал, константу
илипеременную). Сложные выражения включают набор операций, функ-
ции,литералы, константы и переменные.
- Операции инкремента и декремента используются в префиксной и пост-
фикснойформах. Язык С++ дает вам возможность применять эти опе-
рациик переменным, в которых хранятся символы, целые числа и даже
числас плавающей точкой.
- Арифметические операции присваивания дают вам возможность записы-
ватьболее короткие арифметические выражения, в которых первый опе-
рандявляется также переменной, принимающей результат вычислений.
- Оператор sizeof возвращает как для типов данных, так и для переменных
ихразмер в байтах.
- Механизм приведения типа дает вам возможность форсировать преобра-
зованиетипа выражения.
- Операции отношений и логические операции дают вам возможность стро-
итьлогические выражения.
- Булевы выражения объединяют операции отношений и логические опе-
рациидля формулирования нетривиальных условий. Эти выражения позволяют
программепринимать сложные решения.
- Условное выражение предлагает вам короткую форму для простого опе-
ратораif-else с двумя альтернативами.
- Операции манипулирования битами выполняют поразрядные операции
AND,OR, XOR и NOT. Кроме того, в С++ поддерживаются поразрядные
операциисдвига >.
- Операции манипулирования битами с присваиванием предлагают корот-
киеформы для простых операций манипулирования битами. Буквы и цифры
Множествосимволов Си включает большие и малые буквы из английского алфавита и 10десятичных арабских цифр:
-большиеанглийские буквы:
AB C D E F G H I J K L M N O P Q R T U V W X Y Z
-малыеанглийские буквы:
ab c d e f g h i j k l m n o p q r t u v w x y z
-десятичныецифры:
01 2 3 4 5 6 7 8 9
Буквыи цифры используются при формировании констант, иден-
тификаторови ключевых слов. Все эти конструкции описаны ниже. Компилятор Си рассматриваетодну и ту же малую и большую
буквыкак отличные символы. Если в данной записи использованы малые буквы, то заменамалой буквы „a“ на большую букву „A“ сделает отличнойданную запись от предшествующей. Пробельные символы
Пробел, табуляция, перевод строки, возврат каретки, новая страница, вертикальнаятабуляция и новая строка- это сиволы, называемые пробельными, поскольку ониимеют то же самое назначение, как и пробелы между словами и строками напечатной странице. Эти символы разделяют об»екты, определенныепользователем, такие, как константы и идентификаторы, от других об«ектовпрограммы.
СимволCONTROL-Z рассматривается как индикатор конца файла. Компилятор игнорирует любой текст, следующий за символом
CONTROL-Z.
КомпиляторСи игнорирует пробельные символы, если они не используются как разделителиили как компоненты константы-символа или строковых литералов. Это нужно иметьв виду, чтобы дополнительно использовать пробельные символы для повышения наглядности программы (например, для просмотра редактором текстов). Знаки пунктуации и специальные символы
Знаки пунктуации и специальные символы из множества символов Си используются дляразличных целей, от организации текста программы до определения заданий,которые будут выполнены компилятором или откомпилированной программой. Втаблице 2.1 перечислены эти символы.
-----------------------------------------------------------
Символ Наименование Символ Наименование
-----------------------------------------------------------
, Запятая ! Восклицатель-
ныйзнак
. Точка | Вертикальная
черта
; Точкас за- / Наклонная чер-
пятой тавправо
: Двоеточие \ Наклоннаячер-
тавлево
? Знаквопроса ~ Тильда
' Одиночнаяка _ Подчеркивание
вычка
( Леваякруглая # Знак номера
скобка
) Праваякруглая % Знак процента
скобка
{ Леваяфигурная & Амперсанд
скобка
} Праваяфигурная ^ Caret
скобка
скобка
> Праваяугловая = Знак равно
скобка
[ Левая квадратная + Знак плюс
скобка
] Праваяквадратная
скобка-----------------------------------------------------------
Табл. 2.1.Знаки пунктуации и специальные символы
Эти символы имеют специальный смысл для компилятора Си. Их использование в языке Сиописывается в дальнейшем содержании руководства. Знаки пунктуации измножества представимых символов, которые не представлены в данном списке, могутбыть использованы только в строковых литералах, константах-символах икомментариях. ESC- последовательности
ESC- последовательности- это специальные символьные комбинации, которыепредставляют пробельные символы и неграфические символы в строках исимвольных константах.
Их типичное использование связано со спецификацией таких действий, как возврат каретки и табуляция , а также для задания литеральных представлений символов, таких как символ двойная кавычка.ESC-последовательность состоит из наклонной черты влево, за которой следуетбуква, знаки пунктуации ' » \ или комбинация цифр. В таблице 2.2. приведенсписок ESC- последовательностей языка Си.
-------------------------------------------------
ESC-последовательность Наименование
-------------------------------------------------
\n Новаястрока
\t Горизонтальнаятабу-
ляция
\v Вертикальнаятабуля-
ция
\b Пробел
\r Возвраткаретки
\f Новаястраница
\a Звонок(сигнал)
\' Одиночнаякавычка
\" Двойнаякавычка
\\ Наклоннаячерта влево
\ddd ASCIIсимвол в восьми-
ричномпредставлении
\xdd ASCIIсимвол в шестнад-
цатиричномпредставлении
Табл. 2.2.ESC- последовательности
Еслинаклонная черта влево предшествует символу, не включенному в этот список,то наклонная черта влево игнорируется, а символ представляется как литеральный. Например, изображение \c
представляетсимвол «c» в литеральной строке или константе-символе.
Последовательности \ddd и \xdd позволяют задать любой символ в ASCII (Американский стандартныйкод информационного интерфейса) как последовательность трех восьмеричных цифрили двух шестнадцатеричных цифр. Например, символ пробела может быть заданкак \010 или \x08. Код ASCII «нуль» может быть задан как \0 или \x0. В восьмеричной ESC- последовательности могут быть использованы от одной дотрех восьмеричных цифр.
Например,символ пробела может быть задан как \10 . Точно так же в шестнадцатеричнойESC- последовательности могут быть использованы от одной до двухшестнадцатеричных цифр. Так, шестнадцатеричная последовательность для символа пробела может быть задана как \x08 или \x8 .
Замечание:
Когдаиспользуется восьмеричная или шестнадцатеричная ESCпоследовательность в строках, то нужно полностью задавать все цифры ESC- последовательности (трицифры для восьмеричной и две цифры для шестнадцатеричной ESC-последовательностей). Иначе, если символ непосредственно следующий за ESC-последовательностью, случайно окажется восьмеричной или шестнадцатеричной цифрой, то он проинтерпретируется как часть последовательности. Например,строка \x7Bell при выводе на печать будет выглядеть как {ell, поскольку \x7B проинтерпретируется как символ левой фигурной скобки({) . Строка \x07Bell будет правильным представлением сим-
вола«звонок» с последующим словом Bell.
ESC- последовательности позволяют посылать неграфические управляющие символы квнешним устройствам. Например, ESC- последовательность\033 часто используетсякак первый символ команд управления терминалом и принтером. Неграфические символы всегда должны представляться ESC-последовательностями, поскольку,непосредственное использование в программах на Си неграфических символовбудет иметь непредсказуемый результат.
Наклоннаячерта влево (\) помимо определения ESC-последовательностей используется также, как символ продолжения строки в препроцессорных определениях.
Еслисимвол «новая строка» следует за наклонной чертой влево, то новаястрока игнорируется и следующая строка рассматривается, как часть предыдущейстроки. Операции
Операции-это специальные комбинации символов, специфицирующие действия попробразованию различных величин. Компилятор интерпретирует каждую из этихкомбинаций как самостоятельную единицу, называемую лексемой (token).
ВТабл. 2.3 представлен список операций. Операции должны использоваться точнотак, как они представлены в таблице: без пробельных символов между символамив тех операциях, которые представлены несколькими символами.
Операцияsizeof не включена в эту таблицу. Она скорее представляет собой ключевоеслово, чем символ.
-------------------------------------------------
Операция Наименование -------------------------------------------------
! Логическое НЕ
~ Побитовое дополнение
+ Сложение
- Вычитание,арифмети-
ческоеотрицание
* Умножение
/ Деление
% Остаток
>> Сдвигвправо
> Больше
>= Большеили равно
== Равно
!= Неравно
& ПобитовоеИ, адрес от
| Побитовое включающее ИЛИ
^ Побитовоеисключающее ИЛИ
&& ЛогическоеИ
|| ЛогическоеИЛИ
' Последовательноевыполне-
ние(запятая)
?: Операцияусловного вы-
ражения
++ Инкремент
-- Декремент
= Простоеприсваивание
+= Сложениес присваиванием
-= Вычитаниес присваиванием
*= Умножениес присваиванием
/= Делениес присваиванием
%= Остатокс присваиванием
>>= Сдвигвправо с присваива-
иванием
нием
&= ПобитовоеИ с присваива-
нием
|= Побитовоевключающее ИЛИ
сприсваиванием
^= Побитовоеисключающее ИЛИ
сприсваиванием
-------------------------------------------------------
Табл.2.3. Операции
Замечание:
Операция условного выражения ?: -это тернарная, а не двухсимвольная операция. Формат условного выражения следующий:?: Константы
Константа-это число, символ или строка символов. Константы используются в программе как неизменяемые величины. В языке Си различают четыре типа констант: целыеконстанты, константы с плавающей точкой, константы-символы и строчныелитералы. Целые константы
Целаяконстанта- это десятичное, восьмеричное или шестнадцатеричное число, котороепредставляет целую величину. Десятичная константа имеет следующий формат представления:
,
где — это одна или более десятичных цифр от 0 до 9.
Восьмеричнаяконстанта имеет следующий формат представления:
0,
где — это одна или более восьмеричных цифр от 0 до 7. Записьведущего нуля необходима.
Шестнадцатеричнаяконстанта имеет один из следующих форматов представления:
0x
0X,
где одна или более шестнадцатеричных цифр. Шестнадцатеричная цифра может быть цифрой от 0 до 9 или
буквой(большой или малой) от A до F. В представлении константы допускается «смесь» больших и малых букв. Запись ведущего нуля и следующего заним символа x или X необходима.
Пробельныесимволы не допускаются между цифрами целой константы. В Табл. 2.4иллюстрируются примеры целых констант.
-----------------------------------------------------------
Десятичные Восьмеричные Шестнадцатеричные
константы константы константы
-----------------------------------------------------------
10 012 0xa или 0xA
132 0204 0x84
32179 076663 0x7dB3 или 0x7DB3
-----------------------------------------------------------
Табл.2.4 Примеры констант
Целыеконстанты всегда специфицируют положительные величины. Если требуетсяотрицательные величины, то необходимо сформировать константное выражение иззнака минус и следующей за ним
константы. Знак минус рассматривается как арифметическая операция.
Каждаяцелая константа специфицируется типом, определяющим ее представление в памятии область значений. Десятичные константы могут быть типа int или long.
Восьмеричныеи шестнадцатеричные константы в зависимости от размера могут быть типа int, unsigned int, long или unsigned long. Если константа может бытьпредставлена как int, она специфицируется типом int. Если ее величинабольше, чем максимальная положительная величина, которая может быть представлена типом int, но меньше величины, которая представляется в том жесамом числе бит как и int, она задается типом unsigned int. Наконец,константа, величина которой больше чем максимальная величина,представляемая типом unsigned int, задется типом long или unsignedlong, если это необходимо. В Табл. 2.5 показаны диапазо-
ны величин восьмеричных и шестнадцатеричных констант, представимыхсоответствующими типами на машине, где тип int имеет длину 16 бит.
-----------------------------------------------------------
Шестнадцатеричные Восьмеричные Тип
диапазоны диапазоны
-----------------------------------------------------------
0x0-0x7FFF 0-077777 int
0x8000-0xFFFF 0100000-0177777 unsigned int
0x10000-0x7FFFFFFF 0200000-017777777777 long
0x80000000-0xFFFFFFFF 020000000000-030000000000 unsigned long
-----------------------------------------------------------
Табл. 2.5 Диапазоны величин восьмеричных и
шестнадцатеричныхконстант
Важностьрассмотренных выше правил состоит в том, что восьмеричные и шестнадцатеричные константы не содержат «знаковых» расширений, когда онипреобразуются к более длинным типам (преобразование типов смотри в разделе 5«Выражения и присваивания»).
Программист может определить для любой целой константы тип
long,приписав букву «l» или «L» в конец константы. В Табл. 2.6показаны примеры целых констант.
------------------------------------------------------------
Десятичные Восьмеричные Шестнадцатеричные
константы константы константы
------------------------------------------------------------
10L 012L 0xaL или 0xAL
79l 0115l 0x4fl или 0x4Fl
------------------------------------------------------------
Табл.2.6 Примеры целых констант типа long Константы с плавающей точкой
Константас плавающей точкой- это действительное десятичное положительное число.Величина действительного числа включает целую, дробную части и зкспоненту. Константы с плавающей точкой имеют следующий формат представления:
[][.][E[-]],
где — одна или более десятичных цифр (от 0 до 9),
аE или e -символ экспоненты. Целая или дробная части константы могут бытьопушены, но не обе сразу. Десятичная точка может быть опущена только тогда,когда задана экспонента.
Экспонентасостоит из символа экспоненты, за которым следует целочисленная величинаэкспоненты, возможно отрицательная.
Пробельныесимволы не могут разделять цифры или символы
константы.
Константы с плавающей точкой всегда специфицируют положительные величины. Еслитребуются отрицательные величины, то необходимо сформировать константноевыражение из знака минус и следующей за ним константы. Знак минусрассматривается как арифметическая операция.
Примеры констант с плавающей точкой и константных выраже-
ний:
15.75
1.575E1
1575e-2
-0.0025
-2.5e-3
25e-4
Целаячасть константы с плавающей точкой может быть опущена, например:
.75
.0075e2
-.125
-.175E-2
Всеконстанты с плавающей точкой имеют тип double. Константа-символ
Константа-символ- это буква, цифра, знак пунктуации или ESC- символ, заключенные в одиночныекавычки. Величина константы-символа равна значению представляющего ее кодасимвола.
Константа-символимеет следующую форму представления:
'',
где может быть любым символом иэ множества представимых символов,включая любой ESC- символ, исключая одиночную кавычку ('), наклонную чертувлево (\) и символ новой строки.
Чтобыиспользовать одиночную кавычку или наклонную черту влево в качестве константы-символа, необходимо вставить перед этими знаками наклонную чертувлево. Чтобы представить символ новой строки, необходимо использовать запись'\n'.
----------------------------------------------
Константа Название величины
----------------------------------------------
'a' Малаябуква а
'?' Знаквопроса
'\b' Знакпробела
'0x1B' ASCIIESC- символ
'\'' Одиночнаякавычка
'\\' Наклоннаячерта влево
-------------------------------------------------
Табл.2.7 Примеры констант-символов.
Константы-символыимеют тип int. Строковые литералы
Строковыйлитерал- это последовательность букв, цифр и символов, заключенная в двойныекавычки. Строковый литерал рассматривается как массив символов, каждыйэлемент которого представляет отдельный символ. Строковый литерал имеетследующую форму представления:
"",
где — это нуль или более символов из множества представимыхсимволов, исключая двойную кавычку ("), наклонную черту влево (\) исимвол новой строки. Чтобы использовать символ новой строки в строковомлитерале, необходимо напечатать наклонную черту влево, а затем символ новойстроки.
Наклонная черта влево вместе с символом новой строки будут проигнорированы компилятором,что позволяет формировать строковые литералы, располагаемые более чем водной строке. Например, строковый литерал:
«Longstrings can be bro\
ckeninto two pieces.»
идентиченстроке:
«Longstrings can be brocken into two pieces.»
Чтобыиспользовать двойные кавычки или наклонную черту влево внутри строковоголитерала, нужно представить их с предшествующей наклонной чертой влево, какпоказано в следующем примере:
«Thisis a string literal»
«First\\ Second»
"\«Yes,I do,\» she said."
«Thefollowing line shows a null string:»
""
Заметим,что ESC- символы (такие как \\ и \") могут появляться в строковыхлитералах. Каждый ESC- символ считается одним отдельным символом.
Символыстроки запоминаются в отдельных байтах памяти. Символ null (\0) являетсяотметкой конца строки. Каждая строка в программе рассматривается какотдельный об«ект. Если в программе содержатся две идентичные строки, токаждая из них будет храниться в отдельном месте памяти.
Строчныелитералы имеют тип char[]. Под этим подразумевается, что строка- это массив, элементы которого имеют тип char. Число элементов в массиве равно числусимволов в строчном литерале плюс один, поскольку символ null (отметка конца строки) тоже считается элементом массива. Идентификаторы
Идентификаторы- это имена переменных, функций и меток, используемых в программе. Идентификаторсоздается об»явлением соот-
ветствующейему переменной или функции.После этого его можно использовать в последующихоператорах программы. Идентификатор- это последовательность из одной или более букв, цифр или подчерков(_), которая начинается с буквы или подчерка.Допускается любое число символов в идентификаторе, однако только первые 31символ распознаются компилятором. (Программы, использующие результат
работыкомпилятора, такие как, линкер, могут распознавать меньшее число символов).
Прииспользовании подчерков в идентификаторе нужно быть осторожным, посколькуидентификаторы, начинающиеся с подчерка могут совпадать (войти в конфликт) сименами «скрытых» системных программ.
Примерыидентификаторов:
temp1
toofpage
skip12
КомпиляторСи рассматривает буквы верхнего и нижнего регистров как различные символы.Поэтому можно создать отдельные независимые идентификаторы, которые совпадаюторфографически, но различаются большими и малыми буквами. Например, каждый изследующих идентификаторов является уникальным:
add
ADD
Add
aDD
КомпиляторСи не допускает идентификаторов, которые имеют ту же самую орфографию, что иключевые слова. Ключевые слова описаны в следующем раздела
Замечание:
Посравнению с компилятором, сборщик может в большей степени ограничиватьколичество и тип символов для глобальных идентификаторов, и в отличие откомпилятора не делать различия между большими и малыми буквами. (Подробнеесмотри руководство по пакету MSC). Ключевые слова
Ключевые слова- это предопределенные идентификаторы, которые имеют специальное значениедля компилятора Си. Их можно использовать только так как они определены.Имена об«ектов программы не могут совпадать с названиями ключевых слов.
Списокключевых слов:
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof while
do if static volatile
Ключевыеслова не могут быть переопределены. Тем не менее, они могут быть названыдругим текстом, но тогда перед компиляцией они должны быть заменены посредствомпрепроцессора на соответствующие ключевые слова.
Ключевые слова const и volatile зарезервированы для будущего использования.
Следующиеидентификаторы могут быть ключевыми словами для некоторых приложений:
cdecl
far
fortran
huge
near
pascal Комментарии
Комментарий-это последовательность символов, которая воспринимается компилятором какотдельный пробельный символ или, другими словами, игнорируется.
Комментарийимеет следующую форму представления:
/**/,
где может быть любой комбинацией символов из множествапредставимых символов, включая символы новой строки, но исключая комбинацию */.Это означает, что комментарии могут занимать более одной строки, но не могутбыть вложенными.
Комментариидопускаются везде, где разрешены пробельные символы. Компилятор игнорируетсимволы комментария, в частности, в комментариях допускается запись ключевыхслов и зто не приведет к ошибке. Так как компилятор рассматривает комментарийкак символ пробела, то комментарии не могут появляться внутри лексем.
Следующиепримеры иллюстрируют некоторые комментарии:
/*Comments can separate and document
linesof a program. */
/*Comments can contain keywords such as for
andwhile */
/*******************************************
Commentscan occupy several lines. *******************************************/
Таккак комментарии не могут содержать вложенных комментариев, то следующийпример будет ошибочным:
/*You cannot/* nest */ comments */
Компиляторраспознает первую комбинацию */ после слова nest как конец комментария. Затем,компилятор попытается обрабатывать оставшийся текст и выработает сообщение обошибке. Чтобы обойти
компиляцию комментариев больших размеров, нужно использовать директиву #if препроцессора. Лексемы
Когдакомпилятор обрабатывает программу, он разбивает программу на группы символов,называемых лексемами. Лексема- это единица текста программы, которая имеетопределенный смысл для компилятора и которая не может быть разбита вдальнейшем. Операции, константы, идентификаторы и ключевые слова, описанные вэтом разделе, являются примерами лексем. Знаки пунктуации, такие как квадратныескобки ([]), фигурные скобки ({}), угловые скобки (), круглые скобки и запятые, также являются лексемами. Границы лексем определяютсяпробельными символами и другими лексемами, такими как операции и знакипунктуации. Чтобы предупредить неправильную работу компилятора, запрещаютсяпробельные символы между символами идентификаторов, операциями, состоящими изнескольких символов и символами ключевых слов.
Когда компилятор выделяет отдельную лексему, он последовательно об»единяетстолько символов, сколько возможно, прежде чем перейти к обработке следующейлексемы. Поэтому лексемы, не разделенные пробельными символами, могут быть проинтерпретированы неверно.
Например,рассмотрим следующее выражение:
i+++j
В этом примере компилятор вначале создает из трех знаков плюс самую длинную извозможных операций (++), а затем обработает оставшийся знак +, как операциюсложения (+). Выражение проинтерпретируется как (i++)+(j), а не как(i)+(++j). В таких случаях необходимо использовать пробельные символы иликруглые скобки, чтобы однозначно определить ситуацию.ИСХОДНЫЕ ТЕКСТЫ ПРИМЕРОВ
//Программа VAR.CPP, иллюстрирующая простые переменные
#include
intmain()
{
int i, j = 2;
double x, y = 355.0 / 113;
i = 3 * j;
cout
x = 2 * y;
x = x * x;
cout
return 0;
}
/*
Результаты:
i = 6
j = 2
y = 3.141593
x = 39.4784
*/
//Программа CONST1.CPP, иллюстрирующая константы
#include
#defineSEC_IN_MIN 60
#defineMIN_IN_HOUR 60
intmain()
{
long hours, minutes, seconds;
long totalSec;
cout
cin >> hours;
cout
cin >> minutes;
cout
cin >> seconds;
totalSec = ((hours * MIN_IN_HOUR + minutes) *
SEC_IN_MIN) + seconds;
cout
return 0;
}
/* Тест и результаты:
Введите часы: 10
Введите минуты: 0
Введите секунды: 0
36000 секунд прошло сполуночи
*/
//Программа CONST2.CPP, иллюстрирующая формальные константы
#include
constint SEC_IN_MIN = 60; // глобальная константа
intmain()
{
const int MIN_IN_HOUR = 60; // локальная константа
long hours, minutes, seconds;
long totalSec;
cout
cin >> hours;
cout
cin >> minutes;
cout
cin >> seconds;
totalSec = ((hours * MIN_IN_HOUR + minutes) *
SEC_IN_MIN) + seconds;
cout
return 0;
}
/* Тест и результаты:
Введите часы: 1
Введите минуты: 10
Введите секунды: 20
4220 секунд прошло с полуночи
*/
//Программа OPER1.CPP, иллюстрирующая простые математические операции
#include
intmain()
{
int int1, int2;
long long1, long2, long3, long4, long5;
float x, y, real1, real2, real3, real4;
cout
cin >> int1;
cout
cin >> int2;
cout
long1 = int1 + int2;
long2 = int1 — int2;
long3 = int1 * int2;
long4 = int1 / int2;
long5 = int1 % int2;
cout
cout
cout
cout
cout
cout
cout
cin >> x;
cout
cin >> y;
cout
real1 = x + y;
real2 = x — y;
real3 = x * y;
real4 = x / y;
cout
cout
cout
cout
cout
return 0;
}
/* Тест и результаты:
Введите первое целое число: 10
Введите второе целое число: 5
10 + 5 = 15
10 — 5 = 5
10 * 5 = 50
10 / 5 = 2
10 % 5 = 0
Введите первое вещественное число: 1.25
Введите второе вещественное число: 2.58
1.25 + 2.58 = 3.83
1.25 — 2.58 = -1.33
1.25 * 2.58 = 3.225
1.25 / 2.58 = 0.484496
*/
//Демонстрацияопераций инкремента и декремента см. в программе OPER2.CPP
//Программа SIZEOF.CPP, которая возвращает размеры данных, используя
//для этого операцию sizeof() с переменными и типами данных.
#include
intmain()
{
short int aShort;
int anInt;
long aLong;
char aChar;
float aReal;
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
return 0;
}
/* Результаты:
Таблица 1. Размеры памяти для переменных"
Тип данных Используемая
память (в байтах)
------------------ -----------
short int 2
integer 2
long integer 4
character 1
float 4
Таблица 2. Размеры памяти для типов данных
Тип данных Используемая
память (в байтах)
------------------ -----------
short int 2
integer 2
long integer 4
character 1
float 4
*/
//Простая программа TYPECAST.CPP, демонстрирующая приведение типа
#include
intmain()
{
short shortInt1, shortInt2;
unsigned short aByte;
int anInt;
long aLong;
char aChar;
float aReal;
// присваиваются значения
shortInt1= 10;
shortInt2 = 6;
// действия выполняются без приведения типа
aByte = shortInt1 + shortInt2;
anInt = shortInt1 — shortInt2;
aLong = shortInt1 * shortInt2;
aChar = aLong + 5; // автоматическое преобразование
// в символьный тип
aReal = shortInt1 * shortInt2 + 0.5;
cout
// дейтсвия выполняются с приведением типа
aByte = (unsigned short) (shortInt1 + shortInt2);
anInt= (int) (shortInt1 — shortInt2);
aLong = (long) (shortInt1 * shortInt2);
aChar = (unsigned char) (aLong + 5);
aReal = (float) (shortInt1 * shortInt2 + 0.5);
cout
return 0;
}
/*Результаты:
shortInt1 = 10
shortInt2 = 6
aByte = 16
anInt = 4
aLong = 60
aCharis A
aReal= 60.5
shortInt1= 10
shortInt2= 6
aByte= 16
anInt= 4
aLong= 60
aCharis A
aReal= 60.5
*/
/* *** ВОПРОСЫ И ОТВЕТЫ ***
Существуютли особые соглашения о присвоении имен идентификаторам?
Существуетнесколько стилей, которые стали популярными в последние
годы.Стиль, который используется в наших занятиях, требует начинать
имяпеременной с символа, набранного в нижнем регистре. Если идентифи-
каторсостоит из нескольких слов, как, например, numberOfElements,
набирайтепервый символ каждого последующего слова в верхнем реги-
стре.
Какреагирует компилятор, если вы объявляете переменную, но никогда не
присваиваетеей значения?
Компиляторвыдает предупреждение, что на переменную нет ссылок.
Каковобулево выражение для проверки того, что значение переменной i
находитсяв заданном диапазоне значений (например, определяемом пере-
меннымиlowVal и hiVal)?
Выражением,которое определяет, находится ли значение переменной i
внекотором диапазоне, является (i >= lowVal && i 2. Конструкциипринятия решений и циклы ВОПРОСЫ И ОТВЕТЫ
Предъявляетли С++ какие-либо требования на отступ операторов в предложениях оператора?
Нет. Отступ определяется только Вами. Типовые размеры отступа составляют дваили четыре пробела. Использование отступов делает ваш листинг намного болееудобочитаемым.
Вот пример оператора if с записью предложений без отступа:
if ( i > 0 )
j = i * 1;
else
j = 10 — i;
Сравните этот листинг и его вариант с отступами
if ( i > 0 )
j = i * i;
else
j = 10 — i;
Последний вариант читается много легче; легко указать, где операторы if иelse. Более того, если вы будете работать с вложенными циклами, отступы ещеболее значимы в отношении удобочитаемости кода.
Каковыправила написания условий в операторе if-else?
Здесьсуществуют два подхода. Первый рекомендует писать условия так, что trueбудет чаще, чем false. Второй подход рекомендует избегать отрицательныхвыражений (тех, которые используют операции сравнения != и булевы операции !).
Программисты из последнего лагеря преобразуют такой оператор if:
if ( i != 0 )
j = 100/i;
else
j = 1;
в следующую эквивалентную форму:
if ( i == 0 )
j = 1;
else
j = 100/i;
хотя вероятность равенства нулю переменной i достаточно низка.
Какобработать условие, подобное нижеследующему, где имеется деление на переменную,которая может оказаться равной нулю?
if ( i != 0 && 1/i > 1 )
j = i * i;
С++ не всегда оценивает проверяемые условия полностью. Эта частичная оценкапроисходит, когда член булева выражения превращает все выражение в false илиtrue, независимо от значения других членов. В этом случае, если переменная iравна 0, исполняющая система не будет оценивать 1/i > 1, потому что член i!= 0 есть false и обращает в false все выражение, независимо от значениявторого члена. Это называется укороченной оценкой булевых выражений.
Действительноли необходимо включать предложения else или default в многоальтернативныеоператоры if-else и switch?
Программисты настоятельно рекомендуют включение этих всеохватывающихпредложений для гарантии того, что многоальтернативные операторы будутобрабатывать все возможные условия. Однако технически для компиляции программыэто не является необходимым.
Каксмоделировать цикл while циклом for?
Рассмотрим простой пример.
int i; int i = 1;
for (i=1; i
cout
} i += 2;
}
Циклу while необходим начальный оператор, инициирующий переменную управленияциклом. Заметим также, что внутри цикла while находится оператор, изменяющийзначение переменной управления циклом.
Каксмоделировать цикл while циклом do-while?
Рассмотрим простой пример.
i = 1; i = 1;
do { while (i
cout
i += 2; i += 2;
} while (i
Оба цикла имеют одинаковые условия в предложениях while.
Заметим, однако, что если цикл спроектирован таким образом, что начальноезначение i может быть неизвестным заранее, то это может привести к различнымэффектам. Например, если i исходно равно 11, то цикл слева выполнится один раз,тогда как цикл справа не сделает ни одной итерации.
Какоткрытый цикл for может эмулировать циклы while и do-while?
Открытый цикл for эмулирует другие циклы С++ установкой оператора if выхода изцикла в начале или конце цикла. Рассмотрим пример эмуляции цикла while открытымциклом for:
i = 1; i = 1;
while (i
if (i > 10) break;
cout
i += 2; i += 2;
} }
Заметим, что открытый цикл for использует оператор if выхода из цикла какпервый оператор внутри цикла. Условие, проверяемое оператором if, естьлогическое обращение условия цикла while.
Рассмотрим простой пример, иллюстрирующий эмуляцию цикла do-while:
i = 1; i = 1;
do { for (;;) {
cout
if (i > 10) break;
i += 2; i += 2;
} while (i
Открытый цикл for использует оператор if выхода из цикла перед концом цикла.Оператор if проверяет обратное логическое условие, так же как в цикле do-while.Однако имейте, пожалуйста, в виду, что приведенные примеры довольно грубы инеэлегантны. Никто никогда не будет использовать открытый оператор for подобнымобразом. Конечно, можно было бы пропустить одно из трех предложений внутрискобок цикла for (например, предложение инициализации, если управляющаяпеременная уже инициализирована). Открытые циклы for чаще всего используются вслучаях, когда выход из цикла бывает редким событием, например, если приобработке данных, вводимых пользователем с клавиатуры, нажатие клавиши Escдолжно приводить к выходу из программы.
Можноли во вложенном цикле for использовать переменную управления внешним циклом вкачестве границы диапазона значений для внутренних циклов?
Да. С++ не только не запрещает такое использование, на самом деле
оно в порядке вещей. Рассмотрим простой пример.
for ( int i = 1; i
for ( int j = i; i
cout
Ограничиваетли С++ вложение циклов разных типов?
Нет. В программе на С++ вы можете вкладывать любые комбинации
циклов.СТРУКТУРАПРОГРАММЫ
В этом разделе описывается структураисходной программы на Си и определяются термины, используемые в последующихразделах руководства при описании языка. По сути, здесь представлен общийобзор особенностей языка Си, которые в дальнейшем рассмотрены в деталях. Исходная программа
Исходная программа- это совокупность следующих об«ектов: директив, указаний компилятору, об»явлений иопределений. Директивы задают действия препроцессора по преобразованию текстапрограммы перед компиляцией. Указания компилятору- это команды, выполняемые компилятором во время процесса компиляции. Об«явления задают имена иатрибуты переменных, функций и типов, используемых в программе. Определения-это об»явления, определяющие переменные и функции.
Определение переменной в дополнении к ееимени и типу задает начальное значение об«явленной переменной. Крометого, определение предполагает распределение памяти для переменной.
Определение функции специфицирует ее структуру, которая
представляет собой смесь из об»явлений и операторов,которые образуют саму функцию. Определение функции также задает имя функции,ее формальные параметры и тип возвращаемой величины.
Исходная программа может содержать любое число директив,
указаний компилятору, об«явлений и определений.Любой из об»ектов программы имеет определенный синтаксис, описанный в этомруководстве, и каждая составляющая может появляться в любом порядке, хотявлияние порядка, в котором следуют переменные и функции может быть использовано в программе (см. раздел 3.5 «Время жизни и видимость»).
Нетривиальная программа всегда содержитболее одного определения функции. Функция определяет действия, выполняемыепрограммой.
В следующемпримере иллюстрируется простая исходная программа на языке Си.
int x = 1;/* Variable definitions */
int y = 2;
extern int printf(char *,...);/* Function declaration */
main () /* Function definition for main function */
{
int z; /* Variable declarations */
int w;
z = y + x; /* Executablestatements */
w = y — x;
printf(«z = %d \nw = %d \n», z, x);
}
Эта исходная программа определяет функцию с именем main и об«являет функцию printf.Переменные x и y задаются своими определениями. Переменные z и w толькооб»являются.ОБЪЯВЛЕНИЯ
В этом разделе описываются форматы исоставные части об«явлений переменных, функций и типов. Об»явленияСи имеют следующий синтаксис:
[][][=][,[=...],
где:
-спецификатор класса памяти; - имя определяемого типа;
- идентификатор,который может быть модифицирован при об«явлении указателя, массива илифункции;
- задает значение илипоследовательность значений, присваиваемых переменной при об»явлении.
Все переменные Си должны быть явнооб«явлены перед их использованием. Функции Си могут бытьоб»явлены явно или неявно в случае их вызова перед определением.
Язык Си определяет стандартное множество типов данных. К этому множеству можно добавлять новые типы данныхпосредством их
об«явлений на типах данных уже определенных.
Об»явление Си требует одного илиболее деклараторов. Декларатор- это идентификатор, который может бытьопределен с квадратными скобками ([]), эвездочкой (*) или круглыми скобками () для об«явления массива, указателя или функции. Когда об'являетсяпростая переменная (такая как символ, целое или плавающее), структура или совмещение простых переменных, то декларатор- это идентификатор.
В Си определено четыре спецификатора классапамяти, а именно: auto, extern, register и static.
Спецификатор класса памяти определяет,каким образом об»являемый об«ект запоминается и инициализируется ииз каких частей программы можно ссылаться на него. Расположениеоб»явления внутри программы, а также наличие или отсутствие другихоб«явлений- также важные факторы при определении видимости переменных.
Об»явленияфункций описаны в разделе 4.4. Спецификаторытипов
Язык Сиподдерживает определения для множества базовых типов данных, называемых«основными» типами. Названия этих типов перечислены в Табл. 4.1.
------------------------------------------------------------
Типы целых Типыплавающих Другие типы
------------------------------------------------------------
signed char float void
signed int double
signed short intsigned long int
unsigned char
unsigned int
unsignet short int unsigned long int
-----------------------------------------------------------
Табл. 4.1. Основные типы.
Перечислимые типы также рассматриваютсякак основные типы. Спецификаторы перечислимых типов рассмотрены в разделе4.7.1. Типы signed char, signed int, signed short int и signed long int
вместе с соответствующими двойниками unsignedназываются типами целых.
Спецификаторы типов float и doubleотносятся к типу «плавающих». В об«явлениях переменых и функцийможно использовать любые спецификаторы „целый“ и»плавающий".
Тип void может быть использован толькодля об«явления функций, которые не возвращают значения. Типы функций рассмотрены в разделе 4.4.
Можно задать дополнительные спецификаторытипа путем об»явления typedef, описанного в разделе 4.7.2.
При записи спецификаторов типовдопустимы сокращения как показано в табл. 4.2. В целых типах ключевое словоsigned может быть опущено. Так, если ключевое слово unsigned опускается в записи спецификатора типа, то тип целого будет знаковым, даже если опущено ключевоеслово signed.
В некоторыхреализациях могут быть использованы опции компилятора, позволяющие изменитьумолчание для типа char со знакового на беззнаковый. Когда задана такая опция, сокращение char имеет то же самое значение, что и unsigned char, иследовательно ключевое слово sidned должно быть записано при об«явлении символьной величины со знаком.
-----------------------------------------------------------
Спецификатор типа Сокращение
-----------------------------------------------------------
signed char char
signed int signed, int
signed short int short, signed short
signed long int long, signed long
unsigned char -
unsigned int unsigned
unsigned short int unsignetshort
unsignet long int unsignetlong
float -
long float double
------------------------------------------------------------
Табл. 4.2. Спецификаторы исокращения
Замечание: в этом руководстве в основномиспользуются сокращенные формы, перечисленные в Табл. 4.2, при этомпредполагается, что char по умолчанию знаковый.
В табл. 4.3 длякаждого типа приведены: размер распределяемой памяти и области значенийпеременных для данного типа. Поскольку тип void не представляетпеременных, он не включен в эту таблицу.
-----------------------------------------------------------
Тип Представление Область значений
в памяти величины
-----------------------------------------------------------
char 1байт -128 до 127
int зависит от
реализации
short 2байта -32768 до 32767
long 4байта -2.147.483.648 до 2.147.483.647
unsigned char 1 байт 0до 255
unsigned зависит от
реализации
unsigned short 2 байта 0 до 65535
unsigned long 4 байта 0до 4.294.967.295
float 4 байта IEEE стандартное
соглашение
double 8 байт IEEE стандартное
соглашение------------------------------------------------------------
Табл 4.3 Размер памяти иобласть значений типов
Тип charиспользуется для запоминания буквы, цифры или символа из множествапредставимых символов. Значением об»екта типа char является ASCII код,соответствующий данному символу. Так как тип char интерпретируется какоднобайтовая целая величина с областью значений от -128 до 127, то тольковеличины от 0 до 127
имеют символьные эквиваленты. Аналогично, тип unsignedchar может запоминать величины с областью значений от 0 до 255.
Заметим, что представление в памяти иобласть значений для типов int и unsigned int не определены в языке Си. По умолчанию размер int (со знаком и без знака) соответствует реальному размеру целого на данной машине. Например, на 16-ти разрядной машине тип int всегда 16разрядов или 2 байта. На 32-ух разрядной машине тип int всегда 32 разряда или 4байта. Таким образом, тип int эквивалентен типам short int или long int взависимости от реализации.
Аналогично, тип unsigned int эквивалентен типам unsigned short или unsigned long. Спецификаторы типовint и unsigned int широко используются в программах на Си, поскольку они позволяют наиболее эффективно манипулировать целыми величинами на данноймашине.
Однако, размертипов int и unsigned int переменный, поэтому программы, зависящие от спецификиразмера int и unsigned int могут быть непереносимы. Переносимость кодаможно улучшить путем включения выражений с sizeof операцией. Деклараторы
Синтаксис:
[]
[constant-expression>]
*
()
()
()
Сипозволяет об«являть: массивы величин, указатели на величины, величинывозвратов функций. Чтобы об»явить эти об«екты, нужно использоватьдекларатор, возможно модифицированный квадратными скобками ([]), круглымискобками () и звездочкой (*), что соответствует типам массива, функции или указателя. Деклараторы появляются в об»явлениях указателей, массивов ифункций.Деклараторы массивов, функцийи указателей
Когда деклараторсостоит из немодифицируемого идентификатора, то об'ект, которыйоб«является, имеет немодифицированный тип. Звездочка, которая можетпоявиться слева от идентификатора, модифицирует его в тип указателя. Еслиза идентификатором следуют квадратные скобки ([]), то тип модифицируется на типмассива. Если за идентификатором следуют круглые скобки, то тип модифицируется на тип функции. Сам по себе декларатор не образует полного об»явления.Для этого в об«явление должен быть включен спецификатор типа.Спецификатор типа задает тип элементов массива или тип адресуемыхоб»ектов и возвратов функции.
Следующие примеры иллюстрируют простейшие формы деклараторов:
1. int list[20]
2. char *cp
3. doublefunc(void),
где:
1. Массив listцелых величин
2. Указатель cpна величину типа char
3. Функция func без аргументов, возвращающая величину double Составные деклараторы
Любой декларатор может быть заключен в круглые скобки. Обычно, круглые скобкииспользуются для спецификации особенностей интерпретации составногодекларатора. Составной декларатор- это идентификатор, определяемый более чемодним модификатором массива, указателя или функции.
С отдельнымидентификатором могут появиться различные комбинации модификаторов массива,указателя или функции. Некоторые комбинации недопустимы. Например, массив неможет быть композицией функций, а функция не может возвратить массив илифункцию. При интерпретации составных деклараторов квадратные и круглые скобки(справа от идентификатора) имеют приоритет перед звездочкой (слева отидентификатора). Квадратные или круглые скобки имеют один и тот же приоритет и рассматриваются слева направо. Спецификатор типа рассматривается на последнемшаге, когда декларатор уже полностью проинтерпретирован. Можно использовать круглые скобки, чтобы изменить порядок интерпретации на необходимый в данномслучае.
Приинтерпретации составных деклараторов может быть предложено простое правило,которое читается следующим образом: «изнутри- наружу». Нужноначать с идентификатора и посмотреть вправо, есть ли квадратные или круглыескобки. Если они есть, то проинтерпретировать эту часть декларатора,затем посмотреть налево, если ли звездочка. Если на любой стадии справавстретится закрывающая круглая скобка, то вначале необходимо применить всеэти правила внутри круглых скобок, а затем продолжить интерпретацию. напоследнем шаге интерпретируется спецификатор типа. В следующем примере проиллюстрированы эти правила. Последовательность шагов при интерпретацииперенумерована.
char *(*(*var)()) [10];
^ ^ ^ ^ ^ ^ ^
7 6 4 2 1 3 5
1. Идентификаторvar об'явлен как
2. Указатель на
3. Функцию,возвращающую
4. Указатель на
5. Массив из 10элементов, который состоит 6. Из указателей на
7. Величины типаchar.
В следующихпримерах показывается каким образом круглые скобки могут поменять смыслоб«явлений.
1.int *var[5]; — массив указателей на величины типа int.
2.int (*var)[5]; — указатель на массив величин типа int.
3. long *var(long,long); — функция, возвращающая указатель на величину типа long.
4.long (*var) (long,long); — указатель на функцию, возвращающую величину типаlong.
5. struct both {
int a;
char b;
} ( *var[5] ) ( struct both,struct both); массив указателей на функции, возвращающих структуры.
6. double ( *var( double (*) [3] ) ) [3];
функция,возвращающая указатель на массив из трех величин типа double.
7. union sign {
int x;
unsigned y;
} **var[5][5];
массив массивов указателей науказатели совмещений.
8. union sign *(*var[5]) [5];
массив указателей на массивуказателей на совмещения.
Описание примеров:
В первом примере, модификатор массиваимеет высший приоритет, чем модификатор указателя, так что varоб»является массивом. Модификатор указателя определяет тип элементовмассива; элементами являются указатели на величины типа int.
Во втором примерескобки меняют значение об«явления первого примера. Теперь модификаторуказателя имеет более высокий приоритет, чем модификатор массива, ипеременная var об»является как указатель на массив из пяти величин типаint.
В третьем примере модификатор функцииимеет более высокий приоритет, чем модификатор указателя, так что переменная var об«является функцией, возвращающей указатель навеличину типа long. Функция об»явлена с двумя аргументами типа long.
Четвертый пример похож на второй. Скобкизадают более высокий приоритет модификатору указателя, и поэтому переменная var об«является как указатель на функцию, возвращающую величину типаlong. По прежнему функция об»явлена с двумя аргументами типа long.
Элементы массива не могут быть функциями. Взамен этому в пятом примере показано, какоб«явить массив указателей на функции. В этом примере переменная varоб»явлена как массив из пяти указателей на функции, возвращающие структурыс двумя элементами. Оба аргумента функции об«явлены как структуры типаboth. Заметим, что круглые скобки, в которые заключено выражение *var[5], обязательны.Без них об»явление будет неверным, поскольку будет об«явлен массивфункций:
/*ILLEGAL */
struct both *var[5] ( structboth, struct both );
В шестом примере показано, какоб»являть функцию, возвращающую указатель на массив. Здесь varоб«явлена функцией, возвращающей указатель на массив из трех величин типаdouble. Тип аргумента функции задан составным абстрактным декларатором.Круглые скобки, заключающие звездочку, требуются, так как в противномслучае типом аргумента был бы массив из трех указателей на величины типаdouble.
В седьмом примере показано, чтоуказатель может указывать на другой указатель и массив может состоять измассивов. Здесь var- это массив из пяти элементов. Каждый элемент, в свою очередь, так же массив из пяти элементов, каждый из которых являетсяуказателем на указатель совмещения, состоящего из двух элементов.
В восьмом примере показано, как круглые скобки изменили
смысл об»явления. В этом примере var- это массив изпяти указателей на массив из пяти указателей на совмещения. Об«явления переменной
В этом разделедано описание синтаксиса и семантики об»явлений переменной. В частности,здесь об«ясняется каким образом об»явить следующие переменные:
Тип переменной Описание
Простая переменная Переменнаяцелого или плаваю-
щеготипа.
Переменная перечис- Простаяпеременная целого типа
ления. котораяпринимает значения из
предопределенногонабора зна-
ченийпоименованных констант. Структура Переменная, которой соответс-
твует композиция отдельных переменных, типы которыхмогут отличаться.
Совмещение Переменная,которой соответс-
твует композиция отдельных переменных,занимающих одно и то же пространство памяти. Типы переменных композиции могутотличаться.
Массив Переменная,представляющая на-
борэлементов одного типа.
Указатель Переменная,которая указывает
на другую переменную (содержит местоположение другойпеременной в форме адреса).
Общий синтаксис об«явленийпеременных следующий:
[] [,...],
где — задает тип данных,представляемых переменной, а — это имя переменной, возможномодифицированное для об»явления массива или указателя. В об«явлении может быть задана более чем одна переменная путем задания множественного об»явления, в котором деклараторы разделены запятыми. задает класс памяти переменной. В некоторых случаях переменные могут быть инициализированы при их определении. Классы памяти иинициализация описаны в разделах 4.6 и 4.7 соответственно. Объявление простойпеременной
Синтаксис:
[,...];
Об«явление простой переменной определяет имя переменной и ее тип; оно может такжеопределять класс памяти переменной, как это описано в разделе 4.6. Имяпеременной- это идентификатор, заданный в об»явлении. Спецификатортипа задает имя определяемого типа данных.
Можно определитьимена различных переменных в том же самом об«явлении, задавая списокидентификаторов, разделенных запятой. Каждый идентификатор списка именуетпеременную. Все переменные, заданные в об»явлении, имеют один и тот жетип.
Примеры
intx; /* Example 1 */
unsigned longreply, flag /* Example 2 */ double order; /*Example 3 */
В первом примереоб«является простая переменная x. Эта пе-
ременная может принимать любоезначение из множества значений, определяемых для типа int.
Во втором примере об»явлены две переменные: reply и flag. Обе переменные имеют типunsigned long.
Втретьем примере об«явлена переменная order, которая имеет тип double. Этойпеременной могут быть присвоены величины с плавающей запятой. Объявлениеперечисления
Синтаксис:enum[]{}[,...];enum[,...];
Об»явление перечисления задает имя переменной перечисления и определяет список именованныхконстант, называемый списком перечисления. Значением каждого имени спискаявляется целое число. Переменная перечисления принимает значение одной из именованных констант списка. Именованные константы списка имеют тип int. Та-
ким образом, памятьсоответствующая переменной перечисления- это память, необходимая дляразмещения отдельной целой величины.
Объявлениеперечисления начинается с ключевого слова enum и
имеет две формы представления. Впервой форме представления имена перечисления задаются в списке перечисления.
Опция - это идентификатор, который именует тип перечисления,определенного в .
Переменнуюперечисления именует . В об«явлении может быть описанаболее чем одна переменная перечисления.
Во второй формеиспользуется тег перечисления, который ссы-
лается на тип перечисления. В этой формеоб»явления список перечисления не представлен, поскольку типперечисления определен в другом месте. Если задаваемый тег не ссылается науже определенный тип перечисления, или если именуемый тегом тип находится внетекущей видимости, то выдается ошибка.
имеет следующийсинтаксис:
[=][,
[=
.
.
.
Каждый идентификатор именует элементы перечисления. По умолчанию первому идентификатору соответствует значение 0,следующий идентификатор ассоциируется со значением 1 и т. д. Имя константыперечисления эквивалентно ее значению.
Запись =переопределяет последовательность значений, заданных по умолчанию. Идентификатор, следующий перед записью = принимаетзначение, задаваемое этим константным выражением. Константное выражение имееттип int и может быть отрицательным. Следующий идентификатор в списке ассоциируется с величиной, равной +1, если он явно не задаетсядругой величиной.
Перечислениеможет содержать повторяющиеся значения идентификаторов, но каждыйидентификатор должен быть уникальным. Кроме того, он должен быть отличным от всехдругих идентификаторов перечислений с той же видимостью. Например, двумразличным идентификаторам null и zero может быть задано значение 0 в одном итом же перечислении. Идентификаторы должны быть отличны от другихидентификаторов с той же самой видимостью, включая имена обычных переменных иидентификаторы других перечислений. Теги перечислений должны быть отличныот тегов перечислений, тегов структур и совмещений с той же самой видимостью.
Примеры:
/****************Example 1 ***************/
enum day {
saturday,
sunday = 0,
monday,
tuesday,
wednesday,
thursday,
friday
}workday;
/*****************Example 2 ***************/
enum day today= wednesday;
В первом примере определяется типперечисления, поименованный day и об«является переменная workday этоготипа перечисления. С saturday по умолчанию ассоциируется значение 0. Идентификатор sunday явно устанавливается в 0. Оставшиеся идентификаторы поумолчанию принимают значение от 1 до 5.
Во втором примере переменной today типаenum day присваивается значение из перечисления. Заметим, что дляприсваивания используется имя константы из перечисления. Так как типперечисления day был предварительно об»явлен, то достаточно сослаться только на тег перечисления. Объявления структур
Синтаксис:struct[]{}[,...];struct[,...];
Об«явлениеструктуры задает имя типа структуры и специфицирует последовательностьпеременных величин, называемых элементами структуры, которые могут иметьразличные типы.
Об»явление структуры начинается с ключевого слова struct и имеет две формы представления,как показано выше. В первой форме представления типы и имена элементовструктуры специфицируются в списке об«явлений элементов. - это идентификатор, который именует тип структуры, определенный в списке об»явлений элементов.
Каждый задает имя переменной типа структуры. Тип переменной в декларатореможет быть модифицирован на указатель к структуре, на массив структур или нафункцию, возвращающую структуру.
Втораясинтаксическая форма использует тег- структуры для ссылки на типструктуры. В этой форме об«явления отсутствует список об»явленийэлементов, поскольку тип структуры определен в другом месте. Определение типаструктуры должно быть видимым для тега, который используется в об«явлении и определение должно предшествовать об»явлению черезтег, если тег не используется для об«явления указателя или структурноготипа typedef. В последних случаях об»явления могут использовать тегструктуры без предварительного определения типа структуры, но все жеопределение должно находиться в пределах видимости об«явления.
Список об»явленийэлементов - это одно или более об«явлений переменных или битовых полей. Каждая
переменная, об»явленная в этом списке, называется элементом структурного типа.Об«явления переменных списка имеют тот же самый синтаксис, что иоб»явления переменных обсуждаемых в этой главе, за исключением того, что об«явления не могут содержать спецификаторов класса памяти илиинициализаторов. Элементы структуры могут быть любого типа: основного, массивом, указателем, совмещением или структурой.
Элемент не может иметь тип структуры, в которой он появляется. Однако, элемент можетбыть об»явлен, как указатель на тип структуры, в которую он входит, позволяя создавать списочные структуры. Битовые поля
Об«явления битовых полейимеют следующий синтаксис:
[]:;Битовое поле состоит из некоторого числа бит, специфициро-
ванных константным выражением-. Для бито-
вого поля спецификатор типа должен специфициро-
вать беззнаковый целый тип, аконстантное выражение должно быть неотрицательной целой величиной. Массивыбитовых полей, указатели на битовые поля и функции, возвращающие битовые поляне допускаются. Идентификатор- именует битовое поле.Неименованное битовое поле, чей размер специфицируется как нулевой, имеетспециальное назначение: оно гарантирует, что память для следующей переменнойоб»явления будет начинаться на границе int.
Идентификаторыэлементов внутри об«являемой структуры должны быть уникальными. Идентификаторы элементов внутри разных структур могут совпадать. В пределахтой же самой видимости теги структур должны отличаться от других тегов (тегов других структур, совмещений и перечислений).
Переменные (элементы) структурызапоминаются последовательно в том же самом порядке, в котором ониоб»являются: первой переменной соответствует самый младший адрес памяти, а последнейсамый старший. Память каждой переменной начинается на границесвойственной ее типу. Поэтому могут появляться неименованные участки междусоседними элементами.
Битовые поля нерасполагаются на пересечении границ, обявленных для них типов. Например,битовое поле, об«явленое с типом unsigned int, упаковывается или впространстве, оставшимся от предидущего unsigned int или начиная с новогоunsigned int.
Примеры
/**************** Example 1****************/
struct {
float x,y;
}complex;
/**************** Example 2*****************/
struct employee {
charname[20];
int id;
longclass;
}temp;
/****************Example 3 ******************/
struct employee student,faculty, staff;
/****************Example 4 ******************/ struct sample {
charc;
float*pf;
structsample *next;
}x;
/*****************Example 5 ******************/
struct {
unsignedicon: 8;
unsigned color:4; unsigned underline: 1; unsigned blink: 1;
}screen[25][80];
В первом примереоб»является переменная с именем complex типа структура. Эта структурасостоит из двух элементов x и y типа float. Тип структуры не поименован.
Во втором примереоб«является переменная с именем temp типа структура. Структура состоит изтрех элементов с именами name, id и class. Элемент с именем name- это массивиэ 20- ти элементов типа char. элементы с именами id и class- это простые переменные типа int и long соответственно. Идентификатор employee являетсятегом структуры.
В третьем примереоб»явлены три переменных типа структура с именами: student, faculty иstaff. Каждая из структур состоит из трех элементов одной и той жеконструкции. Элементы определены при об«явлении типа структуры с тегомemployee в предыдущем примере.
В четвертомпримере об»является переменная с именем x типа структура. Первые дваэлемента структуры представлены переменной c типа char и указателем pf навеличину типа float. Третий элемент с именем next об«являются как указатель на описываемую структуру sample.
В пятом примереоб»является двумерный массив поименованный screen, элементы которого имеютструктурный тип. Массив состоит из 2000 элементов и каждый элементэтоотдельная структура, состоящая из четырех элементов типа bit-fild с именамиicon, color, underline и blink. Об«явлениесовмещений
Синтаксис:union[]{}[,...];union[,...];
Об»явлениесовмещения определяет имя переменной совмещения и специфицирует множествопеременных, называемых элементами совмещения, которые могут быть различныхтипов. Переменная с типом совмещения запоминает любую отдельную величину,определяемую набором элементов совмещения.
Об«явлениесовмещения имеет тот же самый синтаксис, как и об»явление структуры, за исключением того, что она начинается с ключевого слова union вместоключевого слова struct. Для об«явления совмещения и структуры действуютодни и те же правила, за исключением того, что в совмещении не допускаютсяэлементы типа битовых полей.
Память,которая соответствует переменной типа совмещение, определяется величиной дляразмещения любого отдельного элемента совмещения.
Когдаиспользуется наименьший элемент совмещения, то переменная типа совмещенияможет содержать неиспользованное пространство. Все элементы совмещениязапоминаются в одном и том же пространстве памяти переменной, начиная содного и того же адреса. Запомненные значения затираются каждый раз, когда присваивается значение очередного элемента совмещения.
Примеры:
/**************Example 1 ********************/
union sign {
int svar;
unsigneduvar;
}number;
/************** Example 2********************/
union {
char *a,b;
floatf[20];
}jack;
/***************Example 2 *******************/
union {
struct {
charicon;
unsignedcolor: 4;
} window1, window2, window3, window4;
} screen[25][80];
В первом примере об»являетсяпеременная типа совмещения, поименованная number. Список элементов совмещениясостоит из двух
об«явлений переменных: svar типа int и uvar типа unsigned. Это об»явление позволяет запоминать текущее значение number взнаковом или беззнаковом виде. Тип совмещения поименован идентификаторомsign.
Во втором примере об«являетсяпеременная типа совмещения с именем jack. Список элементов об»явлениясостоит из трех об«явлений: указателя a на величину типа char,переменной b типа char и массива f из 20 элементов типа float. Тип совмещенияне поименован.
Память, распределенная для переменнойjack, равна памяти, распределенной под массив f, поскольку f самый большой элемент совмещения.
В третьем примереоб»является двумерный массив совмещений с именем screen. Массив состоитиз 2000 об«ектов. Каждый об»ектэто отдельное совмещение из четырехэлементов: window1, window2, window3, window4, где каждый элемент- этоструктура. В любое заданное время каждый об«ект совмещения поддерживаетсяодним из четырех возможных элементов типа структура. Таким образом, переменнаяscreen- это композиция четырех возможных „windows“. Об»явление массива
Синтаксис:[];[];
Здесь квадратныескобки- это терминальные символы. Об«явление массива определяет типмассива и тип каждого элемента. Оно может определять также число элементов вмассиве. Переменная типа массив рассматривается как указатель на элементымассива. Об»явление массива может представляться в двух синтаксических формах, указанных выше. Декларатор задает имя переменной.Квадратные скобки, следующие за декларатором, модифицируют декларатор на тип массива. Константное выражение
,заключенное в квадратные скобки, определяет число элементов в массиве. Каждыйэлемент имеет тип, задаваемый спецификатором типа ,который может специфицировать любой тип, исключая void и тип функции.
Во второйсинтаксической форме опущено константное выражение в квадратных скобках. Этаформа может быть использована только тогда, когда массив инициализируется илиоб«явлен как формальный параметр или об»явлен как ссылка на массив,явно определенный где-то в программе.
Массивмассивов или многомерный массив определяется путем задания списка константныхвыражений в квадратных скобках, следущего за декларатором:
[]
[]...
Каждое константное выражение- в квадратных скобках определяет число элементов в даннном иэмерении
массива, так что об«явление двумерного массивасодержит два константных выражения, трехмерного- три и т.д. Если многомерныймассив об»является внутри функции или если он инициализируется либооб«является как формальный параметр или об»является как ссылка на
массив, явно определенный где- то в программе, то первоеконстантное выражение может быть опущено.
Массив указателей на величины, заданного типа, может быть определен посредством составногодекларатора, как было описано в разделе 4.3.2.
Типу массив соответствует память, которая требуется для размещения всех его элементов.Элементы массива с первого до последнего запоминаются в последовательныхвозрастающих адресах памяти. Между элементами массива в памяти разрывыотсутствуют. Элементы массива запоминаются друг за другом построчно.Например, массив, содержащий две строки с тремя столбцами каждая,
char A[2][3]
будет запомненследующим образом. Сначала запоминаются три столбца первой строки, затемэлементы трех столбцов второй строки. Смысл этого в том, чтобы последнийиндекс был более быстрым. Чтобы сослаться на отдельный элемент массива, нужноиспользовать индексное выражение, которое описано в разделе 5.2.5.
Примеры:
/*************** Example 1******************/
int scores[10],game;
/*************** Example 2******************/
floatmatrix[10][15];
/*************** Example 3******************/
struct {
floatx,y;
}complex[100];
/*************** Example 4*******************/
char *name[20];
В первом примереоб«является переменная типа массив с именем scores из 10 элементовтипа int. Переменная с именем game об»явлена как простая переменная целоготипа.
Во втором примереоб«является двумерный массив с именем matrix. Массив состоит из 150-тиэлементов типа float.
В третьемпримере об»является массив структур. Массив состоит из 100 об«ектов.Каждый об»ект массива представляет собой структуру, состоящую из двухэлементов.
В четвертомпримере об«явлен массив указателей. Массив состоит из 20-ти элементов,каждый из которых является указателем на величину типа char.
4.4.6.Об»явление указателей
Синтаксис:
*;
Об«явление указателя определяет имя переменной типа указатель и тип об»екта, накоторый указывает эта переменная. Декларатор- определяетимя переменной с возможной модификацией ее типа. Спецификатор типа- задает тип об«екта, который может быть базового типа, типа структуры или совмещения.
Переменная типауказатель может указывать также на функции, массивы и другие указатели. Болееполная информация о типах указателей дана в разделе 4.3.2. „Составныедеклараторы“.
Если указатель неиспользуется до определения типа структуры или совмещения, то он может бытьоб»явлен ранее этого определения. Такие об«явления допускаются, поскольку компилятору не требуется знать размера структуры или совмещения,чтобы распределить память под переменную типа указатель. Указатель можетбыть об»явлен посредством использования тега структуры или совмещения(смотри ниже пример 4).
Переменная,об«явленная как указатель, хранит адрес памяти. Размер памяти, требуемый для адреса, и смысл адреса зависит от данной конфигурации машины.Указатели на различные типы не обязательно имеют одну и ту же длину.
Для некоторыхреализаций используются специальные ключевые слова near, far и huge, чтобымодифицировать размер указателя. Об»явления, использующие специальныеключевые слова, были описаны в разделе 4.3.3. Информация о смыслеключевых слов дана в системной документации.
Примеры:
char*message; /* Example 1 */
int*pointers[10]; /* Example 2 */ int(*pointer)[10]; /* Example 3 */ struct list *next,*previous; /* Example 4 */
struct list{ /* Example 5 */ char *token;
int count;
struct list *next;
}line;
struct id{ /* Example 6 */ unsigned int id_no;
struct name *pname;
}record;
В первом примере об«являетсяпеременная- указатель поименованная message. Она указывает на величину типаchar.
Во втором примере об»явлен массивуказателей, поименованный pointers. Массив состоит из 10 элементов. Каждый элемент- это указатель на переменную типа int.
В третьем примере об«явленапеременная- указатель, поименованная pointer. Она указывает на массив из 10элементов. Каждый элемент в этом массиве имеет тип int.
В четвертом примере об»явлены двепеременныхуказателя, которые ссылаются на величины структурного типа list(смотри следующий пример). Определение типа с именем list должно находиться впределах видимости об«явления.
В пятом примере об»является переменная с именем line, структурного типа, поименованного list. Тип структурыс именем list определяется тремя элементами. Первый элементэто указатель на величину типа char, второй- на величину типа int, а третийэто указатель наследующую структуру типа list.
В шестом примереоб«является переменная с именем record, имеющая тип структуры с именемid. Заметим, что третий элемент с именем pname об»явлен как указатель на другой тип структуры с именем name. Это об«явление может появитьсяперед об»явление структуры с именем name. Об«явлениефункций
Синтаксис:
[]([])[,...];
Об»явлениефункции определяет имя, тип возврата функции и, возможно, типы и число ееаргументов. Об«явление функции также называется forward- об»явлением.Декларатор функции об«являет имя функции, а спецификатор типа задает типвозврата. Если спецификатор типа опущен в об»явлении функции, топредполагается, что функция возвращает величину типа int.
Об«явление функции может включатьспецификаторы класса памяти extern или static.
Списоктипов аргументов.
Список типов аргументов- определяет число и типы аргументов функции. Синтаксиссписка аргументов следующий:
[,...]
Список иментипов- это список из одного или более имен типов. Каждое имя типа отделяетсяот другого запятой. Первое имя типа задает тип первого аргумента, второеимя типа задает тип второго аргумента и т. д. Если список имен типов заканчивается запятой с многоточием (,...), то это означает, что число аргументов функции переменно. Однако, предполагается, что функция будет иметь не меньшеаргументов, чем имен типов, предшествующих многоточию.
Если список типов аргументов- содержит
только многоточие (...), то число аргументов функцииявляется пе-
ременным или равно нулю.
Замечание:
Чтобы поддержать совместимость с программами предидущих версий, компилятор допускает символ запятой безмноготочия в конце списка типов аргументов для обозначения их переменного числа. Запятая может быть использована и вместо многоточия для об»явлениянуля или более аргументов функции. Использование запятой поддерживается толькодля совместимости. Использование многоточия рекомендуется для новогопредставления.
Имя типа- для типов структуры, совмещения или базового типа состоит изспецификатора этого типа (такого как int ). Имена типов для указателей,массивов и функций формируются путем комбинации спецификатора типа с «абстрактным декларатором». Абстрактный декларатор- это деклараторбез идентификатора. В разделе 4.9 «Имена типов» об«ясняется,каким об»разом формировать и интерпретировать абстрактные деклараторы.
Для того чтобыоб«явить функцию, не имеющую аргументов, может быть использованоспециальное ключевое слово void на месте списка типов аргументов.Компилятор вырабатывает предупреждающее сообщение, если в вызове такой функциибудут специфицированы аргументы.
Еще одна специальная конструкциядопускается в списке типов аргументов. Это фраза void *, которая специфицируетаргумент типа указатель. Эта фраза может быть использована в списке типов аргументоввместо имени типа.
Список типов аргументов может быть опущен. В зтом случае скобки после идентификаторафункции все же требуются, хотя они и пусты. В этом случае в об»явлениифункции не определяются ни типы, ни число аргументов в функции. Когда этаинформация опускает-
ся, то компилятор не проверяет соответствия между формальными и фактическими параметрами при вызове функции. Более подробная информациядана в разделе 7.4 «Вызовы функций».
Тип возврата
Функции могут возвращать величины любоготипа за исключением массивов и функций. Для этого посредством спецификатора типа«type-specifier» в об«явлении функции можно специфицироватьлюбой тип: основной, структуру или совмещение. Идентификатор функции можетбыть модифицирован одной или несколькими звездочками (*), чтобы об»явитьвозвращаемую величину типа указателя.
Хотя функции и недопускают возвратов массивов или функций, но они могут возвращать указателина массивы или функции. Функции, которые возвращают указатели на величины типа массив или функция, об«являются посредством модификацииидентификатора функции квадратными скобками, звездочкой и круглыми скобками,чтобы сформировать составной декларатор. Формирование и интерпретациясоставных деклараторов рассматривались в разделе 4.3.2.
Примеры:
int add(int,int); /* Example 1*/
double calc(); /*Example 2 */
char*strfind(char *,...); /*Example 3 */
void draf(void); /*Example 4 */
double(*sum(double, double)) [3]; /* Example 5 */ int (*select(void)) (int); /*Example 6 */
char *p; /*Example 7 */
short *q;
intprt(void *);
В первом примере об»является функция, поименованная add, которая требуетдва аргумента типа int и возвращает величину типа int.
Во второмпримере об«является функция, поименованная calc, которая возвращаетвеличину типа double. Список типов аргументов не задан. В третьем примереоб»является функция, поименованная strfind, которая возвращает указатель навеличину типа char. Функция требует, по крайней мере один аргументуказательна величину типа char. Список типов аргументов заканчивается запятой с многоточием,обозначающим, что функция может потребовать большее число аргументов.
В четвертом примере об«является функция с типом возврата void (нет возвращаемойвеличины). Список типов аргументов также void, означающий отсутствиеаргументов для этой функции.
В пятом примереsum об»является как функция, возвращающая указатель на массив из трех величинтипа double. Функция sum требует два аргумента, каждый из которых является величиной типа double.
В шестом примере функция, поименованная select, об«явлена без аргументов ивозвращает указатель на функцию. Указатель возврата ссылается на функцию,требующую один аргумент типа int и возвращающую величину типа int.
В седьмом примере об»явлена функция prt, которая требует аргумент- указатель любоготипа, и которая возвращает величину типа int. Любой указатель p или q моглибы быть использованы как аргументы функции без выдачи при этом предупреждающегосообщения. Классы памяти
Класс памяти переменной, которая определяет какой либо об«ект, имеет глобальное илилокальное время жизни. Об»ект с глобальным временем жизни существует иимеет значение на протяжении всей программы. Все функции имеют глобальное времяжизни.
Переменные слокальным временем жизни захватывают новую па-
мять при каждомвыполнении блока, в котором они определены. Когда управление на выполнениепередается из блока, то переменная теря-
ет свое значение.
ХотяСи определяет два типа классов памяти, но, тем не менее, имеется следующихчетыре спецификатора классов памяти:
auto
register
static
extern
Об«ектыклассов auto и register имеют локальное время жизни. Спецификаторы static иextern определяют об»екты с глобальным временем жизни. Каждый изспецификаторов класса памяти имеет определенный смысл, который влияет навидимость функций и переменных в той же мере, как и сами классы памяти.Термин «видимость» относится к той части программы, в которой могут ссылаться друг на друга функции и переменные. Об«екты с глобальнымвременем жизни существуют на протяжении выполнения исходной программы, но онимогут быть видимы не во всех частях программы. Видимость и связанная с нейконцепция времени жизни рассмотрена в разделе 3.5.
Месторасположениеоб»явления переменной или функции внутри
исходных файлов также влияютна класс памяти и видимость. Говорят, что об«явления вне определения всехфункций и переменных относятся к внешнему уровню, а об»явления внутриопределений функций относятся к внутреннему уровню.
Точныйсмысл каждого спецификатора класса памяти зависит от того, находится лиоб«явление на внешнем или внутреннем уровне и от того, об»явлен лиоб«ект функцией или переменной. В следующем разделе описывается смыслспецификаторов класса памяти в каждом случае об»явления, а также об«ясняется режим умолчания, когда спецификатор класса памяти опущенпри об»явлении переменной или функции. Об«явления переменнойна внешнем уровне
Об»явленияпеременной на внешнем уровне используют спецификаторы класса памяти static иextern или совсем опускают их. Спецификаторы класса памяти auto и register недопускаются на внешнем уровне.
Об«явления переменных на внешнем уровне- это определения переменных или ссылки наопределения, сделанные в другом месте.
Об»явлениевнешней переменной, которое инициализирует эту
переменную (явноили неявно), называется определением этой переменной. Определение на внешнемуровне может задаваться в следующих различных формах:
-переменная на внешнем уровне может быть определена путем ее об«явления соспецификатором класса памяти static. Такая переменная может быть явноинициализирована константным выражением. Если инициализатор отсутствует, топеременная автоматически инициализируется нулем во время компиляции. Такимобразом, об»явления static int k = 16; и static int k; обарассматриваются как определения;
-переменная определяется, когда она явно инициализируется
на внешнем уровне. Например, int j = 3; это определениепеременной.
Так как переменная определяется навнешнем уровне, то она видима в пределах остатка исходного файла, от места,где она определена. Переменная не видима выше своего определения в том жесамом исходном файле ни в других исходных файлах программы, если необ«явлена ссылка, которая делает ее видимой.
Переменная может быть определена навнешнем уровне внутри исходного файла только один раз. Если задаетсяспецификатор класса памяти static, то в других исходных файлах могут бытьопределены переменные с тем же именем. Так как каждое определениеstatic видимо только в пределах своего собственного исходного файла, токонфликта не возникнет.
Спецификатор класса памяти externиспользуется для об»явления ссылки на переменную, определенную где-то вдругом месте. Такие об«явления используются в случае, когда нужно сделатьвидимым определение переменной в других исходных файлах или выше места, где она определена в том же самом исходном файле. Так как ссылка на переменнуюоб»явлена на внешнем уровне, то переменная видима в пределах остаткаисходного файла от места об«явления ссылки.
В об»явлениях, которые используютспецификатор класса памяти extern, инициализация не допускается, так как ониссылаются на переменные, чьи величины уже определены.
Переменная, на которую делается ссылкаextern, должна быть определена на внешнем уровне только один раз.Определение может быть сделано в любом из исходных файлов, составляющихпрограмму.
Есть одно исключение из правил,описанных выше. Можно опус-
тить из об«явления переменной на внешнем уровне спецификатор класса памяти и инициализатор.Например, об»явление int n; будет правильным внешним об«явлением. Этооб»явление имеет два различных смысла в зависимости от контекста.
1. Еслигде-нибудь в программе будет определена на внешнем уровне переменная с тем жеименем, то об«явление является ссылкой на эту переменную, как если бы былиспользован спецификатор класса памяти extern в об»явлении.
2. Если неттакого определения, то об«явленной переменной распределяется память вовремя линкования и переменная инициализируется нулем. Если в программепоявится более чем одно такое об»явление, то память распределится для наибольшего размера из об«явленных переменных. Например, если программасодержит два неинициализированных об»явления переменной i на внешнемуровне int i; и char i; то память во время линкования распределится под переменнуюi типа int.
Неинициализированныеоб«явления переменной на внешнем уровне не рекомендуются для файлов,которые могут быть размещены в библиотеку.
Пример:
/*****************************************************
SOURCEFILE ONE *****************************************************/
extern int i; /* reference to i
definedbelow */
main()
{
i++;
printf(»%d\n",i); /* i equals 4 */
next();
}
int i = 3; /*definition of i */
next()
{
i++;
printf("%d\n",i); /* i equals 5 */
other();
}
/*****************************************************SOURCE FILE TWO
*****************************************************/
extern int i; /*reference to i in
firstsource file */
other()
{
i++;
printf("%d\n",i); /* i equals 6 */
}
Два исходных файла в совокупности содержат три внешних об«явления i. Одно об»явление содержитинициализацию- int i = 3;, где глобальная переменная i определена с начальным значением равным 3.
Самое первое об«явление extern впервом файле делает глобальную переменную видимой выше ее определения в файле.
Без об»явления extern функция mainне смогла бы сослаться на глобальную переменную i. Об«явление externпеременной i во втором исходном файле делает глобальную переменную видимой вэтом исходном файле.
Все три функции выполняют одну и ту жезадачу: они увеличивают i на 1 и печатают получившееся значение. (Предполагается, что функция printf определена где-то еще в программе.).Печатаются величины равные 4, 5 и 6.
Если быпеременная i не была бы инициализирована, она бы была автоматически установлена в 0 при линковании. В этом случае напечатанные значения были быравны 1, 2 и 3.Об»явление переменной навнутреннем уровне
Любой из четырех спецификаторов класса памяти может быть использован для об«явления переменной на внутреннемуровне. Если спецификатор класса памяти опускается в об»явлениипеременной на внутреннем уровне, то подразумевается класс памяти auto.
Спецификатор класса памяти auto об«являетпеременную с ло-
кальным временем жизни. Переменная видима только в томблоке, где она об»явлена. Об«явления переменных auto могут включать инициализаторы. Переменные класса памяти auto автоматически не инициализируются,а инициализируются явно при об»явлении или присваивании начальныхзначений, посредством операторов внутри блока. Если нет инициализации, товеличина переменной auto считается неопределенной.
Спецификатор класса памяти registerсообщает компилятору о том, чтобы он распределил память под переменную врегистре, если это возможно. Использование регистровой памяти обычно приводитк более быстрому времени доступа и к меньшему размеру результирующего кода.Переменные, об«явленные с классом памяти register име-
ют ту же самую видимость, что и переменные auto.
Число регистров, которое может бытьиспользовано под память переменных, зависит от машины. Когда компиляторвстречает спецификатор класса памяти register в об»явлении, а свободногорегистра не имеется, то для переменной распределяется память класса auto. Компилятор назначает переменным регистровую память в том порядке, в которомпоявляются об«явления в исходном файле. Регистровая память, если онаимеется, гарантирована только для целого и адресного типов.
Переменная, об»явленная навнутреннем уровне со спецификатором класса памяти static, имеет глобальноевремя жизни и имеет видимость только внутри блока, в котором онаоб«явлена. В отличие от переменных auto, переменные, об»явленные какstatic, сохраняют свое значение при завершении блока.
Переменные класса памяти static могутбыть инициализированы константным выражением. Если явной инициализации нет, топеременная класса памяти static автоматически устанавливается в 0. Инициализациявыполняется один раз во время компиляции. Инициализация переменной классапамяти static не повторяется при новом входе в блок.
Переменная, об«явленная со спецификатором класса памяти extern, является ссылкой на переменную с тем жесамым именем, определенную на внешнем уровне в любом исходном файле программы.
Цель внутреннего об»явленияextern состоит в том, чтобы
сделать определение переменной внешнего уровня видимой внутри блока. Внутреннее об'явление externне изменяет видимость глобальной переменной в любой другой части программы.
Пример:
int i = 1;
main()
{ /* reference to i,defined above */
externint i;
/* initial value is zero; a is
visible only within main */
staticint a;
/* b is stored in a register, if possible */ registerint b = 0;
/* default storage class is auto */
intc = 0;
/* values printed are 1, 0, 0, 0*/ printf("%d\n%d\n%d\n%d\n", i, a, b, c);
other();
}
other()
{
/* i is redefined */
inti = 16;
/* this a is visibleonly within other */
static int a = 2;
a += 2;
/* values printed are 16, 4 */
printf("%d\n%d\n",i, a);
}
Переменная i определяется на внешнемуровне с инициализацией 1. В функции main об«явлена ссылка extern напеременную i внешнего уровня. Переменная класса памяти static автоматически
устанавливается в 0, так как инициализатор опущен.Вызов функции print (предполагается, что функция print определена в каком-томесте исходной программы.) печатает величины 1, 0, 0, 0.
В функции other, переменная iпереопределяется как локальная переменная с начальным значением 16. Это невлияет на значение внешней переменной i. Переменная a об»является какпеременная класса памяти static с начальным значением 2. Она не противоречитпеременной a, об«явленной в функции main, так как видимость переменных класса памяти static на внутреннем уровне ограничена блоком, в котором онаоб»явлена.
Значениепеременной увеличивается на 2 и становится равным 4. Если бы функция otherбыла вызвана снова в той же самой программе, то начальное значение a стало быравным 4. Внутренние переменные класса памяти static сохраняют своизначения, когда заканчивается выполнение блока, в котором они об«явлены. Об»явление функции навнешнем и внутреннем уровнях
Функции могут быть об«явлены соспецификаторами класса памяти static или extern. Функции всегда имеютглобальное время жизни.
Правила видимостидля функций отличаются от правил видимости для переменных. Об»явленияфункций на внутреннем уровне имеют тот же самый смысл, что и об«явленияна внешнем уровне. Это значит, что функции не могут иметь блочной видимости и видимость функций не может быть вложенной. Функция об»явленная какstatic,
видима только в пределах исходного файла, в котором онаопределяется. Любая функция в том же самом исходном файле может вызватьфункцию static, но функции static из других файлов нет. Функция static с темже самым именем может быть об«явлена в другом исходном файле.
Функции, об»явленные как externвидимы в пределах всех исходных файлов, которые составляют программу. Любаяфункция может вызвать функцию extern.
Об«явленияфункций, в которых опущен спецификатор класса памяти, считаются по умолчаниюextern. Инициализация
В об»явлениипеременной может быть присвоено начальное значение посредством инициализатора.Величина или величины инициализатора присваиваются переменной.
Синтаксически,записи инициализатора предшествует знак равно (=)
=
Могут быть инициализированы переменныелюбого типа. Функции не инициализируются. Об«явления, которые используютспецификатор класса памяти extern не могут содержать инициализатора.
Переменные, об»явленные навнешнем уровне, могут быть ини-
циализированы. Если они явно не инициализированы, то ониустанавливаются в нуль во время компиляции или линкования. Любая переменная,об«явленная со спецификатором класса памяти static, может бытьинициализирована константным выражением. Инициализация переменных классаstatic выполняется один раз во время компиляции. Если отсутствует явнаяинициализация, то переменные класса памяти static автоматически устанавливаютсяв нуль.
Инициализация переменных auto и registerвыполняется каждый раз при входе в блок, в котором они об»явлены. Еслиинициализатор опущен в об«явлении переменной класса памяти auto илиregister, то начальное значение переменной не определено. Инициализация
составных типов auto (массив, структура, совмещение)запрещена. Любое составное об»явление класса памяти static может бытьинициализировано на внешнем уровне.
Начальными значениями для внешнихоб«явлений переменной и для всех переменных static как внешних так ивнутренних должно быть константное выражение. Автоматические и регистровыепеременные могут быть инициализированы константными или переменными величинами.Базовые типы и типы указателей
Синтаксис:
=
Величина выраженияприсваивается переменной. Для выражения допустимы правила преобразования.
Примеры:
int x =10; /* Example 1 */
register int *px =0; /* Example 2 */ int c = (3 * 1024); /* Example 3 */ int*b = &x; /* Example 4 */
Впервом примере x инициализируется константным выражением 10. Во второмпримере, указатель px инициализирован нулем, в результате чего получился»null" указатель. В третьем примере используется константноевыражение для инициализации c. В четвертом примере инициализируется указатель bадресом другой переменной x. Составные типы
Синтаксис:
={}
Список инициализаторов — это последовательностьинициализаторов, разделенных запятыми. Каждый инициализатор впоследовательности- это либо константное выражение, либо список инициализаторов. Поэтому, заключенный в фигурные скобки список, можетпоявиться внутри другого списка инициализации. Эта конструкция используется для инициализации элементов составных конструкций.
Для каждогосписка инициализации значения константных выражений присваиваются в порядкеследования элементов составной переменной. Когда инициализируетсясовмещение, то список инициализаторов представляет собой единственное константное выражение. Величина константного выражения присваиваетсяпервому элементу совмещения.
Если в спискеинициализации меньше величин, чем их имеется в составном типе, тооставшиеся памяти инициализируются нулем. Если число инициализирующих величинбольше чем требуется, то выдается ошибка.
Этиправила применяются к каждому вложенному списку инициализаторов, точно так жекак и ко всей конструкции в целом.
Пример:
int p[4] [3] = {
{ 1, 1, 1 },
{ 2, 2, 2 }, { 3, 3, 3,}, { 4,4, 4,},
};
В примере об«является массив pразмерности 4 строки на 3 столбца. Элементы первой строки инициализируются 1,второй строки
2 и т. д. Заметим, что списки инициализаторов третьей ичетвертой строк заканчиваются запятой. Последний список инициализаторов { 4,4, 4,} также заканчивается запятой.
Эти дополнительные запятые допускаются, но не требуются. Требуются только тезапятые, которые разделяют константные выражения и списки инициализации. Если список инициализаторов не структурирован под составной об»ект, тоего величины присваиваются в том порядке, в котором подстыкованы элементыоб«екта. Поэтому вышеприведенная инициализация эквивалентна следующей:
int p[4] [3] = {
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4
};
Фигурные скобки могут также появлятьсявокруг индивидуальных инициализаторов в списке.
Когдаинициализируются составные переменные, то нужно позаботиться о том, чтобыправильно использовать фигурные скобки и списки инициализаторов. В следующемпримере иллюстрируется более детально интерпретация компилятором фигурныхскобок.
typedef struct {
int n1, n2, n3;
} triplet;
triplet nlist[2] [3] = {
{ { 1, 2, 3 }, { 4, 5, 6 }, { 7,8, 9 } }, /* Line 1 */ { { 10,11,12}, { 13,14,15}, { 15,16,17} } /* Line 2*/
};
В примере nlistоб»является как массив структур, состоящий из двух строк и трех столбцов.Каждая структура состоит из трех элементов. Первая строка инициализацииназначает величины первой строке массива nlist следующим образом:
1. Первая левая фигурная скобка Line 1информирует компилятор о том, что это начало инициализации первой строки массива nlist(nlist[0]).
2. Вторая левая фигурная скобка означаетто, что начинается инициализация первого элемента первой строки массива (nlist[0] [0] ).
3. Первая правая фигурная скобка сообщаетоб окончании инициализации первого элемента- структуры nlist[0] [0]. Следующая левая фигурная скобка сообщает о начале инициализации второгоэлемента первой строки nlist[0] [1].
4. Процесспродолжается до конца Line 1 и заканчивается по последней правой фигурнойскобке.
Аналогично, Line 2 назначает величинывторой строке массива nlist.
Заметим, чтовнешние фигурные скобки инициализаторов Line 1 и Line 2 требуются. Следующаяконструкция, в которой внешние фигурные скобки опущены будет неверной.
/* THIS CAUSES AN ERROR */
triplet nlist[2] [3] = {
{ 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9}, /* Line 1 */
{ 10,11,12}, { 13,14,15}, {16,17,18} /* Line 2 */
};
В этом примерепервая левая фигурная скобка в Line 1 стартует инициализацию nlist[0], которая является массивом из трех структур. Величины 1, 2, 3 назначаются трем элементам первой структуры. Когда встретится правая фигурная скобка(после величины 3), инициализация nlist[0] закончится и две оставшиеся структурыавтоматически инициализируются нулем. Аналогично, { 4, 5, 6 } инициализирует первую структуру во второй строке nlist, а оставшиеся две структуры nlist[1]установятся в нуль. Когда компилятор встретит следующий список инициализации{ 7, 8, 9 }, то это приведет к попытке инициализировать nlist[2]. Так какnlist содержит только две строки, то будет выдано сообщение об ошибке.
Примеры:
/******************* Example 1*********************/
struct list {
int i, j, k;
float n[2] [3];
} x = {
1,
2,
3,
{4.0, 4.0, 4.0}
};
/******************* Example 2 *********************/
union {
char x[2] [3];
int i, j, k;
} y = {
{'1'},
{'4'}
};
В первом примере три элемента intструктурной переменной x инициализированы 1, 2, и 3 соответственно. Триэлемента первой строки массива m инициализированы как 4.0. Элементы второйстроки инициализированы нулем по умолчанию.
Во втором примереинициализируется переменная y типа совмещения. Первым элементом совмещенияявляется массив, для которого требуется составной инициализатор. Списокинициализации {'1'} задает величины для первой строки массива. Поскольку всписке всего одна величина, то только первый элемент строки массива инициализируетсясимволом 1, а оставшиеся два элемента в строке инициализируются нулем (символом \0) по умолчанию. Аналогично, первый элемент второй строки массиваx инициализируется символом 4, а оставшиеся два элемента в строкеинициализируются нулем. Строковые инициализаторы
Массив может быть инициализирован строчным литералом.
Например,
char code[ ] =«abc»;
инициализирует code как массив символовиз четырех элементов. Четвертым элементом является символ \0, которыйзавершает все строковые литералы.
Если специфицируется размер массива, астрока больше чем
специфицированный размер, то лишние символыотбрасываются. Следующее об«явление инициализирует переменную code, как трехэлементный массив символов:
char code[3] = „abcd“
В примере только три первые символаинициализатора назначаются для массива code. Символ d и сивол нульотбрасываются.
Если строка короче, чем специфицированный размер массива, то оставшиеся элементы массиваинициализируются нулем (символом \0). Об»явления типов
Об«явление типа определяет имя иэлементы структурного или совмещающего типов или имя и перечислимое множествоперечислимого типа.
Имя типа может быть использовано воб»явлениях переменных и функций в качестве ссылки на этот тип. Этополезно, когда многие переменные или функции имеют один и тот же тип.
Об«явление typedef определяет спецификатор типа для типа. Это об»явлениеиспользуется для того, чтобы создавать более короткие или более осмысленные имена типов уже определенных в Си или об«явленныхпользователем. Типы структур, совмещений иперечислений
Об»явления типов структур,совмещений и перечислений имеют ту же самую общую синтаксическую форму, как иоб«явления переменных этих типов. В об»явлении типа идентификаторпеременной опущен, так как нет переменной которая об«является. Именем структуры, совмещения или перечисления является тег.
В об»явлении типа может появитьсясписок об«явлений элементов- или список перечисления-
, определяющие тип.
Сокращенная форма об»явленияпеременной, в котором tag ссылается на тип, определенный где-то еще, при об«явлении типа не используется.
Примеры:
/******************** Example 1********************/
enum status {
loss = -1,
bye,
tie = 0,
win,
};
/********************* Example 2*******************/
struct student {
char name[20];
int id, claas;
};
В первом примере об»является типперечисления, поименованный status. Имя типа может быть использовано в об'явлениях перменных типа перечисления. Идентификатор loss явно устанавливается в -1. Идентификаторы bye и tie ассоциируются со значением 0, а win принимаетзначение 1. Во втором примере об«является структурный тип, поименованныйstudent. Теперь можно использовать такое об»явление, как struct studentemployee, чтобы об«явить структур-
ную переменную employee типа student. Об»явления typedef
Синтаксис:
typedef [,...];Об«явления typedef являются аналогом об»явления переменной,
за исключением того, чтоключевое слово typedef заменяет спецификатор класса памяти.
Об«явление интерпретируется тем же самым путем, как об»явления переменной илифункции, но вместо того, чтобы стать переменной типа, специфицированногооб«явлением, становится синонимом имени типа. Об»явление typedefне создает типов. Оно создает синонимы для существующих имен типов, которыебыли специфицированы другим способом. Любой тип может быть об«явлен с typedef, включая типы указателя, функции и массива. Имя с ключевым словомtypedef для типов указателя, структуры или совмещения может быть об»явлено прежде чем эти типы будут определены, но в пределах видимостиоб«явления.
Примеры:
/********************Example 1 ********************/
typedef int WHOLE;
/******************** Example 2********************/
typedef struct club {
char name[30];
int sise, year;
}GROUP;
/******************** Example 3********************/
typedef GROUP*PG;
/******************** Example 4********************/
typedef voidDRAWE(int, int);
В первом примереоб»является WHOLE как синоним для int .
Во втором примереоб" является GROUP как структурный тип с тремя элементами. Так какспецифицирован также тег clab, то имя GROUP и тег club могу быть использованы воб«явлениях.
В третьем примереиспользуется предидущее имя typedef для об»явления адресного типа. Тип PGоб«является как указатель на тип GROUP, который в свою очередьопределен как структурный тип.
В последнем примере представлен тип DRAWE для функции не
возвращающей значенияи требующей два аргумента типа int. Это означает, например, чтооб»явление DRAWE box; эквивалентно об«явлению void box(int, int); Имена типов
Имя типаспецифицирует особенности типа данных. Имена типов используются в трехконтекстах: в списках типов аргументов, при об»явлении функций, ввычислениях cast (преобразованиях типов), и в sizeof операциях. Списки типов аргументов рассматривались в
разделе 4.5. «Об»явления функций".Преобразования cast и операция sizeof обсуждаются в разделах 5.7.2. и 5.3.4. соответственно.
Именами для основных, перечисляющих, структурных и совмещающих типов являются спецификаторы типа длякаждого из них. Имена для типов указателя, массива и функции задаютсяследующей синтаксической формой:
Абстрактный декларатор- это декларатор без идентификатора, состоящий изодного или более модификаторов указателей, массивов и функций. Модификаторуказателя (*) всегда появляется перед идентификатором в деклараторе, в то времякак модификатор массива ([]) или функции ( () ) появляются послеидентификатора. Таким образом, чтобы правильно интерпретировать абстрактныйдекларатор, нужно начинать интерпретацию с подразумеваемого идентификатора.
Абстрактныедеклираторы могут быть составными. Скобки в составном абстрактномдеклараторе специфицируют порядок интерпретации, подобно тому как этоделается при интерпретации составных деклараторов об«явлений. Абстрактный декларатор, состоящий из пустых круглых скобок () недопускается, поскольку это двусмысленно. В этом случае невозможно определитьнаходится ли подразу-
меваемый идентификатор внутри скобок, и в таком случае-это немодифицированный тип, или перед скобками, тогда- это тип функции.Спецификаторы типа, установленные посредством об»явлений typedef, такжерассматриваются как имена типов.
Примеры:
long * /*Example 1 */
int (*) [5] /*Example 2 */
int (*) (void) /*Example 3 */
В первом примерезадано имя типа как указатель на тип long. Во втором и третьем примерахпоказано каким образом скобки
модифицируютсоставные абстрактные деклараторы. В примере 2 задано имя типа для указателяна массив иэ пяти злементов. В третьем примере именуется указатель на функцию,не требующую аргументов и возвращающую значение типа int.КОНТРОЛЬНЫЕ ВОПРОСЫ:
1. Какие ошибки содержатследующие операторы?
enum State { on, off };
enum YesNo { yes, no};
enum DiskDriveStatus {on, off };
2. Верно или нет, что объявлениеследующего перечислимого типа неправильно?
enum YesNo { no = 0, No= 0, yes = 1, Yes = 1 };
3. Что не так в следующейпрограмме?
#include
int main()
{
int *p = new int;
cout
cin >> *p;
cout
return 0;
}ФункцииОбъявление и определениефункций
— Общая форма определенияфункции такова:
возвращаемыйТипимяФункции()
// обязателен тип возвращаемогозначения
{
return возвращаемоеЗначение;// — если возвращаемыйТип не void
}
— Выход из функцииосуществляется по оператору return. Void-функции
могут не возвращать значения.
Список параметров:
[const] тип1 параметр1,[const] тип2 параметр2, ...
— Ключевое слово constпредохраняет передаваемые по ссылке аргументы от случайного изменения.
Программа USERINFO.CPPиллюстрирует использование модификатора
// const в списке параметров
*/
struct userInfo
{
int age;
char name[150];
};
void processUserInfo(/*const*/userInfo &ui)
// при снятии комментария будетсообщение об ошибке,
// поскольку модификатор constзапрещает изменение параметра
{
if ( ui.age
cout
return;
}
if ( ui.age
ui.age = 21;
}
/*
Если функция вызывается досвоего определения, обязательно должен быть задан прототип функции. Общая формаобъявления функции:
возврТип имяФункции();
При объявлении функции именапараметров могут быть опущены.
— Передача аргумента поссылке позволяет функции изменять значение переданного аргумента и экономитпамять, так как при этом не создается локальная копия аргумента:
[const] тип1& параметр1,[const] тип2& параметр2, ...
void foo(int &); // — объявление функции — это ее прототип
int main()
{
int value = 5;
foo(value);
cout
return 0;
}
void foo(int &parm) // — определение функции вызов параметра по ссылке
{
++parm;
}
/* Результаты:
6
*/
void foo(int *); // пердачауказателя
int main()
{
int value = 5;
foo(&value); // передаетсяадрес
cout
getch();
foo(&value);
cout
getch();
return 0;
}
void foo(int* parm)
{
++*parm; // параметр — указатель
}
/* Результаты:
6
7
— Локальные переменные иконстанты существуют и действуют только в теле данной функции, где ониобъявлены. Объявление локальных переменных подобно объявлению глобальных переменных.
Программа LOCAL.CPP знакомит спонятием локальной переменной
— Ключевое слово staticпозволяет объявить переменную как статическую.
Статическая переменная являетсялокальной переменной, но сохраняет свое значение между вызовами функции. Обычностатические переменные инициализируются. Начальные значения присваиваются передпервым вызовом функции, в которой определена статическая переменная.
Программа STATIC.CPP знакомит спонятием статической локальной переменной
— Макроопределения позволяют вамвводить компактные псевдо-функции, принимающие любые типы данных, посколькукомпилятор не выполняет в этом случае проверку типов:
#define min(n1, n2) (((n1)
#define max(n1, n2) (((n1) >(n2))? (n1): (n2))
double num1 = 50, num2 = 5,rslt;
rslt = min(num1 / 2, num2 * 2);
— При объявлении функции смодификатором inline компилятор заменяет вызов функции ее телом. В этом смыслеэти функции похожи на макросы.
Отличие состоит в том, чтовстроенные функции выполняют проверку типов данных.
Программа INLINE.CPP,иллюстрирующая применение встроенной функции
— Используя аргументы поумолчанию для некоторых параметров, при вызове функции вы можете не задаватьаргументы для этих параметров; тогда им автоматически будут присваиватьсязначения по умолчанию.
Программа DEFARGS.CPP,иллюстрирующая применение аргументов по умолчанию
— Рекурсивными называютсяфункции, которые вызывают сами себя. Количество рекурсивных вызовов должно бытьограничено, чтобы не столкнуться с проблемой нехватки памяти. По этой причинекаждая рекурсивная функция должна выполнять проверку условия на окончаниерекурсии.
Пример программы FACTOR.CPP,использующей рекурсивную функцию
— Перегрузка функций позволяетвам иметь несколько функций с одним именем, но с разными списками аргументов(список аргументов еще называется сигнатурой функции). Тип возвращаемогофункцией значения не является частью сигнатуры.
Программа OVERLOAD.CPP,иллюстрирующая перегрузку функцииТИПОВЫЕ ВОПРОСЫ С ОТВЕТАМИ
Можно ли в С++ объявлятьвложенные функции?
Нет, так как это приводит кбольшим накладным расходам во время выполнения программы.
В каких случаях нужноиспользовать статические глобальные переменные?
Можете использовать их, гдехотите. Когда вы объявляете статической глобальную переменную (которые я вам несоветую использовать вообще), вы даете указание компилятору сделать ееневидимой для функций из других файлов. Такая переменная недоступна из другихфайлов вашего проекта.
Как расходуется память приобслуживании вызовов рекурсивной функции?
Исполняющая системаиспользует стек для хранения временных данных, в том числе необходимых длягенерирования вызова рекурсивной функции. Как и другие ресурсы, стек ограниченв своем размере. В результате при длинной цепочке вызовов рекурсивной функциистек может переполниться, что приведет к остановке программы из-за ошибоквыполнения или переполнения стека.ПРАКТИКУМКонтрольные вопросы
1. Каков будет результат работыследующей программы? Что вы можете сказать по поводу функции swap?
*/
# include
void swap(int i, int j)
{
int temp = i;
i = j;
j = temp;
}
int main()
{
int a = 10, b = 3;
swap (a, b);
cout
return 0;
}
/*
2. Каков будет результат работыследующей программы? Что вы можете сказать по поводу еще одной функции swap?
*/
#include
void swap(int &i, int&j)
{
int temp = i;
i = j;
j = temp;
}
int main()
{
int a = 10, b = 3;
swap (a, b);
cout
return 0;
}
/*
3. Что за проблема возникнет соследующими перегруженными функциями?
*/
void inc(int &i)
{
i = i + 1;
}
void inc(int &i, int diff= 1)
{
i = + diff;
}
/*
4. Найдите ошибку в функции.
/*
double volume(double length,double width = 1, double height)
{
return length * width *height
}
/*
5. Найдите ошибку в функции.
*/
void inc (int &i, intdiff = 1)
{
i = I + diff;
}
/*
6. В этой программе есть ошибка.Что это за ошибка и как ее исправить?
*/
# include
int main()
{
double x = 5.2;
cout
return 0;
}
double sqr( double х)
{ return x * x; }
/*
7. Попробуйте в функциивычисления факториала использовать операцию ?: .Массивы
// Листинг 6.1. исходный текстпрограммы AVERAGE1.CPP
// Программа иллюстрируетиспользование одномерных массивов
// при расчете среднегозначения.
#include
const int MAX = 0x1FFF; //64K/8- максимальный размер массива типа double ***
int main()
{
double array[MAX]; //объявление одномерного массива ***
int num_elem;
// Ввод количестваобрабатываемых данных
do
{
cout
cin >> num_elem;
cout
} while (num_elem MAX);
// Ввод данных
for (int ix = 0; ix
{
cout
cin >> array[ix];
}
// Расчет среднего значения
double sum = 0;
for (ix = 0; ix
sum += array[ix];
cout
return 0;
/*
- При объявлении одномерныхмассивов им можно присвоить начальные значения. Список ИНИЦИАЛИЗАЦИИ долженбыть заключен в фигурные скобки, а элементы в нем должны быть разделенызапятыми. Можно при инициализации задать данных МЕНЬШЕ, чем размер массива. Вэтом случае компилятор автоматически присвоит нулевые значения тем элементам,которые вы не инициализировали. И вдобавок, если вы не укажете размерностьинициализируемого массива, она будет определена по количеству элементов всписке инициализации.
*/
// Листинг 6.2. исходный текстпрограммы AVERAGE2.CPP
// Программа иллюстрируетиспользование одномерных массивов
// при расчете среднегозначения.
// Данные задаются приинициализации массива.
#include
const int MAX = 10; //50
int main()
{
double array[MAX] = { 12.2,45.4, 67.2, 12.2, 34.6, 87.4,
83.6,12.3, 14.8/*, 55.5*/ };
int num_elem = MAX;
//double array[] = { 12.2,45.4, 67.2, 12.2, 34.6, 87.4,
// 83.6,12.3, 14.8, 55.5 };
//int num_elem =sizeof(array) / sizeof(array[0]);
double sum = 0;
for (int ix = 0; ix
{
sum += array[ix];
cout
}
cout
return 0;
}
- Объявление одномерныхмассивов в качестве параметров функции возможно в двух формах: массив-параметрфиксированной размерности и массив-параметр неопределенной длины (открытыймассив), При объявлении параметром массива фиксированной размерностиуказывается размер массива. В этом случае передаваемые функции аргументы должнысоответствовать параметру по типу и размеру. Массив- араметр неопределеннойдлины объявляется с пустыми скобками, означающими, что аргумент может бытьлюбого размера.
(Листинг 6.3а. исходный текстпрограммы MINMAX.CPP)
(Листинг 6.3. исходный текстпрограммы MINMAX.CPP)СОРТИРОВКА массива — ПРИМЕР вфайле list6_4cpp.
В результате сортировки элементымассива распределяются в порядке возрастания или убывания. Осуществлять поиск всортированном массиве намного проще, чем в несортированном. Для сортировкимассивов можно использовать эффективную встроенную функцию быстрой сортировкиqsort.- ПОИСК в массиве
означает нахождение в массиве элемента, совпадающего сзаданным значением. Методы поиска делятся на две группы: для упорядоченных инеупорядоченных массивов. Метод линейного поиска применяется для неупорядоченныхмассивов, а метод двоичного поиска — для сортированных массивов. (Пример — list6_5.cpp)
Рассмотрим понятияПАРАМЕТРОВ-ФУНКЦИЙ и УКАЗАТЕЛИ НА ФУНКЦИИ:
(Листинг 6.5. исходный текстпрограммы SEARCH.CPP)БИБЛИОТЕЧНЫЕ ФУНКЦИИ ПОИСКА иСОРТИРОВКИ в непрерывных массивах:
*/
void *bsearch(const void *key,const void *base, size_t nelem,
size_t width, int(*fcmp)(const void*, const void*));
// key — указатель на искомыйэлемент,
// возвращаемое значение — указатель на элемент (0 — не найден)
// base — базовый адрес массива
// num — число элементов вмассиве
// width — размер элемента
// fcmp — указатель на функциюсравнения элементов массива
// Функция возвращает указательна элемент, а не значение индекса элемента
// Если элемент не обнаружен,возвращается 0.
// Для вычисления индексаможно использовать следующую формулу:
index = (searchRslt — arrayBase) / sizeof(arrayBase[0]);
void *lfind(const void *key,const void *base, size_t *num,
size_t width, int(*fcmp)(const void *, const void*));
void *lsearch(const void *key,void *base, size_t *num,
size_t width, int(*fcmp)(const void *, const void *));
// — если нет элемента, то онвставляется, поэтому возвращаемое значение
// всегда не ноль.
void qsort(void *base, size_tnelem,
size_t width, int(*fcmp)(const void *, const void *));
/*
- При объявлении многомерныхмассивов вам нужно указать тип массива, его имя и размер (заключенный в своюпару скобок) по каждому измерению. Нижнее значение индекса для любого измеренияравно 0. Верхнее значение индекса по любому измерению равно количествуэлементов поэтому измерению минус единица.
- Для того чтобы обратиться кмногомерному массиву, Вам нужно задать его имя и правильные значения индексов.Каждый индекс должен быть заключен в свою пару скобок.
Пример работы с двумерныммассивом:
(Листинг 6.6. Исходный текстпрограммы MATRIX1.CPP)
— При объявлении многомерныхмассивов им можно присвоить начальные значения. Список ИНИЦИАЛИЗАЦИИ долженбыть заключен в фигурные скобки, а элементы в нем должны быть разделенызапятыми. Можно при инициализации задать данных меньше, чем размер массива, Вэтом случае компилятор автоматически присвоит нулевые значения тем элементам,для которых вы не указали начальные значения:
(Листинг 6.7. Исходный текстпрограммы MATRIX2.CPP.)
— Объявление многомерныхмассивов в качестве параметров функции воз- можно в двух формах:массив-параметр фиксированной размерности и массив-параметр неопределеннойдлины по первому измерению. При объявлении параметром массива фиксированнойразмерности указывается размер массива по каждому измерению. В этом случаепередаваемые функции аргументы должны соответствовать по типу и размерупараметру. Массив-параметр неопределенной длины объявляется с пустыми скобкамидля первого измерения, означающими, что передаваемый аргумент может быть любогоразмера по первому измерению. По другим измерениям размеры аргумента ипараметра должны совпадать:
(Листинг 6.8. Исходный текстпрограммы MATRIX3.CPP)Строки и управление вводом/выводом
Здесьподробнее рассматриваются операции консольного ввода/вывода. C++, как и егопредок — язык С — не определяет операции ввода/вывода как часть языка, авыносит операции консольного ввода/вывода в библиотеки ввода/вывода. Такиебиблиотеки в основном предназначены для работы в MS-DOS.Рассмотрим небольшую выборку функций ввода/вывода, объявляемых в заголовочныхфайлах STDIO.H иIOSTREAM.H.
Сегоднямы рассмотрим следующие темы:
· Форматированный потоковый вывод
· Потоковый ввод
· Функция printf
· Строки в C++
· Ввод строк
· Использование стандартнойбиблиотеки функций для работы со строками
· Присвоение значений строкам
· Определение длины строки
· Конкатенация строк
· Сравнение строк
· Преобразование строк
· Перестановка символов в строке вобратном порядке
· Поиск символа
· Поиск подстрокиФорматированный потоковый вывод
C++имеет целое семейство гибких библиотек функций ввода/вывода. Разработчикамязыка было ясно, что функции ввода/вывода из STDIO.H, унаследованныеиз С, имеют ограничения при работе с классами (вы узнаете больше о классах вглаве 8)., В результате в C++ было введено понятие потоков. Вспомним, что потоки,которые уже существовали в С, означают последовательность данных, передаваемыхиз одной части компьютера в другую. В программах, рассматриваемых ранее, вывидели операцию помещения в поток «, например — в стандартный поток вывода, cout.Встречалась вам и операция извлечения из потока », применяемая к стандартномупотоку ввода, cin. В этом разделе мы познакомимся с потоковыми функциямиwidth иprecision, используемыми приформатировании вывода. Библиотеки потоков C++ содержат большое количество такихфункций, позволяющих настроить ваш вывод.
Функция widthзадает ширину поля вывода. Общая форма использования функции widthс потоком cout:
cout.width (widthOf Output);
Функцияprecision определяет количество значащих цифр после точки для чисел с плавающейточкой. Общая форма использования функции precision с потоком cout:
cout.precision(numberOfDigits);
Обратимсяк примеру, программе OUT1.CPP, исходный текст которой приведен в листинге 1.Программа, в которую ничего не вводится, просто выводит форматированные целыечисла, числа с плавающей точкой и символы с использованием функций width иprecision.Листинг 1. Исходный текст программы OUT1.CPP01 // Программа иллюстрирует потоковый форматированный вывод в C++ 02 // с использованием функций width и precision 03 #include 04 05 int main() 06 { 07 int anInt = 67; 08 unsigned char aByte = 128; 09 char aChar = '@'; 10 float aSingle = 355.1112; 11 double aDouble = 1.131112e+002; 12 13 // Вывод простых выражений 14 cout.width(3); cout Пример программной сессии:
Введитетри числа через пробел: 123
Суммачисел = 6
Среднееэтих чисел = 2
Введитетри символа: ABC
Выввели символы 'A', 'B', 'C'
Введитечисло, символ, и число: 12A34.4
Выввели 12 A 34.4
Введитесимвол, число и символ: A3.14Z
Выввели A 3.14 Z
Впрограмме из листинга 2 объявляется четыре переменных типа double и трипеременных типа char. Оператор вывода в строке 10 предлагает вам ввести тричисла. Оператор ввода в строке 11 помещает введенные вами числа в переменные х,у и z. He забывайте, что при вводе чисел их нужно разделять пробелами. Либовводите каждое число с новой строки. Первое введенное вами число будет помещенов переменную х, второе — в у, а третье окажется в переменной z. Данные в переменныезаносятся в том порядке, в котором переменные перечислены в операторе ввода встроке 11. Оператор в строке 12 вычисляет сумму значений переменных х, у и z.Оператор вывода в строках 13 и 14 выводит сумму и среднее значение введенныхвами величин.
Операторвывода в строке 15 предлагает вам ввести три символа. Оператор (ввода в строке16 последовательно размещает введенные символы в переменных с1, с2, с3.Использовать пробел для разделения вводимых символов не обязательно. Например,вы можете ввести данные и таким образом: 1А2, Bob и 1 D d. Оператор вывода встроках 17—19 выводит введенные вами символы, разделенные пробелами изаключенные в одинарные кавычки.
Операторвывода в строке 20 предлагает вам ввести число, символ и число. Оператор вводав строке 21 помещает ваши данные в переменные х, с1 и у. Пробел-разделительздесь нужен только в том случае, если символ может быть интерпретирован какчасть числа. Например, если вам нужно ввести число 12, символ «точка» и число55, вам нужно набрать на клавиатуре 12. 55. Вводимый символ «точка» лучше«заключить» в пробелы, чтобы быть уверенным, что поток вода не воспримет этуточку как точку, разделяющую в вещественном числе целую и дробную части.Оператор вывода в строке 22 выводит введенные вами данные разделенныепробелами.
Операторвывода в строке 23 предлагает вам ввести символ, число и символ. Оператор вводав строке 24 последовательно размещает введенные значения в переменных с1, х,с2. Пробел-разделитель здесь нужно использовать только в том случае, еслисимвол может быть интерпретирован как часть числа. Например, если вам нужноввести символ «-», число 12 и цифру 0, вам нужно набрать на клавиатуре 12 0.Оператор вывода в строке 25 выводит введенные вами данные, разделяя ихпробелами.Функцияprintf
Просматриваяпрограммы, написанные разными людьми, вы часто можете встретить функцию printf.Этот стандартный оператор вывода пришел из языка С. Так как C++ являетсярасширением С, эта функция поддерживается и в этом языке. Многие программистыдо сих пор предпочитают использовать старую функцию printf, а не потокиввода/вывода C++. Вот почему вам эта функция наверняка уже знакома. Но, помимоэтого, эта функция имеет несколько очень мощных возможностей, и в ряде случаевона оказывается удобнее функций потоков. Прототип функций можно найти взаголовочном файле STDIO.H.Функция printf
Общая формаобъявления функции printf:
intprintf(const char *format[, argument,… ]);
Параметрformat является символьным массивом, содержащим выводимый текст. Кроме этогообязательного параметра, могут быть необязательные аргументы. Массив formatможет содержать специальные форматирующие символы, которые выполняютпреобразование необязательных аргументов при выводе.
Функцияprintf является очень мощной функцией с богатыми возможностями форматированиявывода. В качестве первого шага в освоении ее возможностей рассмотримEsc-последовательности, позволяющие представлять специальные символы.Esc-последовательность начинается с символа «\» — «обратная косая черта».Esc-коды представлены в таблице 1.Таблица 1. Еsс — последовательности
Последовательность
Десятичное значение
Шестнадцатеричное значение
Название \а 7 0х07 Звонок \b 8 0х08 Возврат назад \f 12 0х0С Перевод страницы \n 10 0х0А Новая строка \г 13 0x0D Возврат каретки \t 9 0х09 Табуляция \v 11 0х0В Вертикальная табуляция \\ 92 0х5С Обратная черта \' 44 0х2С Апостроф \“ 34 0х22 Кавычка \? 63 0х3 F Знак вопроса \0 Восьмеричное число, от 1 до 3 цифр \XHHH и \xhhh 0xhhh Шестнадцатеричное число
Функцияprintf имеет специальные форматирующие спецификации (символы) для выводапеременных. Общий вид этих спецификаций таков:
%[flags] [width] [.precision] [F | N | h | l| L ]
Опцииflags могут определять выравнивание, отображение знака числа при выводе, выводдесятичной точки и символов заполнения. Кроме того, эти флаги определяютпрефиксы для восьмеричных и шестнадцатеричных чисел. Возможные значения флаговприведены в таблице 2.Таблица 7.2. Значения флагов строки формата функцииprintf
Символ
Назначение - Выравнивать вывод по левому краю поля + Всегда выводить знак числа Пробел Выводить пробел перед положительным числом и знак минус — перед отрицательным # Не влияет на вывод десятичных целых, для шестнадцатеричных чисел выводит префикс 0х или 0Х, перед восьмеричными целыми выводит ноль, десятичную точку для вещественных чисел.
Спецификацияwidth определяет минимальное количество выводимых символов. Если необходимо,используются заполнители — пробелы или нули. Когда значение для widthначинается с нуля, printf использует в качестве заполнителей нули, а непробелы. Если в качестве значения для width используется универсальный символ*, а не число, то printf подставляет на место этого символа значение, котороедолжно содержаться в списке аргументов. Это значение ширины поля должнопредшествовать выводимому значению. Ниже приведен пример вывода числа 2,занимающего три позиции, согласно значению второго аргумента printf:
printf(»%*d",3, 2);
Спецификаторprecision определяет максимальное количество выводимых цифр. В случае целогочисла он определяет минимальное количество выводимых символов. Для precisionтакже можно применить символ *, вместо которого будет подставлено значение изсписка аргументов. Это значение точности представления должно предшествоватьвыводимому значению. Ниже приведен пример вывода числа с плавающей точкой3.3244 с использованием десяти символов, как это задано вторым аргументомprintf:
printf("%7.*f",10, 3.3244);
СимволыF, N, h,lиLявляются символами размера, переопределяющими размер по умолчанию. Символы F иN применяются с указателями,farи nearсоответственно. Символы h, l, и L используются для указания соответственно типовshort int, longилиlong double.
Символамтипа данных должен предшествовать форматирующий символ %. В таблице 7.2 мыпоказали возможные значения флагов форматирующей строки printf. Символытипов данных перечислены в таблице 7.3.Таблица 3. Символы типов данных строки форматафункции printf
Тип данных
символ типа
результат Символ c Один символ d Десятичное целое со знаком i Десятичное целое со знаком O Восьмеричное целое без знака N Десятичное целое без знака X Шестнадцатеричное целое без знака; набор цифр — 0123456789abcdef X Шестнадцатеричное целое без знака; набор цифр — 0123456789ABCDEF Указатель P Для указателей near выводит только смещение в виде: 0000. Указатели far отображаются в виде: SSSS:0000 Указатель на целое N Вещественное F Выводит величину со знаком в формате [-]dddd.dddd E Выводит вещественную величину со знаком в экспоненциальном формате [-]d.dddde[+|-]ddd Е Выводит вещественную величину со знаком в экспоненциальном формате [-]d.ddddE[+|-]ddd G Выводит вещественную величину со знаком в формате f или е в зависимости от ее значения и заданной точности G Выводит вещественную величину со знаком в формате F или Е в зависимости от ее значения и заданной точности Указатель S Выводит строку символов, пока не встретит нуль-терминатор строки
Разберемнебольшой пример. Программа OUT2.CPP, исходный код которой приведен в листинге3, создана на основе программы OUT1.CPP. В этой программе используетсяформатированный вывод с использованием функции printf. Программа выводит те жечисла, что и OUT1.CPP, используя три различных набора спецификацийпреобразования.Листинг 3. Исходный текст программы OUT2.CPP в файлеList7-3.CPP
01
// Программа, использующая printf для форматирования вывода
02
03 #include
04
05 int main()
06 {
07 int anInt = 67;
08 Unsigned char aByte = 128;
09 char aChar = '@';
10 Float aSingle = 355.0;
11 Double aDouble = 1.130e+002;
12
13 Printf("%3d + %2d = %3d\n",
14 aByte, anInt, aByte + anInt );
15
16 Printf(«Вывод использует спецификации преобразования %%lf :\n»);
17 Printf(" %6.4f / %10.4lf = %7.5lf\n",
18 aSingle, aDouble, aSingle / aDouble );
19
20 Printf(«Вывод использует спецификации преобразования %%le :\n»);
21 printf(" %6.4e / %6.4le = %7.5le\n",
22 aSingle, aDouble, aSingle / aDouble );
23
24 printf(«Вывод использует спецификации преобразования %%lg :\n»);
25 printf(" %6.4g / %6.4lg = %7.5lg\n",
26 aSingle, aDouble, aSingle / aDouble );
27
28 printf(«Символьная переменная aChar: %c\n», aChar);
29 printf(«ASCII-код %c: %d\n», aChar, aChar);
30 return 0;
31 }
Пример вывода программы из листинга 3:
128+ 67 = 195
Выводиспользует спецификации преобразования %lf :
355.0000 / 113.0000 = 3.14159
Выводиспользует спецификации преобразования %le :
3.5500e+02 / 1.1300e+02 = 3.14159e+00
Выводиспользует спецификации преобразования %lg :
355 / 113 = 3.1416
Символьнаяпеременная aChar: @
ASCII-код@: 64
В программе излистинга 3 объявляется целый набор переменных различных типов. Оператор выводав строках 13 и 14 выводит целые, используя спецификацию формата %d. В таблице 4приведены результаты действия спецификаций преобразования из строки 13.Обратите внимание на то, что первая переменная была преобразована из типаunsigned char в тип integer.Таблица 4. Результат действия спецификацийформатирования в функции printf из строки 13
Спецификация формата
Переменная
Тип данных
Тип после преобразования %3d aByte unsigned char Int %2d anInt int Int %3d aByte + anInt int Int
Операторвывода в строке 17 выводит переменные aSingle, aDouble и выражение aSingle /aDouble, используя спецификации преобразования %6.4f, %6.41f и % 7.51f.Точность представления задается ими равной 4, 4 и 5 цифрам, а минимальнаяширина поля 6, 6 и 7 цифрам соответственно. Две последних спецификацииосуществляют преобразование величин двойной точности.
Операторвывода в строке 21 подобен оператору из строки 17. Отличие состоит в том, чтоиспользуется е-формат вместо f-формата. Соответственно три значения выводятся вэкспоненциальном формате.
Операториз строки 25 также похож на оператор из строки 17. Основное отличие состоит втом, что вместо f-формата используется g-формат. В результате первые два числавыводятся без дробной части, поскольку они являются целыми величинами.
Операторвывода в строке 28 выводит содержимое переменной aChar по формату %с. Операторвывода в строке 29 выводит ту же переменную aChar дважды, первый раз каксимвол, а второй раз как целое (или, если быть точным, выводится ASCII-кодсимвола). Для этого используются спецификации преобразования %с и %dсоответственно.
Массивы символов в C++
ВC++ имеется специальный класс для работы со строками, которого, конечно, небыло в языке С. В С строки вводились как массивы символов, ограниченныенуль-символом (ASCII-код которого равен нулю), поэтому большое количествопрограмм, написанных на С, используют символьные массивы. Более того, и в C++,несмотря на то, что он имеет класс для работы со строками, находится применениемассивам символов. Поэтому термин «строка» имеет два значения: строка в смыслеC++ и строка как массив символов. Весь этот раздел будет посвящен тому, какнужно и не нужно использовать символьные массивы.
Символ'\0' также называют нуль-терминатором. Строки, оканчивающиесянуль-терминатором, называют еще ASCIIZ-строками, где символ Zобозначает ноль — ASCII-код нуль-терминатора. Еще этот символ называютNUL-символом, поскольку этот термин является его именем в ASCII.
Всестроки обязательно должны оканчиваться нуль-терминатором, и при объявленииразмера массива необходимо это учитывать. Когда вы объявляете строковуюпеременную как массив символов, увеличьте размер массива на один символ длянуль-терминатора. Использование строк с конечным нулем также имеет топреимущество, что здесь отсутствуют ограничения, накладываемые реализацией C++.Кроме того, структура ASCIIZ-строк очень проста.Ввод строк
Впрограммах, которые мы рассматривали, операторы потокового вывода выводилистроковые константы; C++ поддерживает потоковый вывод для строк какспециального не-предопределенного типа данных. (Можно сказать, что это былосделано по требованию масс.) Операции и синтаксис для вывода строковыхпеременных остаются прежними. При вводе строк операция извлечения из потока »не всегда будет работать так, как вы ожидаете, поскольку строки часто содержатпробелы, которые игнорируются оператором ввода; поэтому вместо оператора вводавам нужно использовать функцию getline. Эта функция вводит заданное количествосимволов.Функция getline
Перегруженнаяфункция getline объявляется следующим образом:
istreamsgetline( signed char *buffer,
int size,
char delimiter = '\n') ;
istreamsgetline( unsigned char *buffer,
int size,
char delimiter = '\n') ;
istream&getline( char *buffer,
int size,
char delimiter = '\n') ;
Параметрbuffer является указателем на строку, в которую помещаются вводимые символы.Параметр size задает максимальное количество вводимых символов. Параметрdelimeter определяет символ-ограничитель, при появлении которого ввод символовпрекращается прежде, чем будут введены все size символов. Параметр delimeterимеет аргумент по умолчанию, равный '\n'. В случае ввода символов с клавиатурыэтот символ появляется в потоке ввода при нажатии клавиши
Пример
#include //см. файл Ex01.cpp
int main()
{
char name[80] ;
cout « «Enter your name: »;
cin.getline(name, sizeof(name) — 1);
cout« «Hello » « name « ", how are you?";
return0;
}
Функции,объявленные вSTRING. H
Стандартнаябиблиотека для работы со строками содержит много полезных функций (объявляемыхв STRING.H), разработанных коллективными усилиями многих программистов на С. Вфайлах заголовка STDIO.H и IOS-TREAM.H также имеются прототипы строковыхфункций. Комитетом ANSI/ISO C++ предложен класс для работы со строками. Строкиэтого класса больше похожи на строки в языках Pascal и BASIC. (Мы познакомимсяс классами в День 8, а со строковым классом в День 11.) Этот раздел будетпосвящен рассмотрению некоторых (ни в коей мере не всех) функций, объявленныхв STRING.H.
Некоторыефункции из STRING.H имеют несколько версий. Дополнительные версии этихфункций, имеющих в имени префиксы _f, f или _ работают с указателями типа far.Этих версий вы не встретите в плоской, 32-битной модели памяти компилятораBorland.Присвоение значений строкам
C++поддерживает два способа присвоения значений строкам. Вы можете присвоитьстроковой переменной строковую константу, произведя инициализацию приобъявлении строки. Этот метод прост: требуется операция присваивания истроковая константа.Инициализация строки
Общий метод инициализации строки:
charstringVar[stringSize] = stringLiteral;
Пример
char a3tring[81]= «Borland C++ 5in 21 days»;
char Named = «Rene Kinner»;
Второйспособ присвоить значение строке — это вызвать функцию, которая копируетсодержимое одной строки в другую, — не забывая при этом и нуль-символ. Этафункция называется strcpy. Она предполагает, что копируемая строкаоканчивается символом NUL и прекращает копирование, как только встретит этотсимвол.Функция strcpy
Прототипфункции strcpy таков:
char*strcpy(char *target, const char *source);
Функциякопирует строку source в строку target. Функция предполагает, что целеваястрока имеет размер, достаточный для того, чтобы вместить содержимоестроки-источника.
Пример
charname[41] ;
strcpy(name,«Borland C++ 5»);
Переменнаяname содержит строку «Borland C++ 5».Функция strdup
Функцияstrdup копирует одну строку в другую, при этом отводит необходимое количествопамяти для целевой строки.
Прототипфункции strdup таков:
char*strdup(const char *source);
Функциякопирует строку source и возвращает указатель на строку-копию.
Пример
char*string1 = «Монархия в Испании»;
char*string2;
string2= strdup(string1);
Послетого, как будет отведено необходимое количество памяти для строки string2,строка string1будет скопирована в строку string2. Функция strncpy
Библиотекастроковых функций предлагает также функцию strncpy, копирующую заданноеколичество символов из одной строки в другую.
Прототипфункции strncpy таков:
char* strncpy(char *target, const char *source, size_t num);
Функциякопирует num символов из строки source в строку target. Функция не выполняет ниусечение, ни заполнение строки.
Пример
charstr1[] = «Pascal»;
charstr2[] = «Hello there»;
strcnpy(strl,str2, 5);
Переменнаяstrl содержит строку «Hellol». Заметьте, что символ ‘l’ строки-приемника,следующий за скопированной частью строки, сохранился.Определение длины строки
Приработе со строками часто бывает нужно знать длину строки. Функция strlen
Функцияstrlen возвращает количество символов в строке, в которое не включаетсянуль-терминатор.
Прототипфункции strncpy таков:
size_tstrlen (const char *string) ,
Функция strlenвозвращает длину строки string. size_t — это имя, приписанное типу unsigned intоператором typedef.
Пример
charstr[] = «1234567890»;
size_t i;
i = strlen(str),
Переменной iбудет присвоено значение 10.
Конкатенациястрок
Операцияконкатенации используется достаточно часто, когда новая строка получаетсяобъединением двух или более строк.
Присоединитьодну строку к другой можно функцией strcat.Функция strcat
Конкатенациястрок означает их последовательное присоединение друг к другу.
Прототипфункции strcat таков:
char *strcat(char *target, const char*source);
Функциядобавляет к содержимому целевой строки содержимое строки-источника и возвращаетуказатель на целевую строку. Функция предполагает, что целевая строка можетвместить содержимое объединенной строки.
Пример
char string[81];
strcpy(string, «Turbo»);
strcat (string," C++");
Переменная stringсодержит строку «Turbo C++».Функция strncat
Функция strncatдобавляет к содержимому целевой строки указанное количество символов изстроки-источника.
Прототипфункции strcat :
char *strncat(char *target, const char *source, size_tnum);
Функциядобавляет к содержимому целевой строки num символов из строки-источника ивозвращает указатель на целевую строку.
charstrl[81] = «Hello I am »;
charstr2[41] = «Keith Thompson»;
strncat(strl,str2, 5);
Переменная strlтеперь содержит строку«Hello I am Keith».
Примериспользования функций getline,strlen и strcat в файле List7_4.cpp (исходный код программы STRING.CPP). Программавыполняет следующие задачи:
· Предлагает вам ввести строку; вводне должен превышать 40 символов
· Предлагает вам ввести вторуюстроку; ввод не должен превышать 40 символов
· Выводит число символов,содержащихся в каждой строке
· Присоединяет вторую строку кпервой
· Выводит результат конкатенации
· Выводит длину объединенной строки
· Предлагает вам ввести символ дляпоиска
· Предлагает вам ввести символ длязамены
· Выводит содержимое объединеннойстроки после замены символаСравнение строк
Посколькустроки являются массивами символов, вы не можете применить операцию сравнениядля проверки равенства двух строк. Библиотека функций STRING.Hпредлагает набор функций для сравнения строк. Эти функции сравнивают символыдвух строк, используя для этого ASCII-коды символов. Это функции strcmp, stricmp, strncmpиstrnicmp.
Вообщеговоря, все функции сравнения работают одинаково: возвращают 0, если две строкисовпали, отрицательную величину, если вторая строка больше по величине, иположительное значение, если большей оказалась первая строка.Функция strcmp
Функция strcmp выполняет сравнение двух строк с учетом регистра символов.
Прототипфункции strcmp:
int strcmp(const char *strl, const char *str2);
Функциясравнивает строки strl иstr2. Возвращает в качестве результатасравнения целую величину:
= 0когда strl равна str2;
>0 когда strl больше, чем str2.Пример
char stringl[] = «Borland C++»;
char string2[] = «BORLAND C++»;
i = strcmp(string1, string2);
Впоследнем операторе переменной i присваивается положительное значение, так какstring1 больше string2 (ASCII-коды символов в нижнем регистре большеASCII-кодов символов в верхнем.)Функция stricmp
Функция stricmpвыполняет сравнение двух строк, не учитывая регистра символов.
Прототипфункции stricmp:
int stricmp(const char *strl, const char *str2);
Функциясравнивает строки strl и str2, не делая различия между символами в нижнем иверхнем регистре. Возвращает в качестве результата сравнения целую величину:
= 0 когда strlравна str24
>0 когда strl больше, чем str2.Пример
char string1[] = «Borland C++»;
char string2[] = «BORLAND C++»;
int i = strcmp(string1, string2);
Впоследнем операторе переменнойi присваивается значение 0, так какstring1 и string2 отличаются друг от друга только регистромсимволов.
Функция strncmpвыполняет сравнение заданного количества символов двух строк с учетом регистрасимволов.Функция strncmp
Прототипфункции strncmp:
int strncmp(const char *strl, const char *str2, size_tnum);
Функциясравнивает первые num символов строк strl и str2. Возвращаетв качестве результата сравнения целую величину:
= 0 когда strlравна str2;
>0 когда strl больше, чем str2.Пример
char string1[] = «Borland C++»;
char string2[] = «Borland Pascal»;
i = stricmp(string1,string2, 9);
Впоследнем операторе переменной i присваивается отрицательное значение, так какзначение «Borland С» меньше, чем «BorlandР».
Функцияstrnicmp
Функция strnicmp выполняет сравнение заданного количества символов двух строк без учетарегистра символов.
Прототипфункции strnicmp :
int strnicmp(const char *strl, const char *str2,size_t num);
Функция сравниваетпервые num символов строк strl и str2, не делая различия в регистре символов.Возвращает в качестве результата сравнения целую величину:
= 0 когда strlравна str2;
>0 когда strl больше, чем str2.Пример
char string1[] = «Borland C++»;
char string2[] = «BORLAND Pascal»;
i = strnicmp(string1, string2, 7);
Впоследнем операторе переменной i присваивается значение 0, так как подстрока «Borland» отличается в этих строках только регистром.
Рассмотримпример программы, в которой применяются функции сравнения строк. Программа излистинга 5 объявляет массив строк и присваивает им значения. Затем программавыводит исходный массив, сортирует его и выводит значения строкотсортированного массива. |(см. List7_5.cpp — Исходный текст программыSTRING2.CPP)Преобразование строкФункция strlwr
Прототипфункции strlwr:
char*strlwr (char *source)
Функцияпреобразует символы верхнего регистра в символы нижнего регистра в строке source.Другие символы не затрагиваются. Функция возвращает указатель на строку source.Пример
char str[] = «HELLO THERE»;
strlwr(str);
Переменная strтеперь содержит строку«hello there».Функция strupr
Прототипфункции strupr:
char* strupr(char *source)
Функцияпреобразует символы нижнего регистра в символы верхнего регистра в строке source.Другие символы не затрагиваются. Функция возвращает указатель на строку source.Пример
char str[] =«Borland C++»;
strupr(str);
Переменная strтеперь содержит строку «BORLAND С ++».Обращение строк
Библиотека STRING.H предлагает функциюstrrev для записи символов в строке вобратном порядке.Функция strrev
Прототипфункции strrev:
char*strrev(char *str)
Функцияобращает порядок символов в строке str и возвращает указатель на строку str. char str[] = «Hello»;
strrev(str);
cout« str;
Будетвыведено «olleH».
Рассмотримпрограмму, которая манипулирует символами в строке. List7_6.cpp показывает исходный текст программы STRING3.CPP. Программа выполняет следующие задачи:
· Запрашивает у вас ввод строки
· Отображает ваш ввод
· Выводит вашу строку в нижнемрегистре
· Выводит вашу строку в верхнемрегистре
· Отображает символы, которые выввели, в обратном порядке
· Выводит сообщение, что ваш ввод несодержит символов верхнего регистра, если это так
· Выводит сообщение, что ваш ввод несодержит символов в нижнем регистре, если это так
· Выводит сообщение, что ваша строкасимметрична, если это такПоиск символов
Библиотека STRING.H предлагает ряд функций для поиска символов в строках. Это функции strchr, strrchr, strspn, strcspnиstrpbrk. Они осуществляют поиск в строках символов и простыхсимвольных шаблонов.Функция strchr
Функция strchrопределяет первое вхождение символа в строку.
Прототипфункции strchr:
char* strchr(const char *target, int c)
Функциянаходит первое вхождение символа с в строку target. Функциявозвращает указатель на символ в строке target, которыйсоответствует заданному образцу с. Если символ с в строке не обнаруживается,функция возвращает 0.
Пример
charstr[81] = «Borland C++»;
char*strPtr;
strPtr= strchr(str, '+');
Указатель strPtrтеперь содержит адрес подстроки "++" в строке str. Функция strrchr
Функция strrchrопределяет последнее вхождение символа в строке.
Прототипфункции strrchr:
char* strrchr(const char *target, int c)
Функциянаходит последнее вхождение символа с в строку target. Функциявозвращает указатель на символ в строке target, которыйсоответствует заданному образцу с. Если символ с в строке не обнаруживается,функция возвращает 0.Пример
char str[81] = «Borland C++is here»;
char* strPtr;
strPtr = strrchr(str, '+');
Указатель strPtrтеперь указывает на подстроку "+ is here " в строке str.Функция Strspn
Функция strspnвозвращает число символов с начала строки, совпадающих с любым символом изшаблона.
Прототипдля функции strspn:
size_t strspn(const char *target, const char *pattern)
Функция strspnвозвращает число символов от начала строки target, совпадающихс любым символом из шаблонаpattern.Пример
char str[] = «Borland C++ 5»;
char substr[] = «narlBod»;
int index;
index = strspn(str, substr);
Этотоператор присваивает 8 переменнойindex, потому что первые восемь символовиз str содержатся в подстрокеsubstr.Функция strcspn
Функция strcspnпросматривает строку и выдает число первых символов в строке, которые несодержатся в шаблоне.
Прототипфункции strcspn:
size_t strcspn(const char* str1, const char* str2)
Функция strcspnпросматривает строку str1 и выдает длину подстроки, отсчитываемой с началастроки, символы которой полностью отсутствуют в строке str2.Пример
char strng[] = «The rain in Spain»;
int i = strcspn(strng, "in");
Этотпример возвращает 3 (расположение первого пробела в строке strng) переменной i.Функция strpbrk
Функция strpbrkпросматривает строку и определяет первое вхождение любого символа из образца.
Прототипфункции strpbrk:
char* strpbrk(const char* target, const char* pattern)
Функция strpbrkищет в строке target первое вхождение любого символа из образца pattern. Если символы из образца не содержатся в строке, функция возвращает 0.Пример
char *str = «Hello there how are you»;
char *substr = «hr»;
char *ptr;
ptr = strpbrk(str, substr);
cout « ptr « endl;
Выувидите на экране строку«here how are you», потому что 'h' встречаетсяв строке str раньше, чем 'r'.Поиск строк
Библиотека функций STRING.H предлагает для поиска подстроки встроке функциюstrstr.Функция strstr
Прототип функции strstr:
char* strstr(const char *str, const char *substr);
Функцияищет в строке str первое вхождение подстроки substr. Функциявозвращает указатель на первый символ найденной в строке str подстроки substr.Если строка substr не обнаружена в строке str, функция возвращает 0.Пример
char str[] = «Hello there! how are you»;
char substr[] = «how»;
char *ptr;
ptr = strstr (str, substr);
cout « ptr « endl;
Этоприведет к выводу строки«how are you», поскольку встроке str , была обнаружена подстрока «how».Указатель ptr содержит адрес остатка первоначальной строки, начинающегося сподстроки «how».Функция strtok
Библиотекафункций для работы со строками имеет функцию strtok, котораядает вам возможность разбить строку на подстроки на основании заданного наборасимволов-ограничителей.
Подстрокииногда называются лексемами.
Прототипфункции strtok:
char* strtok(char *target, const char*delimiters);
Функцияразбивает строку на лексемы, согласно символам-ограничителям, заданным впараметре delimeters. В следующем примере показано, как работать с этойфункцией и как получать лексемы, на которые была разбита строка. Функция strtokвводит символ '\0' после каждой лексемы. (Опять же не забудьте сохранить копиювашей строки в другой строковой переменной.)Пример
#include // см. файл Ex02.cpp
#include
int main()
{
char *str = "(Base_Cost +Profit) * Margin";
char *tkn = "+*()";
char *ptr = str;
printf("%s\n", str);
// Первый вызов функции
ptr = strtok(str, tkn);
printf(«Лексемы этой строки:%s», ptr);
while (ptr)
{
// Первый аргумент должен быть равен нулю
if ((ptr = strtok(0, tkn)) != 0)
printf (",%s", ptr);
}
printf("\n");
return 0;
}
Врезультате выполнения этой программы на экран выводятся следующие строки:
(Base_Cost + Profit) * Margin
Лексемыэтой строки: Base_Cost,Profit, Margin
Рассмотримпример программы поиска символов и строк. Листинг 7 (List7_7.cpp) содержит исходный текст программы STRING4.CPP.Программа выполняет следующие задачи:
· Запрашивает у вас ввод основнойстроки
· Запрашивает строку поиска
· Предлагает вам ввести символпоиска
· Выводит линейку цифр и основнуюстроку
· Выводит номер символа в основнойстроке, с которого начинается строка поиска *
· Выводит номер символа в основнойстроке, совпавшего с символом поиска.Основы объектно-ориентированногопрограммирования СИНТАКСИС ОСНОВНЫХ КОНСТРУКЦИЙОбъявление базовых классов
В С++ мы имеем возможность объявлятьклассы, которые инкапсулируют элементы-данные и функции-элементы. Эти функцииизменяют и позволяют обращаться к значениям данных-элементов и выполняют другиезадачи.
Базовый класс
Базовый класс определяется следующимобразом (синтаксис):
class className
{
private:
protected:
public:
};
Пример 1:
class point
{
protected:
double х;
double у;
public:
point(double xVal, double yVal);
double getX();
double getY();
void assign(double xVal, doubleyVal);
point& assign(point &pt);
};
Разделыкласса
Классы С++ имеют три различных уровнядоступа к своим элементам — как к данным, так и к функциям:
- Закрытые(частные) элементы
- Защищенныеэлементы
- Открытыеэлементы
К данным взакрытом разделе имеют доступ только функции-элементы класса.
Классам-потомкамзапрещен доступ к закрытым данным своих 6азовых классов.
К данным взащищенной секции имеют доступ функции-элементы класса и классов-потомков.Данные из открытой секции находятся в области видимости функций-элементовкласса, функций-элементов классов-потомков, и вообще доступны кому угодно.
Существуют следующие правила дляразделов класса:
1. Разделы могутпоявляться в любом порядке.
2. Один и тот жераздел можно определять несколько раз.
3. Если неопределен ни один раздел, компилятор (по умолчанию) объявляет все элементызакрытыми.
4. Помещатьданные-элементы в открытый раздел следует только в том случае, если в этом естьнеобходимость, например, если это упрощает вашу задачу. Обычноэлементы-данные помещаются в защищенный раздел, чтобы к ним имели доступфункции-элементы классов-потомков.
5. Используйте дляизменения значений данных и доступа к ним функции-элементы. При использованиифункции вы можете осуществлять проверку данных и, если нужно, изменять другиеданные.
6. Класс можетиметь несколько конструкторов.
7. Класс можетиметь только один деструктор, который должен объявляться в открытом разделекласса.
8. Функции-элементы(в том числе конструкторы и деструкторы), состоящие из нескольких операторов,должны определяться вне объявления класса. Определение функции можетсодержаться в том же файле, в котором определяется класс. Это напоминаетпорядок работы с обычными функциями: задание прототипа и определение функции.
Конструкторы являются специфическим типом функций-элементов, типвозвращаемого значения для которых не указывается, а имя должно совпадать сименем класса-хозяина. Вызываются они при создании нового представителя класса.Деструктор вызывается для разрушения представителя класса.
При определении функции-элемента выдолжны указать ее имя и имя ее класса. Сначала вы должны Сначала необходимоуказать имя класса (т.н. квалификатор), а затем, через два двоеточия (::),имя функции. В качестве примера рассмотрим такой класс:
class point
{
protected:
double x;
double y;
public:
point(doublexVal, double yVal);
doublegetX();
// другие функции-элементы
};
Определения конструктора ифункций-элементов должны выглядеть так
point::point(double xVal, double yVal)
{
// операторы
}
double point::getX()
{
// операторы
}
После того, как вы объявили класс, вы можете использовать имя класса в качествеспецификатора типа данных при объявлении представителей класса. Синтаксисобъявления тот же, что и при объявлении переменной.
В листинге8.1 приведенисходный текст программы RECT.CPP. Программа предлагает вам ввести длинуи ширину прямоугольника (в данном примере прямоугольник является объектом).Затем программа выводит значения длины, ширины и площади определенного вамипрямоугольника. Конструкторы
Конструкторы и деструкторы в С++ вызываются автоматически, что гарантируетправильное создание и разрушение объектов класса.
Общий вид (синтаксис) объявленияконструктора:
class className
{
public:
className(); //конструктор по умолчанию
className(const className &c); // конструктор копии
className(); // остальные конструкторы
};
Пример 2:
// Замечание: Здесь только объявлениекласса без описания объявленных
// функций-параметров
class point
{
protected:
double x;
double y;
public:
point();
point(double xVal, double yVal);
point(const point &pt);
double getX();
double getY();
void assign(double xVal, doubleyVal);
point& assign(point &pt);
};
int main()
{
point p1;
point p2(10, 20);
point p3(p2);
p1.assign(p2);
cout
cout
cout
return 0;
}
Конструктор копии создает объект класса, копируя при этом данные изсуществующего объекта класса.
В С++ имеются следующие особенности иправила работы с конструкторами:
1. Имя конструктора класса должносовпадать с именем класса.
2. Нельзяопределять тип возвращаемого значения для конструктора, даже тип void.
3. Класс можетиметь несколько конструкторов или не иметь их совсем.
4. Конструктором по умолчаниюявляется конструктор, не имеющий параметров, или конструктор, у которого всепараметры имеют значения по умолчанию.
Рассмотрим два примера с фрагментамиобъявления конструкторов.
// класс с конструктором без параметров
class point1
{
protected:
double x;
double y;
public:
point1();
// другие функции-элементы
};
// конструктор класса имеет параметры созначениями по умолчанию
class point2
{
protected:
double x;
double y;
public:
point2(double xVal = 0, double yVal =0);
// другие функции-элементы
};
5. Конструкторкопии создает объект класса на основе существующего объекта.
Например:
class point
{
protected:
double x;
double y;
public:
point();
point(double xVal = 0, double yVal =0);
point(const point &pt);
// другие функции-элементы
};
6. Объявление объекта класса, котороеможет содержать параметры и, в качестве параметра, имя уже существующегообъекта, влечет за собой вызов конструктора. Но какой из конструкторов будетиспользоваться в каждом конкретном случае? Ответ зависит от того, как многоконструкторов вы объявили и с какими аргументами вы объявляете объект класса.Например, рассмотрим следующие объявления объектов последней версии классаpoint:
point p1; //применяется конструктор по умолчанию
point p2(1.1, 1.3); // используетсявторой по счету конструктор
point p3(p2); // используется конструкторкопии
Поскольку объект p1 объявляется безпараметров, компилятор использует
конструктор по умолчанию. Объект p2объявляется с двумя вещественными
аргументами, поэтому компилятор вызоветвторой конструктор. Объект p3
при объявлении имеет параметром объектp2, поэтому компилятор вызовет
конструктор копии, чтобы создатьновый объект из объекта p2.
ВНИМАНИЕ:
Определяйте конструкторкопии, особенно для классов, моделирующих динамические структуры данных.Конструкторы копии должны выполнять т.н. глубокое копирование, котороеподразумевает копирование динамических данных. Если вы не определиликонструктор копии, компилятор создаст конструктор копии по умолчанию, которыйбудет создавать поверхностную копию, копируя только элементы-данные. При этомбудет скопировано содержимое данных-элементов, содержащих указатели на другие,данные, но сами эти данные скопированы не будут.
Не полагайтесь наповерхностный конструктор копии для классов имеющих
данные-указатели.Деструкторы
Классы С++ могут содержатьдеструкторы, которые автоматически разрушают объекты класса.
Общий синтаксис объявлениядеструктора:
class className
{
public:
className(); //конструктор по умолчанию
// другие конструкторы
~className(); //объявление деструктора
// другие функции-элементы
};
Пример 3 на синтаксис обявлениядеструктора:
class String
{
protected:
char *str;
int len;
public:
String();
String(const String& s);
~String();
// другие функции-элементы
};
Деструкторы в С++ имеют следующиеособенности и подчиняются следующим правилам:
1. Имя деструкторадолжно начинаться со знака тильды (~), за которым должно следовать имя класса.
2. Нельзяопределять тип возвращаемого значения, даже тип void.
3. Класс можетиметь только один деструктор или ни одного. В последнем случае компиляторсоздаст деструктор по умолчанию.
4. Деструктор недолжен иметь параметров.
5. Исполняющаясистема автоматически вызывает деструктор класса, когда объект класса выходитза пределы области действия и может быть удален, или удаляется явным образом.
(см. LIST8-2.CPP)Объявление иерархии классов
Производныйкласс
Общая форма (синтаксис) объявленияпроизводного класса:
class classname: [] parentClass
{
private:
protected:
public:
};
Пример 4 объявления класса Rectangleи класса-потомка Box:
class Rectangle
{
protected:
double length;
double width;
public:
Rectangle(double len, double wide);
double getLength() const;
double getWidth() const;
double assign(double len, doublewide);
double calcArea();
};
class Вох: public Rectangle
{
protected:
double height;
public:
Box(double len, double wide, doubleheight);
double getHeight () const;
assign(double len, double wide,double height);
double calcVolume();
};
(см. LIST8-3.CPP)Виртуальные функции
Мы уже упоминали о полиморфизме — важной особенности объектно-
ориентированного программирования.Рассмотрим следующий пример (6):
#include
class X
{
public:
double A(double x) { return x * x; }
double B(double x) { return A(x) / 2;}
};
class Y: public X
{
public:
double A(double x) { return x * x *x; }
};
int main ()
{
Y y;
cout
return 0;
}
В классе X объявляются функции A и B,причем функция B вызывает функцию А. Класс Y, потомок класса X, наследуетфункцию B, но переопределяет функцию A. Цель этого примера — демонстрацияполиморфного поведения класса Y. Мы должны получить следующий результат: вызовнаследуемой функции X::B должен привести к вызову функции Y::A. Что же выдастнам наша программа? Ответом будет 4.5, а не 13.5! В чем же дело? Почемукомпилятор разрешил выражение y.B(3) как вызов наследуемой функции X::B,которая, в свою очередь, вызывает X::A, а не функцию Y::A, что должно было быпроизойти в случае полиморфной реакции класса?
Виртуальные функции объявляютсяследующим образом (синтаксис):
class className1
{
// функции-элементы
virtual returnTypefunctionName();
};
class className2: public className1
{
// функции-элементы
virtual returnTypefunctionName();
};
Пример 7, показывающий, как при помощивиртуальных функций можно реализовать полиморфное поведение классов X и Y:
#include
class X
{
public:
virtual double A(double x) { return x* x; }
double B (double x) { return A(x) /2; }
};
class Y: public X
{
public:
virtual double A(double x) { return x* x * x; }
};
main()
{
Y y;
cout
return 0;
}
Этот пример выведет вам правильноезначение 13.5, потому что в результате вызова наследуемой функции X::B,вызывающей функцию A, в качестве функции A во время выполнения программы будетиспользована замещающая функция Y::A.
*** Правило виртуальной функции ***
Правило виртуальной функции гласит:
«Виртуальная однажды — виртуальна всегда».
Это означает следующее. Если выобъявили функцию как виртуальную в некотором классе, то в классах-потомках,переопределяющих эту функцию, она также будет виртуальной, но только если онаимеет тот же список параметров. Если переопределенная функция в классе-потомкеимеет другой список параметров, то ее версия из базового класса будетнедоступна классу-потомку (и всем его потомкам). Это может показатьсянеудобным, но только на первый взгляд.
Правило это справедливо и для всехязыков объектно-ориентированного программирования, поддерживающих виртуальныефункции, но не допускающих перегрузку функций. В С++ положение несколько иное.Вы можете объявлять невиртуальные перегруженные функции, совпадающие по имени свиртуальными функциями, но имеющие другой список параметров. И, кроме того, выне можете наследовать невиртуальные функции, имя которых совпадает свиртуальными функциями. Рассмотрим пример 8, иллюстрирующий сказанное.
#include
class A
{
public:
A() {}
virtual void foo(char c)
{ cout
};
class B: public A
{
public:
B() {}
void foo(const char* s)
{ cout
void foo(int i)
{ cout
virtual void foo(char c)
{ cout
};
class C: public B
{
public:
C() {}
void foo(const char* s)
{ cout
void foo(double x)
{ cout
virtual void foo(char c)
{ cout
};
int main()
{
A Aobj;
B Bobj;
C Cobj;
Aobj.foo('A');
Bobj.foo('B');
Bobj.foo(10);
Bobj.foo(«Bobj»);
Cobj.foo('C');
Cobj.foo(144.123);
Cobj.foo(«Cobj»);
return 0;
}
В этом примере вводятся три класса — A, B и C — образующих линейную иерархию наследования. В классе A объявляетсявиртуальная функция foo(char).
Класс B объявляет свою версиювиртуальной функции foo(char), но, кроме того, в классе B объявляютсяневиртуальные перегруженные функции foo(const char*) и foo(int). Класс Cобъявляет свою версию виртуальной функции foo(char) и невиртуальныеперегруженные функции foo(const char*) и foo(double). Обратите внимание на то,что в классе C приходится заново объявлять функцию foo(const char*), посколькув данном случае функция-элемент B::foo(const char*) не наследуется. Такимобразом, в С++ схема наследования отличается от обычной для случая виртуальнойи перегруженных функций с одинаковым именем. В функции main объявляются объектыдля всех трех классов и вызываются различные версии функции-элемента foo.Дружественные функции
В С++ функции-элементы имеют доступко всем данным-элементам своего класса. Кроме этого, С++ предусматривает такуювозможность еще и для дружественных функций. Объявление дружественной функциипроизводится в объявлении класса и начинается с ключевого слова friend. Кроменаличия спецификатора friend, объявление дружественной функции совпадает собъявлением функции-элемента, однако прямого доступа к классу дружественная функцияне имеет, поскольку для этого необходим скрытый указатель this, который ейнедоступен. Но если вы передаете такой функции указатель на объектдружественного класса, функция будет иметь доступ к его элементам. Когда выопределяете дружественную функцию вне объявления дружественного ей класса, вамне нужно в определении указывать имя класса. Дружественной называется обычнаяфункция, которой открыт доступ ко всем элементам-данным одного или несколькихклассов.
Общий вид (синтаксис) объявлениядружественной функции следующий:
class className
{
public:
className();
// другие конструкторы
friend returnTypefriendFunction();
};
Пример 9:
class String
{
protected:
char *str;
int len;
public:
String();
~String();
// другие функции-элементы
friend String& append(String&str1, String &str2);
friend String& append(const char*str1, String &str2);
friend String& append(String&str1, const char* str2);
};
Дружественные функции могут решатьзадачи, которые при помощи
функций-элементов решаются с трудом,неуклюже или не могут быть решены вообще.
Рассмотрим простой примериспользования дружественных функций. Текст программы FRIEND.CPP представлен влистинге 8.5. Программа следит за памятью, отведенной для хранения массивасимволов. Эта программа — первый шаг к созданию класса string.Операции и дружественные операции
Последняя программа использовалафункции-элементы и дружественную функцию, которые реализовали действия,выполняемые в стандартных типах с помощью операций вроде = и +. Подход типичендля языков C и Pascal, потому что эти языки не поддерживают определяемыепользователем операции. В отличии от них C++ позволяет вам объявлять операции идружественные операции. Эти операции включают в себя: +, -, *, /, %, ==, !=,=, >, +=, -=, *=, /=, %=, [],
(), >. Обратитесь кописанию языка C++, где обсуждаются детали определения этих операций. С++трактует операции и дружественные операции как специальный типфункций-элементов и дружественных функций.
Общий синтаксис для объявленияопераций и дружественных операций:
class className
{
public:
// конструкторы и деструктор
// функции-элементы
// унарная операция
returnType operator operatorSymbol();
// бинарная операция
returnType operatoroperatorSymbol(operand);
// унарная дружественная операция
friend returnType operatoroperatorSymbol(operand);
// бинарная дружественная операция
friend returnType operatoroperatorSymbol(firstOperand, secondOperand);
};
Пример 10:
class String
{
protected:
char *str;
int num;
public:
String();
~String();
// другие функции-элементы
// операция присваивания
String& operator =(String&s);
String& operator +=(String&s);
// операции конкатенации
friend String& operator+(String& s1, String& s2);
friend String& operator +(constchar* s1, String& s2);
friend String& operator+(String& s1, const char* s2);
// операции отношения
friend int operator >(String&s1, String& s2);
friend int operator =>(String&s1, String& s2);
friend int operator
friend int operator
friend int operator ==(String&s1, String& s2);
friend int operator !=(String&sl, String& s2);
};
Код, который вы пишете, будетиспользовать операции и дружественные операции точно так же, как ипредопределенные операции. Следовательно, вы можете создавать операции, чтобыподдерживать действия над классами, моделирующими, например, комплексные числа,строки, векторы и матрицы. Эти операции дают возможность вам записыватьвыражения в более привычной форме, чем использование вызовов функций.Виртуальные функции
Мы ужеупоминали о полиморфизме — важной особенности объектно-ориентированногопрограммирования. Рассмотрим следующий пример (6):
#include
class X
{
public:
doubleA(double x) { return x * x; }
doubleB(double x) { return A(x) / 2; }
};
class Y: publicX
{
public:
doubleA(double x) { return x * x * x; }
};
int main ()
{
Y y;
cout
return 0;
}
В классе Xобъявляются функции A и B, причем функция B вызывает функцию А. Класс Y,потомок класса X, наследует функцию B, но переопределяет функцию A. Цель этогопримера — демонстрация полиморфного поведения класса Y. Мы должны получитьследующий результат: вызов наследуемой функции X::B должен привести к вызовуфункции Y::A. Что же выдаст нам наша программа? Ответом будет 4.5, а не 13.5! Вчем же дело? Почему компилятор разрешил выражение y.B(3) как вызов наследуемойфункции X::B, которая, в свою очередь, вызывает X::A, а не функцию Y::A, чтодолжно было бы произойти в случае полиморфной реакции класса?
Виртуальныефункции объявляются следующим образом (синтаксис):
class className1
{
//функции-элементы
virtual returnTypefunctionName();
};
class className2: public className1
{
//функции-элементы
virtualreturnType functionName();
};
Пример 7,показывающий, как при помощи виртуальных функций можно реализовать полиморфноеповедение классов X и Y:
#include
class X
{
public:
virtualdouble A(double x) { return x * x; }
double B(double x) { return A(x) / 2; }
};
class Y: publicX
{
public:
virtualdouble A(double x) { return x * x * x; }
};
main()
{
Y y;
cout
return 0;
}
Этот примервыведет вам правильное значение 13.5, потому что в результате вызованаследуемой функции X::B, вызывающей функцию A, в качестве функции A во времявыполнения программы будет использована замещающая функция Y::A.Правило виртуальной функции
Правиловиртуальной функции гласит:
«Виртуальная однажды — виртуальна всегда».
Это означаетследующее. Если вы объявили функцию как виртуальную в некотором классе, то вклассах-потомках, переопределяющих эту функцию, она также будет виртуальной, нотолько если она имеет тот же список параметров. Если переопределенная функция вклассе-потомке имеет другой список параметров, то ее версия из базового классабудет недоступна классу-потомку (и всем его потомкам). Это может показатьсянеудобным, но только на первый згляд.
Правило этосправедливо и для всех языков объектно-ориентированного программирования,поддерживающих виртуальные функции, но не допускающих перегрузку функций. В С++положение несколько иное. Вы можете объявлять невиртуальные перегруженныефункции, совпадающие по имени с виртуальными функциями, но имеющие другойсписок параметров. И, кроме того, вы не можете наследовать невиртуальныефункции, имя которых совпадает с виртуальными функциями.
Рассмотримпример 8, иллюстрирующий сказанное.
#include
class A
{
public:
A() {}
virtual voidfoo(char c)
{ cout
};
class B: publicA
{
public:
B() {}
void foo(constchar* s)
{ cout
void foo(inti)
{ cout
virtual voidfoo(char c)
{ cout
};
class C: publicB
{
public:
C() {}
voidfoo(const char* s)
{ cout
voidfoo(double x)
{ cout
virtual voidfoo(char c)
{ cout
};
int main()
{
A Aobj;
B Bobj;
C Cobj;
Aobj.foo('A');
Bobj.foo('B');
Bobj.foo(10);
Bobj.foo(«Bobj»);
Cobj.foo('C');
Cobj.foo(144.123);
Cobj.foo(«Cobj»);
return 0;
}
В этомпримере вводятся три класса — A, B и C — образующих линейную иерархию наследования.В классе A объявляется виртуальная функция foo(char).
Класс Bобъявляет свою версию виртуальной функции foo(char), но, кроме того, в классе Bобъявляются невиртуальные перегруженные функции foo(const char*) и foo(int).Класс C объявляет свою версию виртуальной функции foo(char) и невиртуальныеперегруженные функции foo(const char*) и foo(double). Обратите внимание на то,что в классе C приходится заново объявлять функцию foo(const char*), посколькув данном случае функция-элемент B::foo(const char*) не наследуется. Такимобразом, в С++ схема наследования отличается от обычной для случая виртуальнойи перегруженных функций с одинаковым именем. В функции main объявляются объектыдля всех трех классов и вызываются различные версии функции-элемента foo.
Дружественныефункции
В С++функции-элементы имеют доступ ко всем данным-элементам своего класса. Кромеэтого, С++ предусматривает такую возможность еще и для дружественных функций.Объявление дружественной функции производится в объявлении класса и начинаетсяс ключевого слова friend. Кроме наличия спецификатора friend, объявлениедружественной функции совпадает с объявлением функции-элемента, однако прямогодоступа к классу дружественная функция не имеет, поскольку для этого необходимскрытый указатель this, который ей недоступен. Но если вы передаете такойфункции указатель на объект дружественного класса, функция будет иметь доступ кего элементам. Когда вы определяете дружественную функцию вне объявлениядружественного ей класса, вам не нужно в определении указывать имя класса.Дружественной называется обычная функция, которой открыт доступ ко всемэлементам-данным одного или нескольких классов.
Общий вид(синтаксис) объявления дружественной функции следующий:
class className
{
public:
className();
// другиеконструкторы
friendreturnType friendFunction();
};
Пример 9:
class String
{
protected:
char *str;
int len;
public:
String();
~String();
// другиефункции-элементы
friendString& append(String &str1, String &str2);
friendString& append(const char* str1, String &str2);
friendString& append(String &str1, const char* str2);
};
Дружественныефункции могут решать задачи, которые при помощи
функций-элементоврешаются с трудом, неуклюже или не могут быть решены вообще.
Рассмотримпростой пример использования дружественных функций.
Текст программыFRIEND.CPP представлен в листинге 8.5. Программа следит за памятью, отведеннойдля хранения массива символов. Эта программа — первый шаг к созданию классаstring.Операции и дружественные операции
Последняяпрограмма использовала функции-элементы и дружественную функцию, которыереализовали действия, выполняемые в стандартных типах с помощью операций вроде= и +. Подход типичен для языков C и Pascal, потому что эти языки неподдерживают определяемые пользователем операции. В отличии от них C++позволяет вам объявлять операции и дружественные операции. Эти операциивключают в себя: +, -, *, /, %, ==, !=, =, >, +=, -=, *=,/=, %=, [], (), >. Обратитесь к описанию языка C++, гдеобсуждаются детали определения этих операций. С++ трактует операции идружественные операции как специальный тип функций-элементов и дружественныхфункций.
Общийсинтаксис для объявления операций и дружественных операций:
class className
{
public:
//конструкторы и деструктор
//функции-элементы
// унарнаяоперация
returnTypeoperator operatorSymbol();
// бинарнаяоперация
returnTypeoperator operatorSymbol(operand);
// унарнаядружественная операция
friendreturnType operator operatorSymbol(operand);
// бинарнаядружественная операция
friendreturnType operator operatorSymbol(firstOperand, secondOperand);
};
Пример 10:
class String
{
protected:
char *str;
int num;
public:
String();
~String();
// другиефункции-элементы
// операцияприсваивания
String&operator =(String& s);
String&operator +=(String& s);
// операцииконкатенации
friendString& operator +(String& s1, String& s2);
friend String&operator +(const char* s1, String& s2);
friendString& operator +(String& s1, const char* s2);
// операцииотношения
friend intoperator >(String& s1, String& s2);
friend intoperator =>(String& s1, String& s2);
friend intoperator
friend intoperator
friend intoperator ==(String& s1, String& s2);
friend intoperator !=(String& sl, String& s2);
};
Код, которыйвы пишете, будет использовать операции и дружественные операции точно так же,как и предопределенные операции. Следовательно, вы можете создавать операции,чтобы поддерживать действия над классами, моделирующими, например, комплексныечисла, строки, векторы и матрицы.
Эти операциидают возможность вам записывать выражения в более привычной форме, чемиспользование вызовов функций.ИСХОДНЫЕ ТЕКСТЫ ПРИМЕРОВ
(Листинг 8.1.исходный текст программы RECT.CPP
// ПрограммаC++, иллюстрирующая использование класса.
// Программамоделирует прямоугольник.)
// Листинг 8.2.Исходный текст программы ARRAY.CPP
// Программадемонстрируюет использование конструкторов и деструкторов:
// — создаетдинамический массив (объект),
// — присваивает значения элементам динамического массива,
// — выводитзначения элементов динамического массива,
// — удаляетдинамический массив.
// Листинг 8.3.Исходный текст программы CIRCLE.CPP
// Простойпример иерархии классов.
// Листинг 8.4.Исходный текст программы VIRTUAL.CPP
// Программадемонстрирует использование виртуальных функций
// для моделированияквадратов и прямоугольников и вывода их
// размеров иплощадиВОПРОСЫ И ОТВЕТЫ
Что случится,если я объявлю конструктор по умолчанию, конструктор копии и другиеконструкторы в защищенной области?
Программы,использующие ваш класс, не смогут создавать объекты этого класса. Однако онисмогут объявлять классы-потомки с открытыми конструкторами.
Могу я задатьцепочку вызовов функций-элементов ?
Да, можете,только если указанные в цепочке функции-элементы возвращают ссылку на тот жесамый класс. Например, если в классе String объявлены следующиефункции-элементы:
String&upperCase();
String&reverse();
StringamapChar(char find, char replace);
вы можетенаписать следующий оператор обработки объекта класса
String:
s.upperCase().reverse().mapChar('', '+');
Что можетслучиться, если класс полагается на конструктор копии, созданный компилятором,и при этом класс использует указатели в качестве элементов-данных?
Этиконструкторы выполняют побитовую копию объекта. Следовательно, соответствующиеэлементы-указатели в обоих объектах будут ссылаться на те же самые динамическиеданные. Этот способ создания копии объекта — верный путь к различнымнеприятностям.
Могу ли ясоздавать массив объектов?
Да, можете.Однако соответствующий класс должен иметь заданный по умолчанию конструктор.При создании массива используется ранее упомянутый конструктор.
Могу ли яиспользовать указатель при создании объекта класса?
Да, можете,но в этом случае вы должны использовать операции new и delete, чтобыраспределять и освобождать память для данного объекта.
Вот пример,использующий класс Complex. Не забудьте, что для обращения к элементам классовили структур используется операция ->, если вы ссылаетесь на них при помощиуказателей.
Complex *pC;
pC = newComplex;
// операции собъектом, к которому обращаются по указателю pC
delete pC;
или
Complex *pC =new Complex;
// операции собъектом, к которому обращаются по указателю pC
delete pC;Контрольные вопросы
1. Найдитеошибку в следующем объявлении класса:
class String{
char *str;
unsignedlen;
String ();
String(const String& s);
String(unsigned size, char = ' ');
String(unsigned size);
String& assign(String& s);
~String();
unsignedgetLen() const;
char*getString();
// другиефункции-элементы
};
2. Найдитеошибку в следующем объявлении класса:
class String{
protected:
char *str;
unsignedlen;
public:
String();
String(const char* s);
String(const String& s);
String(unsigned size, char = ' ');
String(unsigned size);
~String();
// другиефункции-элементы
3. Верно илинет? Следующий оператор, который создает объект s класса String, объявленногоранее, является правильным:
s =String(«Hello Borland C++»);
4. Если впрограмме OPERATOR.CPP вы следующим образом измените объявления объектов, будетли программа компилироваться без ошибок?
String s1 =String(«Kevin»);
String s2 =String(" Нау");
String s3 =s1;ФАЙЛОВЫЕ ОПЕРАЦИИ ВВОДА/ВЫВОДА
Сегодняшний урок посвящен файловым операциямввода/вывода с использованием библиотеки управления потоками C++. Увас есть две возможности: либо использовать функции файловоговвода/вывода, описанные в заголовочном файле STDIO.H, либо функцииstream-библиотеки C++. Каждая из этих библиотек имеет множество мощных и удобныхфункций. Сегодня будут представлены основные операторы, которые позволят вамчитать и записывать данные в файл. Вы изучите следующие темы:
Стандартные функции потоков ввода/вывода
- Последовательный ввод/вывод потокас текстовой информацией
- Последовательный ввод/выводдвоичных данных
- Прямой доступ к потоку двоичныхданныхStream-библиотека C++
Stream-библиотека (известная также как библиотекаiostream) выполнена в виде иерархии классов, которые описаны в несколькихзаголовочных файлах. Файл IOSTREAM.H, используемый до сих пор, — это только одиниз них. Другой, который будет интересен в этой главе, — FSTREAM.H. ФайлIOSTREAM.H поддерживает основные классы для ввода/вывода потока. Файл FSTREAM.H содержитопределения для основных классов файлового ввода/вывода.
Существуют дополнительные файлы библиотекиввода/вывода, в которых имеются более специализированные функции ввода/вывода.ОБЩИЕ ФУНКЦИИ ПОТОКОВОГО ВВОДА/ВЫВОДА
В этом разделе представлены функции-элементыввода/вывода, являющиеся общими как для последовательного, так и для прямогодоступа. Эти функции включают open, close, good и fail в дополнение коперации!.. Функция open открывает файловый поток для ввода,вывода, добавления, а также для ввода и вывода. Эта функция позволяетуказывать тип данных, с которыми вы собираетесь работать: двоичные илитекстовые.
При работе с файловым вводом/выводом очень важно знатьразличие между текстовым и двоичным режимами. Текстовый режимпредназначен для текстовых файлов, в которых имеются строки обычного текста.Двоичный режим используется для любых других и особенно для файлов,которые сохраняются в форматах, неудобных для чтения человеком.
Существуют некоторые особые тонкости, связанные сфайлами текстового режима, на которые следует обратить особое внимание изапомнить. Первая из них — символ EOF (26 в коде ASCII или Ctrl+Z) — представляет собой метку (символ) конца файла. В текстовом режиме, гдевстречается символ EOF, система C++ низкого уровня автоматически продвигаетсяк концу файла; вы ничего не можете прочитать после специальногосимвола. Это может вызвать проблемы, если такой специальный символ окажется всередине файла.
Другая особенность текстового режима заключается втом, как интерпретируются строки текстового файла. Каждая строка заканчивается последовательностьюконца строки (EOL). На компьютерах PC и совместимых с нимиEOL-последовательность представлена двумя символами кода ASCII: CR (13 в кодеASCII или Ctrl+M) и LF (10 в коде ASCII или Ctrl+J). ЭтаCRLF-последовательность используется функциями чтения и записи текстовой строки, которыеавтоматически, вставляют ее в файл или удаляют из него. Заметьте, чтона большинстве других, систем (UNIX и Macintosh) EOF просто являетсясимволом LF.Функция-компонент open
Прототип функции open
voidopen (const char* filename, int mode, int m = filebuf::openprot);
Параметр filename задает имя открываемого файла.Параметр mode указывает режим ввода/вывода. Далее следует список аргументовдля mode, описанных в заголовочном файле FSTREAM.H:
- in открытьпоток для ввода,
- out открытьпоток для вывода,
- ate установитьуказатель потока на конец файла,
- app открытьпоток для добавления,
- trunk удалить содержимое файла,если он уже существует (bc++5),
- nocreate инициировать ошибку,если уже не существует,
- noreplace инициировать ошибку,если файл уже существует,
- binary открыть в двоичном режиме.
Пример 1.
//открыть поток для ввода
fstreamf;
f.open(«simple.txt»,ios::in);
//открыть поток для вывода fstream f;
fstreamf;
f.open(«simple.txt», ios::out);
//открыть поток ввода/вывода для двоичных данных fstream f;
fstreamf;
f.open(«simple.txt»,ios::in | ios::out | ios::binary);
Внимание: Классы файловых потоков предусматриваютконструкторы, которые выполняют действия (и имеют такие же параметры)функции-компонента open.
Функция close закрывает поток и освобождаетиспользовавшиеся ресурсы. Эти ресурсы включают буфер памяти для операции потоковоговвода/вывода.Функция-компонент close
Прототип для функции close:
voidclose();
Пример 2.
fstreamf;
//открыть поток
f.open( «simple.txt», ios:: in);
//работа с файлом
//закрыть поток
f.close();
Stream-библиотека C++ включает в себя набор основныхфункций, которые контролируют состояние ошибки потоковой операции. Этифункции включают следующие:
1. Функция good() возвращаетненулевое значение, если при выполнении потоковой операции не возникает ошибки. Объявлениефункции good: int good();
2. Функция fail() возвращаетненулевое значение, если при выполнении потоковой операции возникает ошибка. Объявлениефункции fail: int fail();
3. Перегруженная операция!применяется к экземпляру потока для определения состояния ошибки.
Stream-библиотека C++ предоставляет дополнительныефункции для установки и опроса других аспектов и типов ошибокпотока.ПОСЛЕДОВАТЕЛЬНЫЙ ТЕКСТОВЫЙ ПОТОК ВВОДА/ВЫВОДА
Функции и операции последовательного текстовоговвода/вывода являются довольно простыми. Вы уже имели дело со многими из нихв предыдущих уроках. Эти функции и операции включают:
- Операция извлечения из потока
- Операция помещения в поток>> читает символы потока.
- Функция getline читает строку изпотока.Функция-элемент getline
Прототипы функции-элемента getline:
istream&getline (char* buffer, int size, char delimiter = '\n');
istream&getline (signed char* buffer, int size, char delimiter = '\n');
istream&getline (unsigned char* buffer, int size, char delimiter = '\n');
Параметр buffer — это указатель на строку, принимающуюсимволы из потока. Параметр size задает максимальное число символов длячтения. Параметр delimiter указывает разделяющий символ, которыйвызывает прекращение ввода строки до того, как будет введено количество символов,указанное в параметре size. По умолчанию параметру delimiter присваиваетсязначение '\n'.
Пример 3.
fstreamf;
chartextLine[MAX];
f.open(«sample.txt»,ios::in);
while(!f.eof()) {
f.getline(textLine, MAX);
cout
}
f.close();
Рассмотрим пример. В листинге 10.1 приведен исходныйкод программы TRIM.CPP. Программа выполняет следующие задачи:
- Выдает запрос на ввод именивходного текстового файла.
- Выдает запрос на ввод именивыходного текстового файла. (Программа проверяет имена этих файлов насовпадение, и в случае положительного результата повторяет запрос на ввод имени выходногофайла).
- Читает строки из входного файла иудаляет из них пробелы.
- Записывает эти строки в выходнойфайл и также в стандартное окно вывода.Листинг10.1. Исходный код программы TRIM.CPP
//C++ программа демонстрации последовательного файлового
//ввода/вывода
Программа в листинге 10.1 не объявляет никакихклассов, вместо этого она фокусируется на использовании файловых потоков дляввода и вывода текста. Эта программа описывает функции trimStr, getInputFilename,getOutputFilename, processLines и обязательную функцию main.
Функция trimStr вычищает пробелы встроках, передаваемых через параметр s. Эта функция объявляет переменную i иприсваивает ей индекс символа, находящегося сразу за завершающим нулем.Функция использует цикл while, начинающийся в строке 14, чтобы выполнить обратноесканирование символов в строке s до первого символа, не являющегосяпробелом. Оператор в строке 16 присваивает завершающий нуль символу,стоящему справа от последнего символа, не являющегося пробелом, в строкеs.
Функция getInputFilename получает имя входного файла иоткрывает соответствующий файловый поток. Параметр inFile передает это имявызывающей функции. Ссылочный параметр f передает открытый входной потоквызывающей функции. Функция getInputFilename объявляет локальный флажок ok ииспользует цикл do-while (строки с 23 по 34), чтобы открыть входной файл. Строка25 содержитпервый оператор тела цикла, в котором флажок ok инициализируется значением true.Оператор вывода в строке 26 запрашивает ввод имени входного файла; в строке27 с помощью вызова функции getline это имя принимается и сохраняется впеременной inFile. Оператор в строке 28 пытается открыть входной файл,используя параметр потока f. Оператор open использует значение ios::in дляуказания на то, что входной текстовый файл был открыт. Если вызов возвращаетошибку, оператор if (строка 29) определит это, сообщит об ошибке открытия файлапользователю и присвоит переменной ok значение false. При значении ok,равном true, цикл do-while будет выполняться и сохранять ответы пользователя дотех, пока не произойдет успешное открытие файла.
Функция getOutputFilename подобна функцииgetInputFilename в попытках получить имя файла от пользователя и открыть файл.Однако в этом случае файл открывается на запись и его имя сравнивается с именемвходного файла. В строке 47 проверяется совпадение имен входного ивыходного файлов, и, если пользователь ввел одно и то же имя, ему предлагаетсяснова ввести имя выходного файла.
Функция processLines читает строки их входногофайлового потока, приводя их в порядок и записывая в выходной файловый поток.Параметры fin и font передают файловые указатели входного и выходногопотоков, соответственно. Эта функция объявляет локальную строковую переменнуюline и использует (строки с 69 по 74) цикл while для обработки текстовыхстрок. Предложение while содержит вызов функции getline, которая читаетследующую строку входного потока fin и присваивает переменной lineсодержимое этой строки. В теле этого цикла просто вызывается функция trimStr, азатем line передается в потоки fout и cout. Заметьте, что команды дляполучения строки текста из файла и передачи ее в файл в точности совпадают стеми, которые могли бы использоваться для получения текста с экрана ипередачи его на экран. Это происходит потому, что используемые вами cout и cin — на самом деле только потоки, которые открываются автоматически и работаютнепосредственно с
экраном.
Функция main, как обычно со всеми уже описаннымифункциями, довольно простая. Она только объявляет переменные файловыхпотоков fin, fout и inFile, outFile для сохранения имен этих потоков. Далеевходной и выходной файлы открываются в функциях getInputFilename иgetOutputFilename. Наконец, функция processLine приводит в порядок и копирует содержимоефайла, а функция-компонент close вызывается для каждого из потоков.ПОСЛЕДОВАТЕЛЬНЫЙ ДВОИЧНЫЙ ФАЙЛОВЫЙ ВВОД/ВЫВОД
Stream-библиотека C++ имеет перегруженные потоковыефункции-элементы write и read для последовательного двоичного файловоговвода/вывода. Функция write посылает ряд байт в выходной поток. Этафункция может записывать любую переменную или экземпляр в поток.Функция-элемент write
Прототип перегруженной функции-элемента:
ostream&write(const char* buff, int num);
ostream&write(const signed char* buff, int num);
ostream&write(const unsigned char* buff, int num);
Параметр buff — это указатель на буфер, содержащийданные, которые будут посылаться в выходной поток. Параметр num указываетчисло байт в буфере, которые передаются в этот поток.
Пример 4.
constMAX = 80;
charbuff[MAX+1] = «Hello World!»;
intlen = strlen (buff) + 1;
fstreamf;
f.open(«CALC.DAT»,ios::out | ios::binary);
f.write((constunsigned char*) &len, sizeof(len));
f.write((constunsigned char*) buff, len);
f.close();
В этом примере открывается файл CALC.DAT, записываетсяцелое, содержащее число байт в строке и записывается сама строка передтем, как файл закрывается.
Функция read считывает некоторое количество байт извходного потока. Эта функция может считывать любую переменную или экземпляриз потока. Функция-элемент read
Прототип перегруженной функции-элемента read:
ostream&read(char* buff, int num);
ostream&read(signed char* buff, int num);
ostream&read(unsigned char* buff, int num);
Параметр buff — это указатель на буфер, которыйпринимает данные из входного потока. Параметр num указывает числосчитываемых из потока байт.
Пример 5.
constMAX = 80;
charbuff [MAX+1];
intlen;
fstreamf;
f.open(«CALC.DAT»,ios::in | ios::binary);
f.read((unsignedchar*) &len, sizeof(len));
f.read((unsignedchar*) buff, len);
f.close();
В этом примере считывается информация, записанная впредыдущем примере.
Рассмотрим пример, выполняющий последовательныйдвоичный потоковый ввод/вывод. В листинге 10.2 представлен исходный кодпрограммы ARRAY.CPP. Эта программа объявляет класс, который моделируетчисленный динамический массив. Операции ввода/вывода позволяют программечитать и писать как отдельные элементы массива, так и целый массив вдвоичный файл. Эта программа создает массивы arr1, arr2 и аrrЗ, а затемвыполняет следующие задачи:
- Присваивает значения элементаммассива arr1. (Этот массив имеет 10 элементов).
- Присваивает значения элементаммассива аrrЗ. (Этот массив имеет 20 элементов).
- Отображает значения массива arr1.
- Записывает элементы массива arr1 вфайл ARRAY1.DAT (по одному за операцию).
- Читает элементы массива arr1 изэтого файла в массив arr2 (по одному за операцию). (Массив arr2 имеет 10 элементов, то есть онодного размера с массивом arr1).
- Отображает элементы массива arr2.
- Отображает элементы массива аrrЗ.
- Записывает элементы массива аrrЗ вфайл ARRAY3.DAT, все сразу.
- Читает (все сразу) данные из файлаARRAY3.DAT и сохраняет их в массиве arr1.
- Отображает значения массива arr1.(Выход показывает, что массив arr1 имеет тот же размер, что и массив arr3).Листинг10.2. Исходный код программы ARRAY.CPP
//C++ демонстрация последовательного двоичного
//ввода/вывода
Программа листинга 10.2 объявляет версию класса Array,который похож на приводимый в главе 8 в листинге 8.2. Основное отличиев том, что здесь мы использовали operator[ ] для замены и функции store, иrecall. Эта операция проверяет правильность указания индекса и возвращаетзначение в badIndex, если аргумент выходит за диапазон массива. Вдополнение к operator[ ] мы добавили функции-элементы writeElem, readElem,writeArray и readArray для выполнения последовательного двоичного файлового ввода/вывода.Мы также добавили функции resize и getPrt как вспомогательные функциисоответственно для изменения размера массива и для возвращенияуказателя на соответствующий элемент массива.
Обратите внимание также на копирующий конструктор встроке 29, и особенно на то, что он действительно ничего не определяет. Это- прием, который может быть использован для защиты других функций или классовот их автоматического копирования, когда вы знаете, что это плохо (в этомслучае потому, что используются динамические данные, содержимое которыхнеобходимо копировать).
Функция writeElem, определенная в строках с 43 по 49,записывает одиночные элементы массива в выходной поток. Параметр osпредставляет выходной поток. Параметр index определяет элемент массива для записи.Функция writeElem возвращает true, если индекс правильный и если операцияпо выводу осуществляется без ошибок. После того, как writeElem записывает элемент массива,внутренний указатель потока продвигается в следующее положение.
Функция readElem, определяемая в строках с 51 по 57,считывает одиночный элемент массива из входного потока. Параметр Isпредставляет входной поток. Параметр index определяет индекс элемента массива длячтения. Эта функция возвращает true, если индекс массива правильный и еслиоперация по вводу осуществляется без ошибок. После того, как readElemсчитывает элемент массива, внутренний указатель потока продвигается в следующееположение.
Функции writeElem и readElem позволяют экземплярукласса соответственно писать и читать элементы данных из различных потоков.
Функция writeArray, определенная в строках с 59 по 69,записывает все элементы массива в двоичный файл. Параметр filename определяетимя выходного файла. Функция открывает выходной поток и записывает значениеэлемента класса size, а затем и элементы динамического массива. ФункцияwriteArray возвращает true, если массив в поток записан успешно. Иначе онавозвращает false. Эта функция открывает локальный выходной поток, используяпотоковую функцию open и передавая ей имя файла и режим ввода/вывода. Режим ввода/выводапредставляет собой выражение ios::out|ios::binary, которое определяет, чтопоток открывается только для вывода двоичных записей. Эта функция дваждывызывает потоковую функцию write — первый раз для записи компонентакласса size, второй — для записи элементов динамического массива.
Функция readArray, определенная в строках с 71 по 83,читает все элементы массива из двоичного файла. Параметр filenameопределяет имя входного файла. Функция открывает входной поток и считывает значениекомпонента класса size, а затем считывает элементы динамического массива.Функция readArray возвращает true, если она успешно считывает массив изпотока. В противном случае, возвращается false. Функция открываетлокальный входной поток, используя потоковую функцию open и передавая ей имяфайла и аргументы режима ввода/вывода. Аргумент режима ввода/вывода — это выражение ios::in | ios::binary, которое определяет, что потокоткрыт только для двоичного ввода. Функция делает два вызова потоковой функцииread, первый — для чтения элемента класса size, и второй — для чтения элементовдинамического массива. Другим свойством функции readArray является то, чтоона изменяет размер экземпляра класса Array для настройки его всоответствии с данными двоичного файла, вызывая функцию-элемент resize. Это означает,что динамический массив, который доступен посредством экземпляракласса, может либо уменьшаться, либо расширяться в зависимости от размерамассива, сохраняемого в файле.
Функция-элемент resize, которая начинается в строке65, на самом деле очень простая. Она проверяет, является ли требуемый размертем же, что и установленный ранее. Если нет, то память,зарезервированная функцией dataPtr, освобождается, а затем создается новая область памяти,соответствующая новому размеру. Этот новый размер присваивается компонентукласса size.
Функция dispArray чаще всего являетсяфункцией-элементом, но я решил сделать ее здесь обычной функцией, чтобы лучшепоказать, как использование функции operator[ ] позволяет тем, кто работает склассом Array, обращаться к нему таким же способом, какой они применяют кэлементам стандартного массива. В этом случае есть простой цикл for, которыйвыполняется для каждого элемента arr и отображает его содержимое.
Наконец, мы подходим к функции main (строка 104).Обычно она в основном
толькозапускает функции, которые уже были созданы, для выполнения
следующихзадач:
- Объявляет (строка 108) триэкземпляра класса Array с именами arr1, arr2 и аrr3. (Первые два экземпляраимеют тот же самый размер динамического массива, заданный константой SIZE1, в то время какаrr3 имеет больший размер, определенный константой SIZE2).
- Объявляет (строка 111) файловыйпоток f и открывает его (используя конструктор потока) для доступа к файлу ARRAY1.DAT вдвоичном режиме.
- Использует циклы for (строки с 114по 116), чтобы произвольно присвоить значения экземплярам arr1 и аrr3.
- Отображает элементы экземпляраarr1 (строка 119).
- Записывает элементы массива arr1 ввыходной файловый поток f, используя цикл for (строка 122) для вызова функции-компонентаwriteElem с выходным файловым потоком f и переменной цикла i.
- Закрывает файловый поток f,вызывая функцию-элемент close этого потока.
- Открывает (строка 127) файловыйпоток f для доступа к файлу ARRAY1.DAT. (На это раз сообщение open определяет режим двоичноговвода)
- Считывает элементы в arr2(которому до сих пор не присваивались никакие значения) из входного файловогопотока f, используя цикл for (строка 128).
- Закрывает входной поток (строка130). D Отображает элементы экземпляров arr2 и аrr3 (строки 132 и 133).
- Записывает все содержимое аrr3,вызывая функцию-компонент writeArray. (Функция writeArray имеет аргумент имени файлаARRAY3.DAT).
- Считывает массив файла ARRAY3.DATв экземпляр arr1, вызывая функцию-компонент readArray и передавая ей в качествеаргумента имени файла ARRAY3.DAT.
- Отображает новые элементыэкземпляра arr1.Файловый ввод/вывод с прямым доступом
Файловые операции ввода/вывода прямого доступа такжеиспользуют потоковые функции-элементы read и write, представленные в предыдущемразделе. Stream-библиотека имеет ряд функций, позволяющих вам передвигатьуказатель потока в любое необходимое положение. Функция-элемент seekg — одна изтаких функций.Функция-элемент seekg
Прототип для перегруженной функции-компонента seekg:
istream& seekg(long pos);
istream& seekg(long offset, seek_dir dir);
Параметр pos в первой версии определяет абсолютноеположение байта в потоке. Во второй версии параметр offset определяетотносительное смещение, в зависимости от аргумента dir. Аргументыдля последнего параметра:
ios::beg С начала файла
ios::cur С текущей позиции файла
ios::end С конца файла
Пример
constBLOCK SIZE = 80
charbuff[BLOCK_SIZE] = «Hello World!»;
f.open(«CALC.DAT»,ios::in | ios::out | ios::binary);
f.seekg(3* BLOCK_SIZE); // продвинутся к блоку 4
f.read((constunsigned char*)buff, BLOCK_SIZE);
cout
fclose();
Виртуальный массив — это базирующийся на диске массив,который сохраняет строки фиксированного размера на диске. Вы можетерассматривать его как обычный массив, который вместо сохранениясвоих элементов в памяти записывает их в дисковый файл.
Рассмотрим пример файлового ввода/вывода прямогодоступа. В листинге 10.3 приведен исходный код программы VIRTUAL.CPP иреализует виртуальный массив. Программа выполняет следующие задачи:
- Использует внутренний список имендля создания объекта виртуального массива.
- Отображает элементынеупорядоченного объекта виртуального массива.
- Сортирует элементы объектавиртуального массива.
- Отображает элементы сортированногообъекта виртуального массива.
-
Листинг 10.3. Исходный код прогшраммы VIRTUAL.CPP
//C++ демонстрация файлового ввода/вывода прямого доступа
Программа листинга 10.3 объявляет класс VmArray. Этоткласс моделирует динамический базирующийся на диске массив, которыйсохраняет все его элементы в двоичном файле прямого доступа. Заметьте,что в этом классе объявлен экземпляр класса fstream и что не существуетуказателя на динамический массив. Класс объявляет конструктор, деструктор и рядфункций-компонентов.
Конструктор класса имеет два параметра: Size иfilename. Параметр Size задает размер виртуального массива. Параметр filename именуетдвоичный файл, который сохраняет элементы экземпляров класса.Конструктор открывает поток f, используя потоковую функцию open и передавая ей вкачестве аргументов filename и выражение для режима работы с файломios::in | ios::out | ios::binary.
Это выражение указывает, что поток открывается длядвоичного ввода/вывода (то есть режима прямого доступа). Если конструктор успешнооткрывает файловый поток, он заполняет файл пустыми строками. Деструкторкласса выполняет простую задачу закрытия файлового потока f.
Функции setElem и getElem поддерживают прямой доступ кэлементам массива. Эти функции используют потоковую функцию seekg, чтобы устанавливатьуказатель потока на соответствующий элемент массива. Затем функция setElemвызывает потоковую функцию write для сохранения элемента массива(передаваемый параметром str). Напротив, функция getElem называет потоковуюфункцию read, чтобы получить элемент массива (возвращаемый через аргумент str).Обе функции возвращают результат типа bad, который указывает на успешностьоперации ввода/вывода.
Класс VmArray также объявляет функцию BubbleSort длясортировки элементов виртуального массива. Эта функция использует функции-элементы getElem иsetElem для доступа и свопинга элементов массива. Затем, наконец, запускаетсяпоследняя функция-элемент display для элементов виртуального массива,которая посылает их на экран. Функция main выполняет следующие
задачи:
- Объявляет экземпляр arr классаVmArray. (Этот экземпляр сохраняет 10 строкв двоичном файле ARR.DAT)
- Присваивает случайное значениеэлементам экземпляра аот, используя цикл for (строки 97 и 98).
- Отображает несортированныеэлементы экземпляра arr, вызывая функцию-элемент display.
- Сортирует массив, вызывая функциюBubbleSort.
- Отображает сортированные элементыэкземпляра arr.Заключение
Сегодняшний урок представил краткое введение вбиблиотеку ввода/вывода C++ и вынес на обсуждение следующие вопросы:
- Общие функции ввода/вывода,включая open, close, good, fail и оператор !.
- Функция open открывает файловыйпоток ввода/вывода и поддерживает попеременный и множественный режимы ввода/вывода.Функция close закрывает файловый поток. Функции good и failиндицируют успешную или ошибочную, соответственно, потоковую операциюввода/вывода.
- C++ позволяет выполнятьпоследовательный потоковый ввод/вывод длятекста с использованием операций , так же как и при помощи потоковой функции getline. Операция применяется для
- получения символов. Функцияgetline позволяет вашему приложению считывать строки с клавиатуры или из текстового файла.
- Последовательный потоковыйввод/вывод двоичных данных использует потоковые функции write или read для записи илисчитывания данных из переменных любого типа.
- Потоковый ввод/вывод прямогодоступа для двоичных данных использует функцию seekg в объединении с функциями read и write.Функция seekg позволяет вам передвигать потоковый указатель либо вабсолютное, либо в относительное положение в потоке.Вопросы и ответы
Какможно эмулировать прямой доступ к строкам в текстовом файле?
Сначала считывайте строки из файла как текст,получайте длину строк (плюс два символа для конца каждой строки) и сохраняйтенакапливаемую длину в специальном массиве. (Назовите его, например,lineIndex) Этот массив сохраняет позицию байта, где начинается каждая строка.Последний элемент массива будет содержать размер файла. Для доступа кстроке номер i, используйте функцию seek или seekg, чтобы найтисмещение для lineIndex[i]. Размер строки номер i равен lineIndex[i+1] — lineIndex[i+1]. Какнаписать процедуру общего назначения для копирования между входным ивыходнымфайловым потоком?
Вам необходимо использовать потоковую функцию gcount()для получения ряда байт фактически читаемых в последнемнеформатированном потоковом вводе. Вот функция copyStream:
voidcopyStream(fstreamit fin, fstreamil fout,
unsigned char* buffer, int buffSize)
{
int n;
while (fin. read (buffer, buffaize))
{
n = fin.gcount();
fout.write (buffer, n);
}
}ПрактикумКонтрольные вопросы
1.Верно или нет? Потоковые функции ввода/вывода read и write способны правильносчитывать и записывать данные любого типа.
2.Верно или нет? Потоковые функции ввода/вывода read и write способны правильносчитывать и записывать данные любого типа, не имеющих указателей.
3.Верно или нет? Функции seek и seekg расширяют файл, когда вы передаете индекс, которыйна один или более байт превышает текущий конец файла.
4.Верно или нет? Аргументы функций seek и seekg не требуют проверки диапазона.Упражнение
Создайте программу VSEARCH.CPP, модифицируя программу VIRTUAL.CPP.Класс VmArray в VSEARCH.CPP должен иметь функцию binSearch, которая проводитдвоичный поиск в элементах сортированного массива. Добавьте цикл в конецфункции main для поиска в массиве arr, используя неупорядоченные данныеинициализирующего списка. (Элементы этого списка доступны при использованииданных-указателей.)