Реферат по предмету "Компьютеры и цифровые устройства"


Основы алгоритмического языка С++

Основы алгоритмического языка С Переменные и операции языка С Здесь представлены базовые компоненты программ на С . В их число входят типы данных, переменные, константы и выражения. ИЗУЧАЕМЫЕ ПОНЯТИЯ - Предопределенные типы данных в С включают в себя типы int, char, float, double и void.

В языке С гибкость типов данных увеличивается благодаря применению модификаторов типов. Эти модификаторы изменяют точность представления и диапазон значений переменных. Модификаторами типа являются signed, unsigned, short и long Идентификаторы в С могут иметь длину до 32 символов и должны начинаться с буквы или подчеркивания. Последующие символы идентификатора могут быть буквой, цифрой или подчеркиванием.

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

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

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

Язык С дает вам возможность применять эти операции к переменным, в которых хранятся символы, целые числа и даже числа с плавающей точкой Арифметические операции присваивания дают вам возможность записывать более короткие арифметические выражения, в которых первый операнд является также переменной, принимающей результат вычислений Оператор sizeof возвращает как для типов данных, так и для переменных их размер в байтах Механизм приведения типа дает вам возможность форсировать преобразование типа выражения

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

AND, OR, XOR и NOT. Кроме того, в С поддерживаются поразрядные операции сдвига и Операции манипулирования битами с присваиванием предлагают короткие формы для простых операций манипулирования битами. Буквы и цифры Множество символов Си включает большие и малые буквы из английского алфавита и 10 десятичных арабских цифр -большие английские буквы A B C D E F G H I J K L M N O P Q R T U V W X Y Z -малые английские буквы a b c d e f g h i j k l m

n o p q r t u v w x y z -десятичные цифры 9 Буквы и цифры используются при формировании констант, идентификаторов и ключевых слов. Все эти конструкции описаны ниже. Компилятор Си рассматривает одну и ту же малую и большую буквы как отличные символы. Если в данной записи использованы малые буквы, то замена малой буквы a на большую букву A сделает отличной данную запись от предшествующей.

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

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

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

пробельные символы и неграфические символы в строках и символьных константах. Их типичное использование связано со спецификацией таких действий, как возврат каретки и табуляция , а также для задания литеральных представлений символов, таких как символ двойная кавычка. ESC-последовательность состоит из наклонной черты влево, за которой следует буква, знаки пунктуации или комбинация цифр. В таблице 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 . Операция условного выражения ? -это тернарная, а не двухсимвольная операция.

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

число, которое представляет целую величину. Десятичная константа имеет следующий формат представления digits , где digits - это одна или более десятичных цифр от 0 до 9. Восьмеричная константа имеет следующий формат представления 0 odigits , где odigits - это одна или более восьмеричных цифр от 0 до 7. Запись ведущего нуля необходима. Шестнадцатеричная константа имеет один из следующих форматов представления 0x hdigits 0X hdigits ,

где hdigits одна или более шестнадцатеричных цифр. Шестнадцатеричная цифра может быть цифрой от 0 до 9 или буквой большой или малой от A до F. В представлении константы допускается смесь больших и малых букв. Запись ведущего нуля и следующего за ним символа x или X необходима. Пробельные символы не допускаются между цифрами целой константы.

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

Десятичные константы могут быть типа int или long. Восьмеричные и шестнадцатеричные константы в зависимости от размера могут быть типа int, unsigned int, long или unsigned long. Если константа может быть представлена как int, она специфицируется типом int. Если ее величина больше, чем максимальная положительная величина, которая может быть представлена типом int, но меньше величины, которая представляется в том же самом числе бит как и int, она задается типом

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

приписав букву l или L в конец константы. В Табл. 2.6 показаны примеры целых констант. Константы с плавающей точкой Константа с плавающей точкой- это действительное десятичное положительное число. Величина действительного числа включает целую, дробную части и экспоненту. Константы с плавающей точкой имеют следующий формат представления digits . digits E - digits , где digits - одна или более десятичных цифр от 0 до 9 , а

E или e -символ экспоненты. Целая или дробная части константы могут быть опушены, но не обе сразу. Десятичная точка может быть опущена только тогда, когда задана экспонента. Экспонента состоит из символа экспоненты, за которым следует целочисленная величина экспоненты, возможно отрицательная. Пробельные символы не могут разделять цифры или символы константы. Константы с плавающей точкой всегда специфицируют положительные величины.

Если требуются отрицательные величины, то необходимо сформировать константное выражение из знака минус и следующей за ним константы. Знак минус рассматривается как арифметическая операция. Примеры констант с плавающей точкой и константных выражений 15.75 1.575E1 1575e-2 -0.0025 -2.5e-3 25e-4 Целая часть константы с плавающей точкой может быть опущена, например .75 .0075e2 125 175E-2 Все константы с плавающей точкой имеют тип double.

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

Чтобы использовать одиночную кавычку или наклонную черту влево в качестве константы-символа, необходимо вставить перед этими знаками наклонную черту влево. Чтобы представить символ новой строки, необходимо использовать запись n . Константы-символы имеют тип int. Строковые литералы Строковый литерал- это последовательность букв, цифр и символов, заключенная в двойные кавычки.

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

Наклонная черта влево вместе с символом новой строки будут проигнорированы компилятором , что позволяет формировать строковые литералы, располагаемые более чем в одной строке. Например, строковый литерал Long strings can be bro cken into two pieces. идентичен строке Long strings can be brocken into two pieces. Чтобы использовать двойные кавычки или наклонную черту влево внутри строкового литерала, нужно представить их с предшествующей наклонной чертой влево, как

показано в следующем примере This is a string literal First Second Yes, I do, she said. The following 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 Комментарии Комментарий- это последовательность символов, которая воспринимается компилятором как отдельный пробельный символ или, другими словами, игнорируется. Комментарий имеет следующую форму представления characters , где characters может быть любой комбинацией

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

комментарии не могут появляться внутри лексем. Следующие примеры иллюстрируют некоторые комментарии Comments can separate and document lines of a program. Comments can contain keywords such as for and while Comments can occupy several lines. Так как комментарии не могут содержать вложенных комментариев, то следующий пример будет ошибочным You cannot nest comments

Компилятор распознает первую комбинацию после слова nest как конец комментария. Затем, компилятор попытается обрабатывать оставшийся текст и выработает сообщение об ошибке. Чтобы обойти компиляцию комментариев больших размеров, нужно использовать директиву if препроцессора. Лексемы Когда компилятор обрабатывает программу, он разбивает программу на группы символов, называемых лексемами. Лексема- это единица текста программы, которая имеет определенный смысл для компилятора и

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

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

плюс самую длинную из возможных операций , а затем обработает оставшийся знак , как операцию сложения . Выражение проинтерпретируется как i j , а не как i j . В таких случаях необходимо использовать пробельные символы или круглые скобки, чтобы однозначно определить ситуацию. ИСХОДНЫЕ ТЕКСТЫ ПРИМЕРОВ Программа VAR.CPP, иллюстрирующая простые переменные include iostream.h int main int i, j 2 double x, y 355.0 113 i 3 j cout i i endl j j endl x 2 y x x x cout y y endl x

x endl return 0 Результаты i 6 j 2 y 3.141593 x 39.4784 Программа CONST1.CPP, иллюстрирующая константы include iostream.h define SEC IN MIN 60 define MIN IN HOUR 60 int main 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 endl totalSec секунд прошло с полуночи endl return 0 Тест и результаты Введите часы 10 Введите минуты 0 Введите секунды 0 36000 секунд прошло сполуночи Программа CONST2.CPP, иллюстрирующая формальные константы include iostream.h const int SEC IN MIN 60 глобальная константа int main 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 endl endl totalSec секунд прошло с полуночи endl return 0 Тест и результаты Введите часы 1 Введите минуты 10

Введите секунды 20 4220 секунд прошло с полуночи Программа OPER1.CPP, иллюстрирующая простые математические операции include iostream.h int main int int1, int2 long long1, long2, long3, long4, long5 float x, y, real1, real2, real3, real4 cout endl Введите первое целое число cin int1 cout Введите второе целое число cin int2 cout endl long1 int1 int2 long2 int1 - int2 long3 int1 int2 long4 int1 int2 long5 int1 int2 cout int1 int2 long1 endl cout int1

- int2 long2 endl cout int1 int2 long3 endl cout int1 int2 long4 endl cout int1 int2 long5 endl cout endl endl cout Веедите первое вещественное число cin x cout Введите второе вещественное число cin y cout endl real1 x y real2 x - y real3 x y real4 x y cout x y real1 endl cout x - y real2 endl cout x y real3 endl cout x y real4 endl cout endl endl 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 iostream.h int main short int aShort int anInt long aLong char

aChar float aReal cout Таблица 1. Размеры памяти для переменных endl endl cout Тип данных Используемая endl cout память в байтах endl cout endl cout short int sizeof aShort endl cout integer sizeof anInt endl cout long integer sizeof aLong endl cout character sizeof aChar endl cout float sizeof aReal endl cout endl endl endl cout Таблица 2. Размеры памяти для типов данных endl endl cout Тип данных

Используемая endl cout память в байтах endl cout endl cout short int sizeof short int endl cout integer sizeof int endl cout long integer sizeof long endl cout character sizeof char endl cout float sizeof float endl cout endl endl endl 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 iostream.h int main 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 shortInt1 shortInt1 endl shortInt2 shortInt2 endl aByte aByte endl anInt anInt endl aLong aLong endl aChar is aChar endl aReal aReal endl endl endl дейтсвия выполняются с приведением типа 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 shortInt1 shortInt1 endl shortInt2

shortInt2 endl aByte aByte endl anInt anInt endl aLong aLong endl aChar is aChar endl aReal aReal endl endl endl return 0 Результаты shortInt1 10 shortInt2 6 aByte 16 anInt 4 aLong 60 aChar is A aReal 60.5 shortInt1 10 shortInt2 6 aByte 16 anInt 4 aLong 60 aChar is A aReal 60.5 ВОПРОСЫ И ОТВЕТЫ Существуют ли особые соглашения о присвоении имен идентификаторам? Существует несколько стилей, которые стали популярными в последние годы.

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

Каково булево выражение для проверки того, что значение переменной i находится в заданном диапазоне значений например, определяемом переменными lowVal и hiVal ? Выражением, которое определяет, находится ли значение переменной i в некотором диапазоне, является i lowVal i hiVal . Конструкции принятия решений и циклы ВОПРОСЫ И ОТВЕТЫ Предъявляет ли С какие-либо требования на отступ операторов в предложениях оператора?

Нет. Отступ определяется только Вами. Типовые размеры отступа составляют два или четыре пробела. Использование отступов делает ваш листинг намного более удобочитаемым. Вот пример оператора 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 10 i 2 while i 10 cout i endl cout i endl i 2 Циклу while необходим начальный оператор, инициирующий переменную управления циклом. Заметим также, что внутри цикла while находится оператор, изменяющий значение переменной

управления циклом. Как смоделировать цикл while циклом do-while? Рассмотрим простой пример. i 1 i 1 do while i 10 cout i endl cout i endl i 2 i 2 while i 10 Оба цикла имеют одинаковые условия в предложениях while. Заметим, однако, что если цикл спроектирован таким образом, что начальное значение i может быть неизвестным заранее, то это может привести к различным эффектам.

Например, если i исходно равно 11, то цикл слева выполнится один раз, тогда как цикл справа не сделает ни одной итерации. Как открытый цикл for может эмулировать циклы while и do-while? Открытый цикл for эмулирует другие циклы С установкой оператора if выхода из цикла в начале или конце цикла. Рассмотрим пример эмуляции цикла while открытым циклом for i 1 i 1 while i 10 for if i 10 break cout i endl cout i endl i 2 i 2 Заметим, что открытый цикл for использует оператор if выхода из цикла

как первый оператор внутри цикла. Условие, проверяемое оператором if, есть логическое обращение условия цикла while. Рассмотрим простой пример, иллюстрирующий эмуляцию цикла do-while i 1 i 1 do for cout i endl cout i endl if i 10 break i 2 i 2 while i 10 Открытый цикл for использует оператор if выхода из цикла перед концом цикла. Оператор if проверяет обратное логическое условие, так же как в цикле do-while. Однако имейте, пожалуйста, в виду, что приведенные примеры довольно грубы и неэлегантны.

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

Esc должно приводить к выходу из программы. Можно ли во вложенном цикле for использовать переменную управления внешним циклом в качестве границы диапазона значений для внутренних циклов? Да. С не только не запрещает такое использование, на самом деле оно в порядке вещей. Рассмотрим простой пример. for int i 1 i 100 i 5 for int j i i 100 j cout i j endl Ограничивает ли С вложение циклов разных типов? Нет.

В программе на С вы можете вкладывать любые комбинации циклов. СТРУКТУРА ПРОГРАММЫ В этом разделе описывается структура исходной программы на Си и определяются термины, используемые в последующих разделах руководства при описании языка. По сути, здесь представлен общий обзор особенностей языка Си, которые в дальнейшем рассмотрены в деталях. Исходная программа

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

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

объявлений и определений. Любой из объектов программы имеет определенный синтаксис, описанный в этом руководстве,и каждая составляющая может появляться в любом порядке, хотя влияние порядка, в котором следуют переменные и функции может быть использовано в программе см. раздел 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 Executable statements w y - x printf z d nw d n , z, x Эта исходная программа определяет функцию с именем main и объявляет функцию printf.

Переменные x и y задаются своими определениями. Переменные z и w только объявляются. ОБЪЯВЛЕНИЯ В этом разделе описываются форматы и составные части объявлений переменных, функций и типов. Объявления Си имеют следующий синтаксис sc-specifier type-specifier declarator initializer , declarator initializer , где sc-specifier - спецификатор класса памяти type-specifier - имя определяемого типа declarator - идентификатор, который может быть модифицирован при объявлении указателя, массива или функции

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

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

В Си определено четыре спецификатора класса памяти, а именно auto, extern, register и static. Спецификатор класса памяти определяет, каким образом объявляемый объект запоминается и инициализируется и из каких частей программы можно ссылаться на него. Расположение объявления внутри программы, а также наличие или отсутствие других объявлений- также важные факторы при определении видимости переменных. Объявления функций описаны в разделе 4.4.

Спецификаторы типов Перечислимые типы также рассматриваются как основные типы. Спецификаторы перечислимых типов рассмотрены в разделе 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 int signed, int signed short int short, signed short signed long int long, signed long unsigned char unsigned

int unsigned unsigned short int unsignet short unsignet long int unsignet long float long float double Замечание в этом руководстве в основном используются сокращенные формы, перечисленные в Табл. 4.2, при этом предполагается, что char по умолчанию знаковый. В табл. 4.3 для каждого типа приведены размер распределяемой памяти и области значений переменных для данного типа. Поскольку тип void не представляет переменных, он не включен в эту таблицу. 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 стандартное соглашение Тип char используется для запоминания буквы, цифры или символа из множества представимых символов. Значением объекта типа char является

ASCII код, соответствующий данному символу. Так как тип char интерпретируется как однобайтовая целая величина с областью значений от -128 до 127, то только величины от 0 до 127 имеют символьные эквиваленты. Аналогично, тип unsigned char может запоминать величины с областью значений от 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 операцией. Деклараторы Синтаксис identifier declarator declarator constant-expression declarator declarator declarator arg-type-list declarator Си позволяет объявлять массивы величин, указатели на

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

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

Спецификатор типа задает тип элементов массива или тип адресуемых объектов и возвратов функции. Следующие примеры иллюстрируют простейшие формы деклараторов 1. int list 20 2. char cp 3. double func 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 struct both, struct both

В шестом примере показано, как объявлять функцию, возвращающую указатель на массив. Здесь var объявлена функцией, возвращающей указатель на массив из трех величин типа double. Тип аргумента функции задан составным абстрактным декларатором. Круглые скобки, заключающие звездочку, требуются, так как в противном случае типом аргумента был бы массив из трех указателей на величины типа double.

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

В этом примере var- это массив из пяти указателей на массив из пяти указателей на совмещения. Объявления переменной В этом разделе дано описание синтаксиса и семантики объявлений переменной. В частности, здесь объясняется каким образом объявить следующие переменные Массив Переменная, представляющая на бор элементов одного типа. Указатель Переменная, которая указывает на другую переменную содержит местоположение другой переменной

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

В некоторых случаях переменные могут быть инициализированы при их определении. Классы памяти и инициализация описаны в разделах 4.6 и 4.7 соответственно. Объявление простой переменной Синтаксис type-specifier identifier , identifier Объявление простой переменной определяет имя переменной и ее тип оно может также определять класс памяти переменной, как это описано в разделе 4.6. Имя переменной- это идентификатор, заданный в объявлении.

Спецификатор типа type-specifier задает имя определяемого типа данных. Можно определить имена различных переменных в том же самом объявлении, задавая список идентификаторов, разделенных запятой. Каждый идентификатор списка именует переменную. Все переменные, заданные в объявлении, имеют один и тот же тип. Примеры int x Example 1 unsigned long reply, flag

Example 2 double order Example 3 В первом примере объявляется простая переменная x. Эта переменная может принимать любое значение из множества значений, определяемых для типа int. Во втором примере объявлены две переменные reply и flag. Обе переменные имеют тип unsigned long. В третьем примере объявлена переменная order, которая имеет тип double. Этой переменной могут быть присвоены величины с плавающей запятой.

Объявление перечисления Синтаксис enum tag enum-list identifier , identifier enum tag identifier , identifier Объявление перечисления задает имя переменной перечисления и определяет список именованных констант, называемый списком перечисления. Значением каждого имени списка является целое число. Переменная перечисления принимает значение одной из именованных констант списка. Именованные константы списка имеют тип int. Таким образом, память соответствующая переменной перечисления-

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

перечисления. Во второй форме используется тег перечисления, который ссылается на тип перечисления. В этой форме объявления список перечисления не представлен, поскольку тип перечисления определен в другом месте. Если задаваемый тег не ссылается на уже определенный тип перечисления, или если именуемый тегом тип находится вне текущей видимости, то выдается ошибка. enum-list имеет следующий синтаксис identifier constant-expression , identifier constant-expression

Каждый идентификатор именует элементы перечисления. По умолчанию первому идентификатору соответствует значение 0, следующий идентификатор ассоциируется со значением 1 и т. д. Имя константы перечисления эквивалентно ее значению. Запись constant-expression переопределяет последовательность значений, заданных по умолчанию. Идентификатор, следующий перед записью constant-expression принимает значение, задаваемое этим константным

выражением. Константное выражение имеет тип int и может быть отрицательным. Следующий идентификатор в списке ассоциируется с величиной, равной constant-expression 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 tag member-declaration-list declarator , declarator struct tag declarator , declarator Объявление структуры задает имя типа структуры и специфицирует последовательность переменных величин, называемых элементами структуры, которые могут иметь различные типы. Объявление структуры начинается с ключевого слова struct и имеет две формы представления, как показано

выше. В первой форме представления типы и имена элементов структуры специфицируются в списке объявлений элементов member-declaration-list . tag - это идентификатор, который именует тип структуры, определенный в списке объявлений элементов. Каждый declarator задает имя переменной типа структуры. Тип переменной в деклараторе может быть модифицирован на указатель к структуре, на массив структур или на функцию, возвращающую структуру. Вторая синтаксическая форма использует тег- tag структуры для

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

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

инициализаторов. Элементы структуры могут быть любого типа основного, массивом, указателем, совмещением или структурой. Элемент не может иметь тип структуры, в которой он появляется. Однако, элемент может быть объявлен, как указатель на тип структуры, в которую он входит, позволяя создавать списочные структуры. Битовые поля Объявления битовых полей имеют следующий синтаксис type-specifier identifier constant-expression Битовое поле состоит из некоторого числа бит, специфицированных

константным выражением- constant- expression . Для битового поля спецификатор типа type- specifier должен специфицировать беззнаковый целый тип, а константное выражение должно быть неотрицательной целой величиной. Массивы битовых полей, указатели на битовые поля и функции, возвращающие битовые поля не допускаются. Идентификатор- identifier именует битовое поле. Неименованное битовое поле, чей размер специфицируется как нулевой, имеет специальное назначение оно гарантирует, что память для следующей переменной объявления

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

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

Примеры Example 1 struct float x,y complex Example 2 struct employee char name 20 int id long class temp Example 3 struct employee student, faculty, staff Example 4 struct sample char c float pf struct sample next x Example 5 struct unsigned icon 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 tag member-declaration-list declarator , declarator union tag

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

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

начиная с одного и того же адреса. Запомненные значения затираются каждый раз, когда присваивается значение очередного элемента совмещения. Примеры Example 1 union sign int svar unsigned uvar number Example 2 union char a, b float f 20 jack Example 2 union struct char icon unsigned color 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 . Объявление массива Синтаксис type-specifier declarator constant-expression type-specifier declarator Здесь квадратные скобки- это терминальные символы. Объявление массива определяет тип массива и тип каждого элемента. Оно может определять также число элементов в массиве.

Переменная типа массив рассматривается как указатель на элементы массива. Объявление массива может представляться в двух синтаксических формах, указанных выше. Декларатор declarator задает имя переменной. Квадратные скобки, следующие за декларатором, модифицируют декларатор на тип массива. Константное выражение constant-expression , заключенное в квадратные скобки, определяет число элементов в массиве. Каждый элемент имеет тип, задаваемый спецификатором типа type-

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

скобках, следущего за декларатором type-specifier declarator constant-expression constant-expression Каждое константное выражение- constant-expression в квадратных скобках определяет число элементов в данном измерении массива, так что объявление двумерного массива содержит два константных выражения, трехмерного- три и т.д. Если многомерный массив объявляется внутри функции или если он инициализируется либо объявляется как формальный параметр или объявляется как ссылка на массив, явно определенный где-

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

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

в разделе 5.2.5. Примеры Example 1 int scores 10 , game Example 2 float matrix 10 15 Example 3 struct float x,y complex 100 Example 4 char name 20 В первом примере объявляется переменная типа массив с именем scores из 10 элементов типа int. Переменная с именем game объявлена как простая переменная целого типа. Во втором примере объявляется двумерный массив с именем matrix.

Массив состоит из 150-ти элементов типа float. В третьем примере объявляется массив структур. Массив состоит из 100 объектов. Каждый объект массива представляет собой структуру, состоящую из двух элементов. В четвертом примере объявлен массив указателей. Массив состоит из 20-ти элементов, каждый из которых является указателем на величину типа char. 4.4.6. Объявление указателей Синтаксис type-specifier declarator

Объявление указателя определяет имя переменной типа указатель и тип объекта, на который указывает эта переменная. Декларатор- declarator определяет имя переменной с возможной модификацией ее типа. Спецификатор типа- type- specifier задает тип объекта, который может быть базового типа, типа структуры или совмещения. Переменная типа указатель может указывать также на функции, массивы и другие указатели. Более полная информация о типах указателей дана в разделе 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. Объявление функций Синтаксис type-specifier declarator arg-type-list , declarator Объявление функции определяет имя, тип возврата функции и, возможно, типы и число ее аргументов.

Объявление функции также называется forward- объявлением. Декларатор функции объявляет имя функции, а спецификатор типа задает тип возврата. Если спецификатор типа опущен в объявлении функции, то предполагается, что функция возвращает величину типа int. Объявление функции может включать спецификаторы класса памяти extern или static. Список типов аргументов. Список типов аргументов- arg-type-list определяет число и типы аргументов

функции. Синтаксис списка аргументов следующий type-name-list , Список имен типов- это список из одного или более имен типов. Каждое имя типа отделяется от другого запятой. Первое имя типа задает тип первого аргумента, второе имя типа задает тип второго аргумента и т. д. Если список имен типов заканчивается запятой с многоточием то это означает, что число аргументов функции переменно.

Однако, предполагается, что функция будет иметь не меньше аргументов, чем имен типов, предшествующих многоточию. Если список типов аргументов- arg-type-list содержит только многоточие , то число аргументов функции является переменным или равно нулю. Замечание Чтобы поддержать совместимость с программами предыдущих версий, компилятор допускает символ запятой без многоточия в конце списка типов аргументов для обозначения их переменного числа.

Запятая может быть использована и вместо многоточия для объявления нуля или более аргументов функции. Использование запятой поддерживается только для совместимости. Использование многоточия рекомендуется для нового представления. Имя типа- type- name для типов структуры, совмещения или базового типа состоит из спецификатора этого типа такого как 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 int prt 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. Неинициализированные объявления переменной на внешнем уровне не рекомендуются для файлов, которые могут быть размещены в библиотеку. Пример SOURCE FILE ONE extern int i reference to i defined below 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 first source 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 extern int i initial value is zero a is visible only within main static int a b is stored in a register, if possible register int b 0 default storage class is auto int c 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 int i 16 this a is visible only 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. Инициализация В объявлении переменной может быть присвоено начальное значение посредством инициализатора. Величина или величины инициализатора присваиваются переменной. Синтаксически, записи инициализатора предшествует знак равно initializer

Могут быть инициализированы переменные любого типа. Функции не инициализируются. Объявления, которые используют спецификатор класса памяти extern не могут содержать инициализатора. Переменные, объявленные на внешнем уровне, могут быть инициализированы. Если они явно не инициализированы, то они устанавливаются в нуль во время компиляции или линкования. Любая переменная, объявленная со спецификатором класса памяти static, может быть инициализирована константным

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

запрещена. Любое составное объявление класса памяти static может быть инициализировано на внешнем уровне. Начальными значениями для внешних объявлений переменной и для всех переменных static как внешних так и внутренних должно быть константное выражение. Автоматические и регистровые переменные могут быть инициализированы константными или переменными величинами. Базовые типы и типы указателей Синтаксис expression Величина выражения присваивается переменной.

Для выражения допустимы правила преобразования. Примеры 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. Составные типы Синтаксис initializer-list Список инициализаторов initializer-list - это последовательность инициализаторов, разделенных запятыми. Каждый инициализатор в последовательности- это либо константное выражение, либо список инициализаторов. Поэтому, заключенный в фигурные скобки список, может появиться внутри другого списка инициализации. Эта конструкция используется для инициализации элементов составных

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

нулем. Если число инициализирующих величин больше чем требуется, то выдается ошибка. Эти правила применяются к каждому вложенному списку инициализаторов, точно так же как и ко всей конструкции в целом. Пример 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 определяет спецификатор типа для типа. Это объявление используется для того, чтобы создавать более короткие или более осмысленные имена типов уже определенных в Си или объявленных пользователем. Типы структур, совмещений и перечислений Объявления типов структур, совмещений и перечислений имеют ту же самую общую синтаксическую форму, как и объявления переменных этих типов.

В объявлении типа идентификатор переменной опущен, так как нет переменной которая объявляется. Именем структуры, совмещения или перечисления является тег. В объявлении типа может появиться список объявлений элементов- member-declaration-list или список перечисления enum-list , определяющие тип. Сокращенная форма объявления переменной, в котором 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 student employee, чтобы объявить структурную переменную employee типа student. Объявления typedef Синтаксис typedef type-spesifier declarator , declarator Объявления typedef являются аналогом объявления переменной, за исключением того, что ключевое слово

typedef заменяет спецификатор класса памяти. Объявление интерпретируется тем же самым путем, как объявления переменной или функции, но declarator вместо того, чтобы стать переменной типа, специфицированного объявлением, становится синонимом имени типа. Объявление 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 void DRAWE 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. соответственно. Именами для основных, перечисляющих, структурных и совмещающих типов являются спецификаторы типа для каждого из них. Имена для типов указателя, массива и функции задаются следующей синтаксической формой type-specifier abstract-declarator Абстрактный декларатор abstract-declarator - это декларатор без идентификатора, состоящий из одного или более модификаторов указателей, массивов и функций.

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

делается при интерпретации составных деклараторов объявлений. Абстрактный декларатор, состоящий из пустых круглых скобок не допускается, поскольку это двусмысленно. В этом случае невозможно определить находится ли подразумеваемый идентификатор внутри скобок, и в таком случае- это немодифицированный тип, или перед скобками, тогда- это тип функции. Спецификаторы типа, установленные посредством объявлений 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 iostream.h int main int p new int cout Enter а number cin p cout The square of p p p 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 18 cout Значение параметра меньше 18 endl return if ui.age 21 ui.age 21 Если функция вызывается до своего определения, обязательно должен быть задан прототип функции.

Общая форма объявления функции возврТип имяФункции список параметров При объявлении функции имена параметров могут быть опущены Передача аргумента по ссылке позволяет функции изменять значение переданного аргумента и экономит память, так как при этом не создается локальная копия аргумента const тип1 параметр1, const тип2 параметр2, void foo int - объявление функции - это ее прототип int main int value 5 foo value cout value endl return 0

void foo int parm - определение функции вызов параметра по ссылке parm Результаты 6 void foo int пердача указателя int main int value 5 foo value передается адрес cout value endl getch foo value cout value endl getch return 0 void foo int parm parm параметр - указатель Результаты 6 7 - Локальные переменные и константы существуют и действуют только в теле данной функции, где они объявлены. Объявление локальных переменных подобно объявлению глобальных переменных.

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

Программа STATIC.CPP знакомит с понятием статической локальной переменной - Макроопределения позволяют вам вводить компактные псевдо-функции, принимающие любые типы данных, поскольку компилятор не выполняет в этом случае проверку типов define min n1, n2 n1 n2 ? n1 n2 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 iostream.h void swap int i, int j int temp i i j j temp int main int a 10, b 3 swap a, b cout а a and b b return 0 2. Каков будет результат работы следующей программы? Что вы можете сказать по поводу еще одной функции swap? include iostream.h void swap int i, int j int temp i i j j temp int main int a 10, b 3 swap a, b cout а a and b b 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, int diff 1 i I diff 6. В этой программе есть ошибка. Что это за ошибка и как ее исправить? include iostream.h int main double x 5.2 cout x 2 sqr x return 0 double sqr double х return x x 7.

Попробуйте в функции вычисления факториала использовать операцию Массивы Листинг 6.1. исходный текст программы AVERAGE1.CPP Программа иллюстрирует использование одномерных массивов при расчете среднего значения. include iostream.h const int MAX 0x1FFF 64K 8 - максимальный размер массива типа double int main double array MAX объявление одномерного массива int num elem Ввод количества обрабатываемых данных do cout

Введите размер массива данных 2 MAX cin num elem cout endl while num elem 2 num elem MAX Ввод данных for int ix 0 ix num elem ix cout массив ix cin array ix Расчет среднего значения double sum 0 for ix 0 ix num elem ix sum array ix cout endl Среднее sum num elem endl return 0 - При объявлении одномерных массивов им можно присвоить начальные значения. Список ИНИЦИАЛИЗАЦИИ должен быть заключен в фигурные скобки, а элементы в нем должны быть

разделены запятыми. Можно при инициализации задать данных МЕНЬШЕ, чем размер массива. В этом случае компилятор автоматически присвоит нулевые значения тем элементам, которые вы не инициализировали. И вдобавок, если вы не укажете размерность инициализируемого массива, она будет определена по количеству элементов в списке инициализации. Листинг 6.2. исходный текст программы AVERAGE2.CPP

Программа иллюстрирует использование одномерных массивов при расчете среднего значения. Данные задаются при инициализации массива. include iostream.h 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 num elem ix sum array ix cout массив ix

array ix endl cout endl Среднее sum num elem endl 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 t nelem, 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.CPP 01 Программа иллюстрирует потоковый форматированный вывод в C 02 с использованием функций width и precision 03 include iostream.h 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 int aByte 15 cout.width 2 cout anInt 16 cout.width 3 cout aByte anInt endl 17 18 cout.precision 3 cout aSingle 19 cout aDouble 20 cout.width 7 cout.precision 4 cout aSingle aDouble endl 21 22 cout Символьная переменная aChar 23 aChar endl 24 return 0 25 Пример программной сессии Введите три числа через пробел 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 int printf 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 stdio.h 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 объявляется следующим образом istreams getline signed char buffer, int size, char delimiter n istreams getline unsigned char buffer, int size, char delimiter n istream getline char buffer, int size, char delimiter n Параметр buffer является указателем на строку, в которую помещаются вводимые символы.

Параметр size задает максимальное количество вводимых символов. Параметр delimeter определяет символ-ограничитель, при появлении которого ввод символов прекращается прежде, чем будут введены все size символов. Параметр delimeter имеет аргумент по умолчанию, равный n . В случае ввода символов с клавиатуры этот символ появляется в потоке ввода при нажатии клавиши Пример include iostream.h см. файл Ex01.cpp int main char name 80 cout

Enter your name cin.getline name, sizeof name - 1 cout Hello name , how are you? return 0 Функции, объявленные в 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 поддерживает два способа присвоения значений строкам. Вы можете присвоить строковой переменной строковую константу, произведя инициализацию при объявлении строки. Этот метод прост требуется операция присваивания и строковая константа.

Инициализация строки Общий метод инициализации строки char stringVar stringSize stringLiteral Пример char a3tring 81 Borland C 5 in 21 days char Named Rene Kinner Второй способ присвоить значение строке - это вызвать функцию, которая копирует содержимое одной строки в другую не забывая при этом и нуль-символ. Эта функция называется strcpy. Она предполагает, что копируемая строка оканчивается символом

NUL и прекращает копирование, как только встретит этот символ. Функция strcpy Прототип функции strcpy таков char strcpy char target, const char source Функция копирует строку source в строку target. Функция предполагает, что целевая строка имеет размер, достаточный для того, чтобы вместить содержимое строки-источника. Пример char name 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. Функция не выполняет ни усечение, ни заполнение строки.

Пример char str1 Pascal char str2 Hello there strcnpy strl, str2, 5 Переменная strl содержит строку Hellol . Заметьте, что символ l строки-приемника, следующий за скопированной частью строки, сохранился. Определение длины строки При работе со строками часто бывает нужно знать длину строки. Функция strlen Функция strlen возвращает количество символов в строке, в которое не включается нуль-

терминатор. Прототип функции strncpy таков size t strlen const char string , Функция strlen возвращает длину строки string. size t - это имя, приписанное типу unsigned int оператором typedef. Пример char str 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 t num Функция добавляет к содержимому целевой строки num символов из строки-источника и возвращает указатель

на целевую строку. char strl 81 Hello I am char str2 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 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 равна 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 t num Функция сравнивает первые num символов строк strl и str2.

Возвращает в качестве результата сравнения целую величину 0 когда 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 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. Пример char str 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 stdio.h см. файл Ex02.cpp include string.h 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, double yVal point assign point pt Разделы класса Классы С имеют три различных уровня доступа к своим элементам - как к данным, так и к функциям Закрытые частные элементы Защищенные элементы Открытые элементы К данным в закрытом разделе имеют доступ только функции-элементы класса. Классам-потомкам запрещен доступ к закрытым данным своих 6азовых классов.

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

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

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

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

Сначала вы должны Сначала необходимо указать имя класса т.н. квалификатор , а затем, через два двоеточия , имя функции. В качестве примера рассмотрим такой класс class point protected double x double y public point double xVal, double yVal double getX другие функции-элементы Определения конструктора и функций-элементов должны выглядеть так 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, double yVal point assign point pt int main point p1 point p2 10, 20 point p3 p2 p1.assign p2 cout p1.getX p1.getY endl cout p2.getX p2.getY endl cout p3.getX p3.getY endl return 0 Конструктор копии создает объект класса, копируя при этом данные из существующего объекта класса. В С имеются следующие особенности и правила работы с конструкторами Имя конструктора класса должно совпадать с именем класса.

Нельзя определять тип возвращаемого значения для конструктора, даже тип void. Класс может иметь несколько конструкторов или не иметь их совсем. Конструктором по умолчанию является конструктор, не имеющий параметров, или конструктор, у которого все параметры имеют значения по умолчанию. Рассмотрим два примера с фрагментами объявления конструкторов. класс с конструктором без параметров class point1 protected double x double y public point1 другие

функции-элементы конструктор класса имеет параметры со значениями по умолчанию class point2 protected double x double y public point2 double xVal 0, double yVal 0 другие функции-элементы Конструктор копии создает объект класса на основе существующего объекта. Например class point protected double x double y public point point double xVal 0, double yVal 0 point const point pt другие функции-элементы Объявление объекта класса, которое может содержать параметры

и, в качестве параметра, имя уже существующего объекта, влечет за собой вызов конструктора. Но какой из конструкторов будет использоваться в каждом конкретном случае? Ответ зависит от того, как много конструкторов вы объявили и с какими аргументами вы объявляете объект класса. Например, рассмотрим следующие объявления объектов последней версии класса 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 другие функции-элементы

Деструкторы в С имеют следующие особенности и подчиняются следующим правилам Имя деструктора должно начинаться со знака тильды , за которым должно следовать имя класса. Нельзя определять тип возвращаемого значения, даже тип void. Класс может иметь только один деструктор или ни одного. В последнем случае компилятор создаст деструктор по умолчанию.

Деструктор не должен иметь параметров. Исполняющая система автоматически вызывает деструктор класса, когда объект класса выходит за пределы области действия и может быть удален, или удаляется явным образом. см. 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, double wide double calcArea class Вох public Rectangle protected double height public Box double len, double wide, double height double getHeight const assign double len, double wide, double height double calcVolume см. LIST8-3.CPP Виртуальные функции Мы уже упоминали о полиморфизме - важной особенности объектноориентированного программирования. Рассмотрим следующий пример 6 include iostream.h 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 y.B 3 endl 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 returnType functionName список параметров class className2 public className1 функции-элементы virtual returnType functionName список параметров Пример 7, показывающий, как при помощи виртуальных функций можно реализовать полиморфное поведение классов X и

Y include iostream.h 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 y.B 3 endl return 0 Этот пример выведет вам правильное значение 13.5, потому что в результате вызова наследуемой функции X B, вызывающей функцию

A, в качестве функции A во время выполнения программы будет использована замещающая функция Y A. Правило виртуальной функции Правило виртуальной функции гласит Виртуальная однажды - виртуальна всегда . Это означает следующее. Если вы объявили функцию как виртуальную в некотором классе, то в классах-потомках, переопределяющих эту функцию, она также будет виртуальной, но только если она имеет тот же список параметров.

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

В С положение несколько иное. Вы можете объявлять невиртуальные перегруженные функции, совпадающие по имени с виртуальными функциями, но имеющие другой список параметров. И, кроме того, вы не можете наследовать невиртуальные функции, имя которых совпадает с виртуальными функциями. Рассмотрим пример 8, иллюстрирующий сказанное. include iostream.h class A public A virtual void foo char c cout virtual A foo returns c endl class

B public A public B void foo const char s cout B foo returns s endl void foo int i cout B foo retuzns i endl virtual void foo char c cout virtual B foo returns c endl class C public B public C void foo const char s cout C foo returns s endl void foo double x cout C foo returns x endl virtual void foo char c cout virtual C foo returns c endl 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 returnType friendFunction список параметров Пример 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 operator operatorSymbol operand унарная дружественная операция friend returnType operator operatorSymbol operand бинарная дружественная операция friend returnType operator operatorSymbol 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 const char 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 String sl, String s2 friend int operator String sl, String s2 friend int operator String s1, String s2 friend int operator ! String sl, String s2 Код, который вы пишете, будет использовать операции и дружественные операции точно так же, как и предопределенные операции. Следовательно, вы можете создавать операции, чтобы поддерживать действия над классами, моделирующими,

например, комплексные числа, строки, векторы и матрицы. Эти операции дают возможность вам записывать выражения в более привычной форме, чем использование вызовов функций. Виртуальные функции Мы уже упоминали о полиморфизме - важной особенности объектно-ориентированного программирования. Рассмотрим следующий пример 6 include iostream.h 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 y.B 3 endl 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 returnType functionName список параметров class className2 public className1 функции-элементы virtual returnType functionName список параметров Пример 7, показывающий, как при помощи виртуальных функций можно реализовать полиморфное поведение классов X и Y include iostream.h 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 y.B 3 endl return 0 Этот пример выведет вам правильное значение 13.5, потому что в результате вызова наследуемой функции X B, вызывающей функцию A, в качестве функции A во время выполнения программы будет использована замещающая функция

Y A. Правило виртуальной функции Правило виртуальной функции гласит Виртуальная однажды - виртуальна всегда . Это означает следующее. Если вы объявили функцию как виртуальную в некотором классе, то в классах-потомках, переопределяющих эту функцию, она также будет виртуальной, но только если она имеет тот же список параметров. Если переопределенная функция в классе-потомке имеет другой список параметров, то ее версия из базового

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

по имени с виртуальными функциями, но имеющие другой список параметров. И, кроме того, вы не можете наследовать невиртуальные функции, имя которых совпадает с виртуальными функциями. Рассмотрим пример 8, иллюстрирующий сказанное. include iostream.h class A public A virtual void foo char c cout virtual A foo returns c endl class B public A public B void foo const char s cout B foo returns s endl void foo int i cout

B foo retuzns i endl virtual void foo char c cout virtual B foo returns c endl class C public B public C void foo const char s cout C foo returns s endl void foo double x cout C foo returns x endl virtual void foo char c cout virtual C foo returns c endl 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 returnType friendFunction список параметров Пример 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 operator operatorSymbol operand унарная дружественная операция friend returnType operator operatorSymbol operand бинарная дружественная операция friend returnType operator operatorSymbol 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 const char 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 String sl, String s2 friend int operator

String sl, String s2 friend int operator String s1, String s2 friend int operator ! 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 Stringa mapChar char find, char replace вы можете написать следующий оператор обработки объекта класса String s.upperCase .reverse .mapChar , Что может случиться, если класс полагается на конструктор копии, созданный компилятором, и при этом класс использует указатели в качестве элементов-данных?

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

Могу ли я использовать указатель при создании объекта класса? Да, можете, но в этом случае вы должны использовать операции new и delete, чтобы распределять и освобождать память для данного объекта. Вот пример, использующий класс Complex. Не забудьте, что для обращения к элементам классов или структур используется операция если вы ссылаетесь на них при помощи указателей. Complex pC pC new

Complex операции с объектом, к которому обращаются по указателю pC delete pC или Complex pC new Complex операции с объектом, к которому обращаются по указателю pC delete pC Контрольные вопросы 1. Найдите ошибку в следующем объявлении класса class String char str unsigned len String String const String s String unsigned size, char String unsigned size String assign

String s String unsigned getLen const char getString другие функции-элементы 2. Найдите ошибку в следующем объявлении класса class String protected char str unsigned len 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 void open 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. открыть поток для ввода fstream f f.open simple.txt , ios in открыть поток для вывода fstream f fstream f f.open simple.txt , ios out открыть

поток ввода вывода для двоичных данных fstream f fstream f f.open simple.txt , ios in ios out ios binary Внимание Классы файловых потоков предусматривают конструкторы, которые выполняют действия и имеют такие же параметры функции-компонента open. Функция close закрывает поток и освобождает использовавшиеся ресурсы. Эти ресурсы включают буфер памяти для операции потокового ввода вывода. Функция-компонент close Прототип для функции close void close

Пример 2. fstream f открыть поток f.open simple.txt , ios in работа с файлом закрыть поток f.close Stream-библиотека C включает в себя набор основных функций, которые контролируют состояние ошибки потоковой операции. Эти функции включают следующие Функция good возвращает ненулевое значение, если при выполнении потоковой операции не возникает ошибки. Объявление функции good int good Функция fail возвращает ненулевое значение, если при выполнении потоковой операции возникает ошибка.

Объявление функции fail int fail Перегруженная операция ! применяется к экземпляру потока для определения состояния ошибки. 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. fstream f char textLine MAX f.open sample.txt , ios in while !f.eof f.getline textLine, MAX cout textLine endl 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. const MAX 80 char buff MAX 1 Hello World! int len strlen buff 1 fstream f f.open CALC.DAT , ios out ios binary f.write const unsigned char len, sizeof len f.write const unsigned 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. const MAX 80 char buff MAX 1 int len fstream f f.open CALC.DAT , ios in ios binary f.read unsigned char len, sizeof len f.read unsigned char 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 С конца файла Пример const BLOCK SIZE 80 char buff BLOCK SIZE Hello World! f.open CALC.DAT , ios in ios out ios binary f.seekg 3 BLOCK SIZE продвинутся к блоку 4 f.read const unsigned char buff,

BLOCK SIZE cout buff endl 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 void copyStream 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, используя неупорядоченные данные инициализирующего списка. Элементы этого списка доступны при использовании данных-указателей.



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

Поделись с друзьями, за репост + 100 мильонов к студенческой карме :

Пишем реферат самостоятельно:
! Как писать рефераты
Практические рекомендации по написанию студенческих рефератов.
! План реферата Краткий список разделов, отражающий структура и порядок работы над будующим рефератом.
! Введение реферата Вводная часть работы, в которой отражается цель и обозначается список задач.
! Заключение реферата В заключении подводятся итоги, описывается была ли достигнута поставленная цель, каковы результаты.
! Оформление рефератов Методические рекомендации по грамотному оформлению работы по ГОСТ.

Читайте также:
Виды рефератов Какими бывают рефераты по своему назначению и структуре.