
Штерн В. - Основы C++. Методы программной инженерии - 2003
.pdf30 |
Часть I • Введение в программирование на СФ+ |
Наша первая программа C + + продемонстрировала следующие компоненты, представленные в программе C++:
•Директивы препроцессора
•Комментарии
•Описания и определения
•Операторы и выражения
•Функции и вызовы функций
В следующих разделах мы подробнее обсудим использование компонентов программы разного вида.
Директивы препроцессора
в большинстве языков компилятор видит то, что программист записывает в ис ходный файл. В C + + это не так. Компилятор — не первое инструментальное средство, обрабатывающее исходный код в ходе его превращения в исполняемую программу. Первый инструмент — препроцессор. Что это такое? Интересное но вовведение C + + , унаследованное от С. Цель препроцессора — уменьшить объем исходного кода, подготавливаемого программистом в процессе разработки ПО (или изучаемого при отладке и сопровождении).
Препроцессор обрабатывает исходный код и передает результаты компилято ру. Большинство операторов программы препроцессором игнорируются и переда ются компилятору без изменения. Препроцессор обращает внимание только на директивы препроцессора (и на относящиеся к ним операторы).
Директивы препроцессора начинаются с "#" и занимают всю строку. На одной строке нельзя разместить несколько директив. Если директива не помещается на одной строке, ее можно продолжить на следующей, но предыдущая строка должна заканчиваться специальным символом продолжения "\". "Решетка" (#) должна быть первым символом в строке. А как же свободный формат исходного кода C++? В предыдущей главе говорилось о том, что форматировать исходный код C+ + можно любым удобным (программисту, но не компилятору) образом. Однако формально директивы препроцессора не являются частью языка C + + (или С), как препроцессор — не часть компилятора.
Конечно, на практике без директив препроцессора нельзя написать даже простую программу C+ + , но теоретически эти директивы — не часть языка! На практике препроцессоры поставляют производители компиляторов, но компи ляторы и препроцессоры не связаны. Позднее требование было ослаблено: сим вол "#" должен быть не первым символом строки, а первым отличным от пробела символом.
В листинге 2.1 используются две директивы препроцессора #inclucle. Эта директива приводит к прямой подстановке текста: препроцессор считывает весь файл, имя которого задается в директиве препроцессора, и заменяет директиву содержимым этого файла (без обработки, как есть). Такой подход можно исполь зовать для комбинирования нескольких исходных файлов в одно целое. Чаще всего данная директива применяется для вставки заголовков функций, описывающих задействованные в исходном коде функции.
Имена этих заголовочных файлов заключаются в угловые скобки. Это говорит препроцессору, что нужно найти данный исходный файл в стандартном каталоге заголовочных файлов. Например, директива #include в первой программе задает два заголовочных файла. Первый нужен для использования функции pow(), второй — для операции << и объекта cout. Подробнее о функциях, операциях и объектах рассказывается дальше. Это лишь один из примеров сложности C + + . Чтобы рассказать даже о простой программе, нужно обратиться к помощи компо нентов, которые будут понятны лишь при дальнейшем изучении.
Глава 2 • Быстрый старт: к р а т к и й обзор C-^-i-
#inclucle <iostream> #include <math> using namespace std;
Последняя строка в этом сегменте кода из первой программы представляет директиву using namespace. Это не директива препроцессора, а новое средство языка. Оно указывает компилятору, что нужно распознавать код, поставляемый из заголовочных файлов. Старые компиляторы могут игнорировать эти три строки. В таком компиляторе директива using namespace использоваться не будет. Имя за головочного файла в старом коде должно иметь расширение . h. Следовательно, первые три строки программы должны быть заменены следуюш.ими двумя строками:
#include <iostream.h> #include <math.h>
Директива указывает препроцессору, что нужно найти в каталоге компилятора файлы include (функцию pow(), которая возводит в степень вещественное число, объект cout, представляющий стандартный вывод, например на экран, и опера цию <<, отображающую значения на экране монитора).
Другие заголовочные файлы могут описывать функции, не являющиеся частью стандартной библиотеки. Обычно их пишут работающие над проектом програм мисты. Если имена этих файлов указываются в директивах #include, то они заключаются в двойные кавычки, например:
#include "c:\work\mydef.h"
Данная директива сообщает препроцессору, что нужно скопировать в исходный файл содержимое файла mydef.h из каталога c:\work. В приведенном примере используется абсолютное имя маршрута. Это удобно, если исходный файл пере мещается в другой каталог, а заголовочный остается в прежнем. В таком случае данную директиву не нужно будет изменять. Между тем часто все дерево катало гов проекта перемещается в новое место. Если изменяется местоположение за головочного файла, исходный файл на месте использования придется изменить. Чтобы избежать этого, программисты указывают в директивах #include относи тельный путь.
После обработки препроцессором сама директива #include из исходного файла убирается и не передается компилятору.
Директива #include очень важна. Без нее программа компилироваться не будет. Такие директивы не очень с/южны — нужно знать лишь, что функция требует дан ного заголовочного файла. Справочные средства компилятора напомнят об этом.
Определение константы в листинге 2.1 инициализирует переменную с симво лическим именем PI значением 3,1415926. (Удобно использовать для символи ческих констант буквы верхнего регистра — так легче будет отличить их от переменных, значения которых меняются в ходе выполнения программы.) Компи лятор обработает следующую строку кода:
double х=Р1, у=1, z; / / определение переменных
Исполняемый код скопирует значение по адресу PI в ячейку по адресу х. Альтернативный метод введения константы PI состоит в использовании директивы #def ine. Директива #def ine применяется также для подстановки текста. Ее пер вый аргумент задает текст для подстановки, а второй — подставляемый вместо него текст. Когда препроцессор находит далее по тексту программы символ, соот ветствующий первому аргументу директивы, он заменяет его вторым аргументом директивы. В примере из первой программы C++:
#define PI 3.1415926
f |
32 I |
Часть I ^ Введе- |
Данная директива сообщает препроцессору, что нужно заменить все вхожде ния PI на 3,1415926. Когда препроцессор обрабатывает строку кода:
double х=Р1, у=1, z; |
/ / определение переменных |
он передает компилятору следующую строку:
double х=3.1415926, у - 1, z;
Обратите внимание, что препроцессор удаляет комментарий, так что компиля тор его не видит. (Подробнее об этом — в следующем разделе.)
Директиву #define можно использовать для определения макрокоманд— по следовательности вычислений, подставляемых в исходный код вместо простого символа (как в приведенном примере). Логически они используются в програм мном коде подобно функциям — ряд операций комбинируется под одним именем. Макрокоманды работают быстрее функций и очень популярны в С. В C++ вместо макрокоманд применяются встраиваемые функции (inline). Вот почему макро команды не обсуждаются здесь, хотя несколько лет назад программист, приме няющий язык С, просто обязан был знать, как писать макрокоманды. Макрокоманды — это замечательно, но они являются источником трудно обнару живаемых ошибок.
Другой важный набор директив препроцессора управляет условным вычисле нием. Директива #ifdef включает последующий исходный код, если символиче ское имя в данной директиве определено. Область действия данной директивы ограничивается директивой #endif. Например, следующий код включается в про грамму, если при компиляции имя CPLUSPLUS определено. В противном случае препроцессор убирает этот код — он не нужен, если программа компилируется как программа на языке С.
#ifdef CPLUSPLUS
. . . то, что нужно для программы на C++ #endif
Обратите внимание, что имя CPLUSPLUS не обязано содержать значение — для #ifdef достаточно указать его в директиве #define. Отметим также, что символи ческие имена записываются в верхнем регистре. Это не обязательно, но используется как общее соглашение по программированию. Другое популярное соглашение — имена в нижнем регистре, начинающиеся с двух символов под черкивания.
#define cplusplus #ifdef „cplusplus
. . . т о , что нужно для программы на C++ #endif
Еще один метод указания области действия директивы #if def — применение директивы #else. Следующий за директивой #else код (пока не встретится #endif) не включается в программу, а код после #ifdef — включается, или наоборот. Например:
#define МТ #ifdef МТ
#define NFILE 40
#else
#define NFILE 20 #endif
Главе 2 # Быстрый старт: краткий обзор С^^^ \ 33 |
Данный код аналогичен тому, который можно найти в заголовочных файлах. Если МТ определено, то для числа файлов задается ограничение 40, а если убрать определение из исходного файла — 20.
Директиве #ifdef обратна директива #ifnclef — она включает в файл последу ющий исходный код (до #else или ttendif), если указанное в директиве имя не определено. Если имя определено, то последующий код пропускается. При нали чии директивы #else следующий после нее код (до #enclif) передается компилято ру. Следующий пример также заимствован из заголовочного файла:
#ifclef NULL #define NULL О
#endif
Это очень популярный метод, гарантирующий, что имя определено в программе и только один раз, даже если оно повторяется в нескольких файлах. Если такое определение встретится в другом файле снова, то оно просто игнорируется.
Условные директивы препроцессора часто используются для переносимости программ. Если приложение должно работать в нескольких средах, а код его в каждой среде примерно одинаков, за исключением локализованных сегментов, то эти сегменты можно включить в условные директивы. При переносе системы из одной среды в другую нужно лишь заменить директиву #define для одного имени на директиву #def ine для другого.
Все это звучит просто и эффектно, но реальность, конечно, сложнее, и в дирек тивах препроцессора легко запутаться. Вот почему важно ограничить их примене ние простыми директивами #include для заголовочных файлов. Другие директивы будете использовать, когда лучше познакомитесь с языком.
Комментарии
ВC++ имеются комментарии двух типов: блоки комментариев и комментарии
вконце строки. Блоки комментариев начинаются со стандартного символа "/*" и заканчиваются символами "*/". Комментарии в конце строки начинаются с двух символов "//" и заканчиваются (как можно догадаться) концом строки, т. е. следу ющая строка — это строка исходного файла. Двузначные символы очень рас пространены в C+ + . Большая их часть унаследована из С. Применение таких символов для операций и в других контекстах вместо дополнительных ключевых слов — один из способов, позволивших создателям С разработать язык, в кото ром всего 30 ключевых слов (это убедит любого, что С — на самом деле очень компактный язык).
Знаки в двузначных символах (в C++ все, а не только комментарии) не должны разделяться пробелами (или другими символами).
Текст в комментариях эквивалентен пробелу и для компилятора синтаксически невидим. Это происходит потому, что препроцессор удаляет комментарии перед компиляцией исходного кода. Вот пример блока комментария:
/ * Комментарии предназначены для человека, а не для компилятора.
В комментарии может содержаться любой символ, включая табуляцию, перевод строки, / / , / * . Допускается форматирование комментариев для облегчения чтения текста. */
Многие программисты применяют блоки комментариев в качестве предисло вия к функции или при значительных изменениях в алгоритме. В этих комментари ях описывается назначение, входные и выходные данные (результат вычислений), а также другие функции, вызываемые для выполнения задачи. Часто документиру ется история обновления исходного кода, указывается первоначальный автор и дата первой версии, авторы и даты изменений, цель каждого изменения. Точный
34
формат этих блоков в каждой организации разный. Конечно, надо придерживаться единообразного формата. А еще лучше выработать привычку составлять коммен тарии. Может ли быть что-нибудь более важное, чем комментарии? Да. Это обновление комментариев при изменении исходного кода.- Нет ничего хуже при сопровождении программы, чем неверные комментарии.
Внимание в с допускались только блоки комментариев. Если программист хотел прокомментировать отдельные строки исходного кода, то для этого использовались блоки комментариев (так же, как в приведенной выше первой программе на C++). Похоже, создатели языка С считали, что программистов не затруднит заканчивать комментирование каждой строки символами "*/"•
В листинге 2.2 показана первая программа СН- + , написанная для старого компилятора. Здесь для библиотечного заголовочного файла указывается расши рение . h, вместо const применяется директива #clefine и используются блочные комментарии в стиле С.
Листинг 2.2. |
Первая программа C + + с блочными комментариями |
|
|
|
#include <iostreafn.h> |
/* директива |
препроцессора |
||
«include <math.h> |
/* директива |
препроцессора |
||
#define PI3.1415926 |
/* функция возвращает целое значение */ |
|||
int main(void) |
||||
{ |
|
|
|
|
double x=PI, y=1, z; |
определение переменных |
*/ |
||
cout « |
"Добро пожаловать в мирC++!'' « endl; |
/* ( |
|
|
/* вызов функции */ |
|
|||
z = у + 1; |
/* операция присваивания |
*/ |
||
у = pow(x, z); |
/* вызов функции */ |
|
||
cout « |
"B этом мире pi вквадрате равно " « У « |
endl |
|
|
cout « |
"Приятного дня!" « endl; |
|
|
|
return 0; |
|
|
|
|
|
|
/ * конец блока функции * / |
|
Чтобы избежать набора лишних символов, создатели C++ добавили в язык строчные комментарии, которые работают аналогично блочным: все, что находит ся между V/" и концом строки, невидимо для компилятора.
Между двумя типами комментариев в C++ есть два отличия. Первое очевидно: строчный комментарий не может занимать несколько строк, как блок комментари ев. Именно поэтому блоки комментариев были введены в язык с самого начала. Впрочем, это различие не столь значительно, если учесть, что строчный коммента рий может занимать всю строку.
/ / |
Комментарии предназначены для человека, а не для компилятора. |
|
/ / |
В комментарии может содержаться любой символ, включая табуляцию, |
|
/ / |
перевод строки, |
/ / , / * . Допускается форматирование комментариев |
/ / |
для облегчения |
чтения текста. |
Второе отличие более тонкое. Строчный комментарий может содержать любой символ, включая другие символы комментария. Завершающим ограничителем будет переход на новую строку (код ASCII 12). Блочный комментарий содержит любой символ, включая другие символы комментария, за исключением "*/". Дру гими словами, блочные комментарии не могут быть вложенными. Препроцессор пропустит вложенный открывающий символ "/*" как часть комментария, а вло женный "*/" интерпретирует как конец комментария, передав остальную его
Глава 2 • Быстрый старт: краткий обзор С Ф + |
35 |
часть и второй закрывающий символ "*/" компилятору. Компилятор даст сообще ния об ошибках.
|
|
|
/ * Здесь второй открывающий символ / * невидим */ |
|
||||||
|
|
|
и компилятор интерпретирует |
вторую строку не как комментарий */ |
||||||
|
|
|
Это скорее проявление давнего непонимания программистов разработчиками |
|||||||
|
|
|
компиляторов (и препроцессоров), а не особенностей применяемых инструмен |
|||||||
|
|
|
тальных средств и их "интеллектуальности". С (и C+ + ) благоволят к разработ |
|||||||
|
|
|
чикам инструментария. Кроме того, каждый программист знает, что вложенные |
|||||||
|
|
|
блоки комментариев не допускаются. Так что в случае ошибки программист дол |
|||||||
|
|
|
жен быстро понять, что произошло. |
|
|
|
|
|||
|
|
|
Так зачем же нужны вложенные комментарии? Часто желательно поэкспери |
|||||||
|
|
|
ментировать с альтернативными версиями кода, особенно когда нет уверенности |
|||||||
|
|
|
в том, как они работают. Возьмем, к примеру, первую программу на C+ + . Что, |
|||||||
|
|
|
если захочется посмотреть, как она работает без трех строк в середине? Простей |
|||||||
|
|
|
ший способ — закомментировать эти строки. |
|
||||||
Листинг 2.3. |
Первая программа C + + с закомментированными строками |
|
||||||||
«include |
<iostream.h> |
|
|
|
|
/ * |
директива препроцессора */ |
|||
«include |
<math.h> |
|
|
|
|
|
|
|
||
«define |
PI |
3.1415926 |
|
|
|
|
|
|
|
|
int main(void) |
|
|
|
/ * |
функция возвращает целое значение */ |
|||||
{ |
|
|
|
|
|
|
|
|
|
|
double x=PI, y=1, z; |
|
|
|
/ * |
определение переменных */ |
|||||
cout |
« |
|
"Добро пожаловать |
в мир C++!" « endl; |
/ * |
вызов функции */ |
|
|||
/ * начало исключаемого из программы блока |
|
|
|
|
|
|
||||
Z = у + 1; |
|
|
|
/ * |
операция присваивания */ |
|||||
у = pow(x, z); |
|
|
|
/ * |
вызов функции */ |
|
||||
cout |
« |
|
"В этом мире pi в квадрате равно " |
« |
у « |
endl; |
|
|
||
*/ |
|
|
|
|
|
|
/ / |
конец исключаемого |
блока |
|
cout |
« |
|
"Приятного дня!" « |
endl; |
|
|
|
|
|
|
return |
0; |
|
|
|
|
|
|
|
||
} |
|
|
|
|
|
|
/ * |
конец блока функции |
*/ |
|
|
|
|
Препроцессор корректно |
обработает |
комментарий первой строки блока |
|||||
|
|
|
(z = у + 1;), |
но воспримет символ */ в ее конце как конец комментария, после |
||||||
|
|
|
чего передаст компилятору две следуюш.их строки. Компилятору будет передан |
|||||||
|
|
|
и символ "*/" в конце блока комментария, на что он даст сообщение об ошибке |
|||||||
|
|
|
(разные компиляторы выведут разные сообщения об ошибках). |
Compiling...
c:\data\ch02.cpp
c:\data\ch02.срр(11) : warning С4138: ' * / ' found outside of comment c:\data\ch02.cpp(ll) : error C2059: syntax error : V '
DEMO.EXE - 1 error(s), 1 warning(s)
Вот еще одна причина, по которой лучше использовать комментарии не в конце строки, а выделять для этого отдельную строку. Если бы это было сделано в дан ном примере, то все прошло бы корректно. Конечно, можно применять д^^peктивы условной компиляции, описанные в предыдущем разделе, но они сложнее, чем комментарии, могут приводить к конфликтам имен и предназначены, скорее, для переноса программы в разные вычислительные среды, чем для экспериментов с ее выполнением.
II 36
И енде пара слов о комментариях. В C + + строки представляются как последо вательности символов в двойных кавычках. В таких строках символы комментария интерпретируются как литералы, а не как знак комментария. Другими словами, в строках заключенные в двойные кавычки комментарии не работают. Рассмотрим следующий оператор:
cout « "Hello / * there */ world « endl;
Этот оператор выведет не Hello world, а Hello /* there */ world.
О с т о р о ж н о ! Блоки комментариев не работают внутри строк в двойных кавычках. Для этого нужно вырезать текст, а не комментировать его.
^ F
С о в е т у е м Для удобства чтения могут (и должны) использоваться пробелы, разделяющие логически различные сегменты кода, однако злоупотреблять этим не следует, поскольку исходный код будет слишком растянут по вертикали.
Описания и определения
Когда программист проектирует логику вычислений, результаты одного шага часто используются в качестве исходных данных для другого. Следовательно, эти результаты должны храниться в памяти и при необходимости извлекаться снова.
Вприведенной выше первой программе C-f--b к значению у прибавляется единица,
арезультат используется в качестве второго аргумента при вызове функции pow (она возводит первый аргумент в степень второго аргумента). Чтобы сохранить значение в памяти для быстрого доступа и последуюш,его использования, оно должно иметь физический адрес. Поскольку мы не хотим применять в исходном коде программы физические адреса, значению следует присвоить символическое имя, которое программист будет использовать в программе.
Влистингах 2.1 и 2.2 сумма значения, хранимого в ячейке у, и 1 сохраняются
вячейке z. Программиста не волнует, как увязываются эти имена с адресами. Это проблема разработчика компилятора. Программист должен решить, какие значения следует хранить в памяти, и какие имена использовать для обозначения этих значений. Имя, присваиваемое программистом ячейке памяти, называется идентификатором. На самом деле программисту нужно придумать идентифика торы не только для переменных программы, но и для констант, функций, типов данных и меток (об этом далее будет рассказано более подробно).
Синтаксические правила для идентификаторов просты: они могут начинаться только с буквы или подчеркивания ("_"), но не с цифры или другого специального символа. Иными символами идентификаторами могут быть буквы A:*TZ, а—Z, циф ры 0—9 и знаки подчеркивания. Теоретически обш^ее число символов не ограничи вается. На практике компилятор может не различать идентификаторы, у которых совпадают первые 31 символ (это старое ограничение на д/шну идентификатора). Если 31 символа недостаточно, следует поискать более короткие имена.
За исключением подчеркивания, других специальных символов (&, $, # и т. д.)
видентификаторах не допускается. Не разрешается также использовать внутри идентификатора пробелы.
Вполне допустимо начинать имя идентификатора с подчеркивания, но лучше избегать этого, так как с подчеркивания могут начинаться системные идентифика торы. Применяйте подчеркивание только внутри идентификатора для разделения компонентов его имени (sum_of_squares). Еш,е один популярный метод состоит в том, чтобы начинать новый компонент имени с буквы в верхнем регистре (SumOfSquares).
Глава 2 » Быстрый старт: краткий обзор С+4^ |
[ 37 |
| |
Признаком хорошего вкуса и благоразумия считаются мнемонические имена идентификаторов. Это означает, что имя идентификатора связано с его назначе нием в программе. С этой точки зрения имена в нашей первой программе на С+4- (х, у, z) не особенно хороши. Их можно применять, если данные значения исполь зуются в программе всего несколько раз и в относительно простых вычислениях. Имя PI лучше — оно говорит о смысле значения (по крайней мере тем, кто знает, что это такое).
Внимание в C++ различается регистр символов. Это означает, что компилятор будет различать такие идентификаторы как cnt, Cnt и CNT. Все это — разные имена, что нередко бывает источником ошибок в программе, особенно, если программисты раньше работали с языками, не различающими регистр символов.
Как уже упоминалось выше, определяемые в программе константы часто обозначаются буквами в верхнем регистре. Так лучше отличать их от объектов, значения которых при выполнении программы меняются (таких, как PI в приве денной выше программе C-I- + ).
Ключевые слова C/C++ зарезервированы и не могут использоваться в про грамме в качестве идентификаторов. Все они записываются буквами в нижнем регистре. Вот список ключевых слов, обш.их для С и C+ + :
auto |
break |
case char |
const continue |
default |
do double else enum extern float |
|
for |
goto i f |
int |
long |
register return |
short signed sizeof static struct |
|
switch typedef |
union |
unsigned void volatile |
while |
A вот ключевые слова, зарезервированные в языке C++, но не в С:
asm bool catch |
class const_cast delete dynamic_cast explicit export false |
friend inline |
mutable namespace new operator private protected public |
reinterpert_cast static_cast template this throw true t r y typeid typename |
|
using v i r t u a l |
wchar_t |
He пытайтесь сейчас запомнить все эти идентификаторы. Более того, если попытаться использовать данные ключевые слова как идентификаторы перемен ных, компилятор сообилит, что делать этого нельзя. Так что данный список приве ден не для того, чтобы запоминать эти ключевые слова, а чтобы показать, почему компилятор "жалуется", если их применять в качестве идентификаторов.
В других языках с менее строгими ограничениями программист может ввести подходяш,ий идентификатор и указать его с левой стороны присваивания, особенно не задумываясь: sum_of.squares = 0;
Если сделать это в программе на языке C+ + , то компилятор сообш^ит, что sum_of_squares = 0; — необъявленный идентификатор. В C++ это синтаксиче ская ошибка. Прежде чем использовать имя для переменной в C+ + , его нужно определить.
Имена переменных обозначают адреса в памяти компьютера, где содержатся типизированные значения. Эти значения могут изменяться при выполнении про граммы.
А как с именем PI в приведенной выше программе? В листинге 2.1 оно опреде лено как константа, и попытки изменить данное значение (например, PI = 0;) дадут синтаксическую ошибку. В листинге 2,2 это также константа, заданная Б директиве препроцессора #define. После подстановки значения препроцессором оно становится неизменяемым значением-константой. Например, оператор PI = 0; будет преобразован в 3.1415926 = 0;, а это синтаксическая ошибка.
Переменные в памяти содержат типизированные значения, т. е. программист должен определить тип значения, хранимого в переменной. Тип переменной опи сывает диапазон допустимых для нее значений и разрешенные операции с этими
38 Часть I # Введение в программирование на С-и^-
значениями. Определение устанавливает связь между идентификатором и типом. Каждое определение заканчивается точкой с запятой, например:
i nt num;
double sum_of_squares; char letter;
Первое определение говорит о том, что идентификатор num обозначает цело численную переменную. Его размер — четыре байта (32 бита), а диапазон значе ний — от -2147483648 до +2147483647 (позднее поясняется, что размер типа зависит от машины).
Для данного типа действительны четыре арифметические операции, взятие по модулю (получение остатка), сравнения, сдвиг влево или вправо, логические операции, инкремент и декремент (положительное и отрицательное приращение, об этом подробнее — см. главу 3).
Во втором определении говорится, что идентификатор sum_of_squares исполь зуется для переменной двойной точности с плавающей точкой. Она имеет размер до 8 байт, а абсолютное значение достигает 1.7976931348623158е+308 (здесь e-f 308 означает 10 в степени 308 — довольно большое число). Для данного типа предусмотрены четыре арифметические операции, инкремент и декремент, срав нения.
В третьем определении используется ключевое слово char, означающее, что идентификатор применяется для символьной переменной и может содержать зна чения, представляющие символы (согласно кодам ASCII). Что касается операций, то в C++ символы интерпретируются как целые значения одинарной точности.
Целые значения могут применяться в счетчиках и математических вычислениях. Целочисленные операции — на любой машине самые быстрые. Вот почему целые используются во всех случаях, где достаточно их диапазона и точности. Числа с плавающей точкой имеют дробную часть. Они применяются в коммерческих
инаучных расчетах, где точности целых чисел недостаточно (или мало диапазона
вдва триллиона).
Принцип типа является фундаментальным и в функциональном, и в объектноориентированном программировании. Каждое значение, обрабатываемое в про грамме С+4-, должно иметь тип. Если тип используется некорректно, компилятор помечает это как синтаксическую ошибку. Такое может случиться, если один тип применяется там, где предполагается значение другого типа. Программист должен
позаботиться о корректном использовании типов в программе С+Н |
это одна |
из его основных задач. |
|
В C++ применяются только встроенные типы данных, т. е. типы данных, уже доступные в самом языке. Это целые (например, 65), числа с плавающей точкой (65.0) или символьные значения ("а").
Все они являются примитивными (или скалярными) типами, т. е. не допуска ется декомпозиция значения этих типов на компоненты, с которыми в программе можно манипулировать отдельно. Например, число двойной точности с плаваю щей точкой имеет целую и дробную части (а также экспоненциальную часть), но язык C++ не предусматривает возможности отдельного доступа к этим частям. Можно обращаться только ко всему значению. Таким образом, типы С+Н инструмент абстракции. Они позволяют сконцентрироваться на том, что нужно делать со значением, а не на том, как манипулировать с отдельными компонентами.
Бедность фундаментальных типов в C++ частично компенсируется введением некоторых вариаций целочисленных типов и типов с плавающей точкой — по размерам, диапазонам и точности. Однако это не намного меняет ситуацию. Более важно, что C++ поддерживает составные значения, агрегаты данных, позволяю щие комбинировать отдельные значения примитивов в массивы, структуры
иклассы. Значения составных типов включают в себя несколько компонентов,
иC++ предусматривает методы доступа к отдельным компонентам таких типов.
|
Глава 2 ^ Быстрый старт: к р о т к и й обзор C'^^-f |
| |
ЗУ |
\i |
Когда определение типа обрабатывается на этапе выполнения, для переменной |
|
|||
(примитива |
или составного типа) выделяется область памяти — переменная |
|
||
может использоваться для хранения и извлечения значений описанного для нее |
|
|||
типа. Здесь нет ничего особенного: таким образом работают все современные |
|
|||
языки со строгим контролем типов. |
|
|
|
|
Некоторые программисты для удобства чтения используют для каждого опре |
|
|||
деления отдельную строку исходного кода. Другие полагают, что это только за |
|
|||
трудняет чтение, и помещают несколько определений на одну строку: |
|
|
|
|
int num; |
double sum_of_squares; |
|
|
|
Если переменные имеют один тип, их можно определять отдельно. Каждое определение включает в себя имя типа и заканчивается точкой с запятой:
int а; int b; int с;
Разрешается и комбинировать эти определения: имя типа указывается лишь один раз, а имена переменных разделяются запятыми. Все определение заканчива ется точкой с запятой. Например:
int а, Ь, с; |
/ / допустимое сокращение |
Другими словами, область действия имени типа (здесь int) включает в себя имена всех переменных между именем типа и следуюш,ей точкой с запятой. Пере менные а, Ь, с будут целыми. Эти два стиля определения эквивалентны, но их не следует путать. Например, следуюш,ее определение является синтаксической ошибкой:
int а, Ь, |
int с; |
/ / ошибка |
А вот такие определения будут правильными: |
||
int а, Ь; |
int с; |
/ / нет ошибки |
Небольшая, но значительная разница. Программист, работаюш^ий с C++, всегда |
||
должен помнить о таких различиях. |
||
Для программиста разница ме>кду запятой и точкой с запятой критическая. |
||
Не путайте их. |
|
|
Многие программисты определяют переменные в начале блока функции или |
||
файла. Так сделано и в примере первой программы на C++. В языке С это един |
||
ственный способ определить переменные, однако C++ позволяет программисту |
||
делать это в середине программы — ближе к тому месту, где переменные исполь |
||
зуются. Листинг 2.4 показывает, как мог бы выглядеть листинг 2.1, если приме |
||
нить более гибкий способ определения переменных. |
Листинг 2.4. |
Первая программа C + + с определениями в середине программного кода |
|||||||
#inGlucie <iostream> |
|
/ / |
директива |
препроцессора |
||||
#include |
<cmath> |
|
/ / |
директива |
препроцессора |
|||
using namespace std; |
|
|
|
|
||||
/ / |
директива |
компилятора |
|
|
|
|
||
const double PI = 3.1415926; |
|
/ / |
определение константы |
|||||
int |
main(void) |
|
/ / |
функция возвращает целое значение |
||||
{ |
cout |
« |
"Добро пожаловать в мир C++!" « |
endl; |
/ / |
вызов функции |
||
double у=1, z; |
|
/ / |
определение переменных |
|||||
Z = у + 1; |
|
|
/ / |
операция присваивания |
||||
double х=Р1; |
|
/ / |
определение переменной |
|||||
у = pow(x, |
z); |
|
/ / |
вызов функции |
||||
cout |
« |
"В этом мире pi в квадрате равно |
" « у « |
endl; |
|
|
||
cout |
« |
"Приятного дня!" « endl; |
|
|
|
|
||
return |
0; |
|
|
|
|
|
||
} |
|
|
|
|
|
/ / |
конец блока функции |