Язык программирования C Пятое издание
Скачать 1.85 Mb.
|
$ echo $? В операционной системе Windows для этого применяется команда $ echo %ERRORLEVEL% Вызов компилятора GNU или Microsoft Конкретная команда, используемая для вызова компилятора С++, зависит от применяемой операционной системы и версии компилятора. Наибольшее распространение получили компилятор GNU и компилятор С++ из комплекта Microsoft Visual Studio. По умолчанию для вызова компилятора GNU используется команда g++: $ g++ -о prog1 prog1.cc где $ — это системное приглашение к вводу; -о prog1 — аргумент компилятора и имя получаемого исполняемого файла. Данная команда создает исполняемый файл по имени prog1 или prog1.exe, в зависимости от операционной системы. На операционной системе UNIX исполняемые файлы не имеют расширения, а в операционной системе Windows они имеют расширение .exe. Если пропустить аргумент -о prog1, то компилятор создаст исполняемый файл по имени a.out (на системе UNIX) или a.exe (на Windows). (Примечание: в зависимости от используемого выпуска компилятора GNU, возможно, понадобится добавить аргумент -std=c++0x, чтобы включить поддержку С++ 11.) Для вызова компилятора Microsoft Visual Studio 2010 используется команда c1: С:\Users\me\Programs> cl /EHsc prog1.cpp где C:\Users\me\Programs> — это системное приглашение к вводу; \Users\me\Programs — имя текущего каталога (или папки). Команда cl запускает компилятор, а параметр компилятора /EHsc включает стандартную обработку исключений. Компилятор Microsoft автоматически создает исполняемый файл с именем, которое соответствует первому имени файла исходного кода. У исполняемого файла будет суффикс .exe и то же имя, что и у файла исходного кода. В данном случае исполняемый файл получит имя prog1.exe. Как правило, компиляторы способны предупреждать о проблемных конструкциях. Обычно эти возможности имеет смысл задействовать. Поэтому с компилятором GNU желательно использовать параметр -Wall, а с компиляторами Microsoft — параметр /W4. Более подробная информация по этой теме содержится в руководстве программиста, прилагаемом к компилятору. Упражнения раздела 1.1.1 Упражнение 1.1. Просмотрите документацию по используемому компилятору и выясните, какое соглашение об именовании файлов он использует. Откомпилируйте и запустите на выполнение программу, функция main() которой приведена в разд. 1.1. Page 10/1103 Упражнение 1.2. Измените код программы так, чтобы функция main() возвращала значение -1. Возвращение значения -1 зачастую свидетельствует о сбое при выполнении программы. Перекомпилируйте и повторно запустите программу, чтобы увидеть, как используемая операционная система реагирует на свидетельство об отказе функции main(). 1.2. Первый взгляд на ввод-вывод В самом языке С++ никаких операторов для ввода и вывода (Input/Output — IO) нет. Их предоставляет стандартная библиотека (standard library) наряду с обширным набором подобных средств. Однако для большинства задач, включая примеры этой книги, вполне достаточно изучить лишь несколько фундаментальных концепций и простых операций. В большинстве примеров этой книги использована библиотека iostream. Ее основу составляют два типа, istream и ostream, которые представляют потоки ввода и вывода соответственно. Поток (stream) — это последовательность символов, записываемая или читаемая из устройства ввода-вывода некоторым способом. Термин "поток" подразумевает, что символы поступают и передаются последовательно на протяжении определенного времени.Стандартные объекты ввода и вывода В библиотеке определены четыре объекта ввода-вывода. Для осуществления ввода используется объект cin (произносится "си-ин") типа istream. Этот объект упоминают также как стандартный ввод (standard input). Для вывода используется объект cout (произносится "си-аут") типа ostream. Его зачастую упоминают как стандартный вывод (standard output). В библиотеке определены еще два объекта типа ostream — это cerr и clog (произносится "си-ерр" и "си-лог" соответственно). Объект cerr, называемый также стандартной ошибкой (standard error), как правило, используется в программах для создания предупреждений и сообщений об ошибках, а объект clog — для создания информационных сообщений. Как правило, операционная система ассоциирует каждый из этих объектов с окном, в котором выполняется программа. Так, при получении данных объектом cin они считываются из того окна, в котором выполняется программа. Аналогично при выводе данных объектами cout, cerr или clog они отображаются в том же окне. Программа, использующая библиотеку ввода-вывода Приложению для книжного магазина потребуется объединить несколько записей, чтобы вычислить общую сумму. Сначала рассмотрим более простую, но схожую задачу — сложение двух чисел. Используя библиотеку ввода-вывода, можно модифицировать прежнюю программу так, чтобы она запрашивала у пользователя два числа, а затем вычисляла и выводила их сумму. #include <iostream> Page 11/1103 int main() { std::cout << "Enter two numbers:" << std::endl; int v1 = 0, v2 = 0; std::cin >> v1 >> v2; std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl; return 0; } Вначале программа отображает на экране приглашение пользователю ввести два числа. Enter two numbers: Затем она ожидает ввода. Предположим, пользователь ввел следующие два числа и нажал клавишу <Enter>: 3 7 В результате программа отобразит следующее сообщение: The sum of 3 and 7 is 10 Первая строка кода (#include <iostream>) — это директива препроцессора (preprocessor directive), которая указывает компилятору[1] на необходимость включить в программу библиотеку ostream. Имя в угловых скобок — это заголовок (header). Каждая программа, которая использует средства, хранимые в библиотеке, должна подключить соответствующий заголовок. Директива #include должна быть написана в одной строке. То есть и заголовок, и слово #include должны находиться в той же строке кода. Директива #include должна располагаться вне тела функции. Как правило, все директивы #include программы располагают в начале файла исходного кода.Запись в поток Первый оператор в теле функции main() выполняет выражение (expression). В языке С++ выражение состоит из одного или нескольких операндов (operand) и, как правило, оператора (operator). Чтобы отобразить подсказку на стандартном устройстве вывода, в этом выражении используется оператор вывода (output operator), или оператор <<. std::cout << "Enter two numbers:" << std::endl; Оператор << получает два операнда: левый операнд должен быть объектом класса ostream, а правый операнд — это подлежащее отображению значение. Оператор заносит переданное значение в объект cout класса ostream. Таким образом, результатом является объект класса ostream, в который записано предоставленное значение. Выражение вывода использует оператор << дважды. Поскольку оператор возвращает Page 12/1103 свой левый операнд, результат первого оператора становится левым операндом второго. В результате мы можем сцепить вместе запросы на вывод. Таким образом, наше выражение эквивалентно следующему: (std::cout << "Enter two numbers:") << std::endl; У каждого оператора в цепи левый операнд будет тем же объектом, в данном случае std::cout. Альтернативно мы могли бы получить тот же вывод, используя два оператора: std::cout << "Enter two numbers:"; std::cout << std::endl; Первый оператор выводит сообщение для пользователя. Это сообщение, строковый литерал (string literal), является последовательностью символов, заключенных в парные кавычки. Текст в кавычках выводится на стандартное устройство вывода. Второй оператор выводит endl — специальное значение, называемое манипулятором (manipulator). При его записи в поток вывода происходит переход на новую строку и сброс буфера (buffer), связанного с данным устройством. Сброс буфера гарантирует, что весь вывод, который программа сформировала на данный момент, будет немедленно записан в поток вывода, а не будет ожидать записи, находясь в памяти. Во время отладки программисты зачастую добавляют операторы вывода промежуточных значений. Для таких операторов всегда следует применять сброс потока. Если этого не сделать, оставшиеся в буфере вывода данные в случае сбоя программы могут ввести в заблуждение разработчика, неправильно засвидетельствовав место возникновения проблемы.Использование имен из стандартной библиотеки Внимательный читатель, вероятно, обратил внимание на то, что в этой программе использована форма записи std::cout и std::endl, а не просто cout и endl. Префикс std:: означает, что имена cout и endl определены в пространстве имен (namespace) по имени std. Пространства имен позволяют избежать вероятных конфликтов, причиной которых является совпадение имен, определенных в разных библиотеках. Все имена, определенные в стандартной библиотеке, находятся в пространстве имен std. Побочным эффектом применения пространств имен библиотек является то, что названия используемых пространств приходится указывать явно, например std. В записи std::cout применяется оператор области видимости :: (scope operator), позволяющий указать, что здесь используется имя cout, которое определено в пространстве имен std. Как будет продемонстрировано в разделе 3.1, существует способ, позволяющий программисту избежать частого использования подробного синтаксиса.Чтение из потока Отобразив приглашение к вводу, необходимо организовать чтение введенных пользователем данных. Сначала следует определить две переменные (variable), в данном случае v1 и v2, которые и будут содержать введенные Page 13/1103 данные: int v1 = 0, v2 = 0; Эти переменные определены как относящиеся к типу int, который является встроенным типом данных для целочисленных значений. Мы также инициализируем (initialize) их значением 0. При инициализации переменной ей присваивается указанное значение в момент создания. Следующий оператор читает введенные пользователем данные: std::cin >> v1 >> v2; Оператор ввода (input operator) (т.е. оператор >>) ведет себя аналогично оператору вывода. Его левым операндом является объект типа istream, а правым операндом — объект, заполняемый данными. Он читает значение из потока, представляемого объектом типа istream, и сохраняет его в объекте, заданном правым операндом. Подобно оператору вывода, оператор ввода возвращает в качестве результата свой левый операнд. Другими словами, эта операция эквивалентна следующей: (std::cin >> v1) >> v2; Поскольку оператор возвращает свой левый операнд, мы можем объединить в одном операторе последовательность из нескольких запросов на ввод данных. Наше выражение ввода читает из объекта std::cin два значения, сохраняя первое в переменной v1, а второе в переменной v2. Другими словами, рассматриваемое выражение ввода выполняется как два следующих: std::cin >> v1; std::cin >> v2; Завершение программы Теперь осталось лишь вывести результат сложения на экран. std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl; Хоть этот оператор и значительно длиннее оператора, отобразившего приглашение к вводу, принципиально он ничем не отличается. Он передает значения каждого из своих операндов в поток стандартного устройства вывода. Здесь интересен тот факт, что не все операнды имеют одинаковый тип значений. Некоторые из них являются строковыми литералами, например "The sum of ", другие значения относятся к типу int, например v1 и v2, а третьи представляют собой результат вычисления арифметического выражения v1 + v2. В библиотеке определены версии операторов ввода и вывода для всех этих встроенных типов данных. Упражнения раздела 1.2 Упражнение 1.3. Напишите программу, которая выводит на стандартное устройство вывода фразу "Hello, World". Упражнение 1.4. Наша программа использовала оператор суммы (+) для сложения двух чисел. Напишите программу, которая использует оператор умножения (*) для вычисления произведения двух чисел. Упражнение 1.5. В нашей программе весь вывод осуществлял один большой оператор. Перепишите программу так, чтобы для вывода на экран каждого операнда использовался Page 14/1103 отдельный оператор. Упражнение 1.6. Объясните, является ли следующий фрагмент кода допустимым: std::cout << "The sum of " << v1; << " and " << v2; << " is " << v1 + v2 << std::endl; Если программа корректна, то что она делает? Если нет, то почему и как ее исправить? 1.3. Несколько слов о комментариях Прежде чем перейти к более сложным программам, рассмотрим комментарии языка С++. Комментарий (comment) помогает человеку, читающему исходный текст программы, понять ее смысл. Как правило, они используются для кратких заметок об используемом алгоритме, о назначении переменных или для дополнительных разъяснений сложного фрагмента кода. Поскольку компилятор игнорирует комментарии, они никак не влияют ни на размер исполняемой программы, ни на ее поведение или производительность. Хотя компилятор игнорирует комментарии, читатели нашего кода — напротив. Программисты, как правило, доверяют комментариям, даже когда другие части системной документации считают устаревшими. Некорректный комментарий — это еще хуже, чем отсутствие комментария вообще, поскольку он может ввести читателя в заблуждение. Когда вы модифицируете свой код, убедитесь, что обновили и комментарии! Виды комментариев в С++ В языке С++ существуют два вида комментариев: однострочные и парные. Однострочный комментарий начинается символом двойной наклонной черты (//) и завершается в конце строки. Все, что находится справа от этого символа в текущей строке, игнорируется компилятором. Второй тип комментария, заключенного в пару символов (/* и */), унаследован от языка С. Такие комментарии начинаются символом /* и завершаются символом */. Эти комментарии способны содержать все что угодно, включая новые строки, за исключением символа */. Все, что находится между символами /* и */, компилятор считает комментарием. В парном комментарии могут располагаться любые символы, включая символ табуляции, пробел и символ новой строки. Парный комментарий может занимать несколько строк кода, но это не обязательно. Когда парный комментарий занимает несколько строк кода, имеет смысл указать визуально, что эти строки принадлежат многострочному комментарию. Применяемый в этой книге стиль предполагает использование для обозначения внутренних строк многострочного комментария символы звездочки. Таким образом, символ звездочки в начале строки свидетельствует о ее принадлежности к многострочному комментарию (но это необязательно). В программах обычно используются обе формы комментариев. Парные комментарии, как правило, используют для многострочных объяснений[2], а двойную наклонную черту — для замечаний в той же строке, что и код. #include <iostream> Page 15/1103 /* * Пример функции main(): * Читает два числа и отображает их сумму */ int main() { // Предлагает пользователю ввести два числа std::cout << "Enter two numbers:" << std::endl; int v1 = 0, v2 = 0; // переменные для хранения ввода std::cin >> v1 >> v2; // чтение ввода std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl; return 0; } В этой книге комментарии выделены курсивом, чтобы отличить их от обычного кода программы. Обычно выделение текста комментариев определяется возможностями используемой среды разработки. Парный комментарий не допускает вложения Комментарий, который начинается символом /*, всегда завершается следующим символом */. Поэтому один парный комментарий не может находиться в другом. Сообщение о подобной ошибке, выданное компилятором, как правило, вызывает удивление. Попробуйте, например, откомпилировать следующую программу: /* * парный комментарий /* */ не допускает вложения * под "не допускает вложения" следует понимать, что остальная часть * Page 16/1103 текста будет рассматриваться как программный код */ int main() { return 0; } Упражнения раздела 1.3 Упражнение 1.7. Попробуйте откомпилировать программу, содержащую недопустимо вложенный комментарий. Упражнение 1.8. Укажите, какой из следующих операторов вывода (если он есть) является допустимым: std::cout << "/*"; std::cout << "*/"; std::cout << /* "*/" */; std::cout << /* "*/" /* "/*" */; Откомпилируйте программу с этими тремя операторами и проверьте правильность своего ответа. Исправьте ошибки, сообщения о которых были получены. 1.4. Средства управления Операторы обычно выполняются последовательно: сначала выполняется первый оператор в блоке, затем второй и т.д. Конечно, при последовательном выполнении операторов много задач не решить (включая проблему книжного магазина). Для управления последовательностью выполнения все языки программирования предоставляют операторы, обеспечивающие более сложные пути выполнения. 1.4.1. Оператор while Оператор while организует итерационное (циклическое) выполнение фрагмента кода, пока его условие остается истинным. Используя оператор while, можно написать следующую программу, суммирующую числа от 1 до 10 включительно: #include <iostream> int main() { int sum = 0, val = 1; // Page 17/1103 продолжать выполнение цикла, пока значение val // не превысит 10 while (val <= 10) { sum += val; // присвоить sum сумму val и sum ++val; // добавить 1 к val } std::cout << "Sum of 1 to 10 inclusive is " << sum << std::endl; return 0; } Будучи откомпилированной и запущенной на выполнение, эта программа отобразит на экране следующий результат: Sum of 1 to 10 inclusive is 55 Как и прежде, программа начинается с включения заголовка iostream и определения функции main(). В функции main() определены две переменные типа int — sum, которая будет содержать полученную сумму, и val, которая будет содержать каждое из значений от 1 до 10. Переменной sum присваивается исходное значение 0, а переменной val — исходное значение 1. Новой частью программы является оператор while, имеющий следующий синтаксис. while ( условие ) оператор Оператор while циклически выполняет оператор , пока условие остается истинным. Условие — это выражение, результатом выполнения которого является истина или ложь. Пока условие истинно, оператор выполняется. После выполнения оператора условие проверяется снова. Если условие остается истинным, оператор выполняется снова. Цикл while продолжается, поочередно проверяя Page 18/1103 условие и выполняя оператор , пока условие не станет ложно. В этой программе использован следующий оператор while: // продолжать выполнение цикла, пока значение val // не превысит 10 while (val <= 10) { sum += val; // присвоить sum сумму val и sum ++val; // добавить 1 к val } Для сравнения текущего значения переменной val и числа 10 условие цикла использует оператор меньше или равно ( оператор <=). Пока значение переменной val меньше или равно 10, условие истинно и тело цикла while выполняется. В данном случае телом цикла while является блок, содержащий два оператора. { sum += val; // присвоить sum сумму val и sum ++val; // добавить 1 к val } Блок (block) — это последовательность из любого количества операторов, заключенных в фигурные скобки. Блок является оператором и может использоваться везде, где допустим один оператор. Первым в блоке является составной оператор присвоения (compound assignment operator), или оператор присвоения с суммой ( оператор +=). Этот оператор добавляет свой правый операнд к левому операнду. Это эквивалентно двум операторам: суммы и присвоения. Page 19/1103 sum = sum + val; // присвоить sum сумму val и sum Таким образом, первый оператор в блоке добавляет значение переменной val к текущему значению переменной sum и сохраняет результат в той же переменной sum. Следующее выражение использует префиксный оператор инкремента (prefix increment operator) ( оператор ++), который осуществляет приращение: ++val; // добавить 1 к val Оператор инкремента добавляет единицу к своему операнду. Запись ++val эквивалентна выражению val = val + 1. После выполнения тела цикл while снова проверяет условие. Если после нового увеличения значение переменной val все еще меньше или равно 10, тело цикла while выполняется снова. Проверка условия и выполнение тела цикла продолжится до тех пор, пока значение переменной val остается меньше или равно 10. Как только значение переменной val станет больше 10, происходит выход из цикла while и управление переходит к оператору, следующему за ним. В данном случае это оператор, отображающий результат на экране, за которым следует оператор return, завершающий функцию main() и саму программу. Упражнения раздела 1.4.1 Упражнение 1.9. Напишите программу, которая использует цикл while для суммирования чисел от 50 до 100. Упражнение 1.10. Кроме оператора ++, который добавляет 1 к своему операнду, существует оператор декремента (--), который вычитает 1. Используйте оператор декремента, чтобы написать цикл while, выводящий на экран числа от десяти до нуля. Упражнение 1.11. Напишите программу, которая запрашивает у пользователя два целых числа, а затем отображает каждое число в диапазоне, определенном этими двумя числами. 1.4.2. Оператор for В рассмотренном ранее цикле while для управления количеством итераций использовалась переменная val. Мы проверяли ее значение в условии, а затем в теле цикла while увеличивали его. Такая схема, подразумевающая использование переменной в условии и ее инкремент в теле столь популярна, что было разработано второе средство управления — оператор for, существенно сокращающий подобный код. Используя оператор for, можно было бы переписать код программы, суммирующей числа от 1 до 10, следующим образом: #include <iostream> Page 20/1103 int main() { int sum = 0; // сложить числа от 1 до 10 включительно for (int val = 1; val <= 10; ++val) sum += val; // эквивалентно sum = sum + val std::cout << "Sum of 1 to 10 inclusive is " << sum << std::endl; return 0; } Как и прежде, определяем и инициализируем переменную sum нулевым значением. В этой версии мы определяем переменную val как часть самого оператора for. for (int val = 1; val <= 10; ++val) sum += val; У каждого оператора for есть две части: заголовок и тело. Заголовок контролирует количество раз выполнения тела. Сам заголовок состоит из трех частей: оператора инициализации , условия и выражения . В данном случае оператор инициализации определяет, что объекту val типа int присвоено исходное значение 1: int val = 1; Переменная val существует только в цикле for; ее невозможно использовать после завершения цикла. Оператор инициализации выполняется только однажды перед запуском цикла for. Условие сравнивает текущее значение переменной val со значением 10: val <= 10 Условие проверяется при каждом цикле. Пока значение переменной val меньше или равно 10, выполняется тело цикла for. Выражение выполняется после тела цикла for. В данном случае выражение использует префиксный оператор инкремента, который добавляет 1 к значению переменной val: ++val После выполнения выражения оператор for повторно проверяет условие. Если новое значение переменной val все еще меньше или равно 10, то тело цикла for выполняется снова. Page 21/1103 После выполнения тела значение переменной val увеличивается снова. Цикл продолжается до нарушения условия. В рассматриваемом цикле for тело осуществляет суммирование. sum += val; // эквивалентно sum = sum + val В итоге оператор for выполняется так. 1. Создается переменная val и инициализируется значением 1. 2. Проверяется значение переменной val (меньше или равно 10). Если условие истинно, выполняется тело цикла for, в противном случае цикл завершается и управление переходит к оператору, следующему за ним. 3. Приращение значения переменной val. 4. Пока условие истинно, повторяются действия, начиная с пункта 2. Упражнения раздела 1.4.2 Упражнение 1.12. Что делает следующий цикл for? Каково финальное значение переменной sum? int sum = 0; for (int i = -100; i <= 100; ++i) sum += i; Упражнение 1.13. Перепишите упражнения раздела 1.4.1, используя циклы for. Упражнение 1.14. Сравните циклы с использованием операторов for и while в двух предыдущих упражнениях. Каковы преимущества и недостатки каждого из них в разных случаях? Упражнение 1.15. Напишите программы, которые содержат наиболее распространенные ошибки, обсуждаемые во врезке «Ввод конца файла с клавиатуры». Ознакомьтесь с сообщениями, выдаваемыми компилятором. 1.4.3. Ввод неизвестного количества данных В приведенных выше разделах мы писали программы, которые суммировали числа от 1 до 10. Логическое усовершенствование этой программы подразумевало бы запрос суммируемых чисел у пользователя. В таком случае мы не будем знать, сколько чисел суммировать. Поэтому продолжим читать числа, пока будет что читать. #include <iostream> int main() { int sum = 0, value = 0; Page 22/1103 // читать данные до конца файла, вычислить сумму всех значений while (std::cin >> value) sum += value; // эквивалентно sum = sum + val std::cout << "Sum is: " << sum << std::endl; return 0; } Если ввести значения 3 4 5 6, то будет получен результат Sum is: 18. Первая строка функции main() определяет две переменные типа int по имени sum и value, инициализируемые значением 0. Переменная value применяется для хранения чисел, вводимых в условии цикла while. while (std::cin >> value) Условием продолжения цикла while является выражение std::cin >> value Это выражение читает следующее число со стандартного устройства ввода и сохраняет его в переменной value. Как упоминалось в разделе 1.2, оператор ввода возвращает свой левый операнд. Таким образом, в условии фактически проверяется объект std::cin. Когда объект типа istream используется при проверке условия, результат зависит от состояния потока. Если поток допустим, т.е. не столкнулся с ошибкой и ввод следующего значения еще возможен, это условие считается истинным. Объект типа istream переходит в недопустимое состояние по достижении конца файла (end-of-file) или при вводе недопустимых данных, например строки вместо числа. Недопустимое состояние объекта типа istream в условии свидетельствует о том, что оно ложно. Таким образом, пока не достигнут конец файла (или не произошла ошибка ввода), условие остается истинным и выполняется тело цикла while. Тело состоит из одного составного оператора присвоения, который добавляет значение переменной value к текущему значению переменной sum. Однажды нарушение условия завершает цикл while. По выходе из цикла выполняется следующий оператор, который выводит значение переменной sum, сопровождаемое манипулятором endl. Ввод конца файла с клавиатуры Разные операционные системы используют для конца файла различные значения. Для ввода символа конца файла в операционной системе Windows достаточно нажать комбинацию клавиш <Ctrl+z> (удерживая нажатой клавишу <Ctrl>, нажать клавишу <z>), а затем клавишу <Enter> или <Return>. На машине с операционной системой UNIX, включая Mac OS-X, как правило, используется комбинация клавиш <Ctrl+d>. Возвращаясь к компиляции Одной из задач компилятора является поиск ошибок в тексте программ. Компилятор, безусловно, не может выяснить, делает ли программа то, что предполагал ее автор, но Page 23/1103 вполне способен обнаружить ошибки в форме записи. Ниже приведены примеры ошибок, которые компилятор обнаруживает чаще всего. Синтаксические ошибки . Речь идет о грамматических ошибках языка С++. Приведенный ниже код демонстрирует наиболее распространенные синтаксические ошибки, снабженные комментариями, которые описывают их суть. // ошибка: отсутствует ')' список параметров функции main() int main ( { // ошибка: после endl используется двоеточие, а не точка с запятой std::cout << "Read each file." << std::endl: // ошибка: отсутствуют кавычки вокруг строкового литерала std::cout << Update master. << std::endl; // ошибка: отсутствует второй оператор вывода std::cout << "Write new master." std::endl; // ошибка: отсутствует ';' после оператора return return 0 } Ошибки несовпадения типа . Каждый элемент данных языка С++ имеет тип. Значение 10, например, является числом типа int. Слово "привет" с парными кавычками — это строковый литерал. Примером ошибки несовпадения является передача строкового литерала функции, которая ожидает целочисленным аргумент. Ошибки объявления . Каждое имя, используемое в программе на языке С++, должно быть вначале объявлено. Использование необъявленного имени обычно приводит к сообщению об ошибке. Типичными ошибками объявления является также отсутствие указания пространства имен, например std::, при доступе к имени, определенному в библиотеке, а также орфографические ошибки в именах идентификаторов. #include <iostream> int main() { int v1 = 0, v2 = 0; std::cin >> v >> v2; // ошибка: используется "v" вместо "v1" Page 24/1103 // cout не определен, должно быть std::cout cout << v1 + v2 << std::endl; return 0; } Сообщение об ошибке содержит обычно номер строки и краткое описание того, что компилятор считает неправильным. Исправлять ошибки имеет смысл в том порядке, в котором поступают сообщения о них. Зачастую одна ошибка приводит к появлению других, поэтому компилятор, как правило, сообщает о большем количестве ошибок, чем имеется фактически. Целесообразно также перекомпилировать код после устранения каждой ошибки или небольшого количества вполне очевидных ошибок. Этот цикл известен под названием " редактирование, компиляция, отладка " (edit-compile-debug).Упражнения раздела 1.4.3 Упражнение 1.16. Напишите собственную версию программы, которая выводит сумму набора целых чисел, прочитанных при помощи объекта cin. 1.4.4. Оператор if Подобно большинству языков, С++ предоставляет оператор if, который обеспечивает выполнение операторов по условию. Оператор if можно использовать для написания программы подсчета количества последовательных совпадений значений во вводе: #include <iostream> int main() { // currVal - подсчитываемое число; новые значения будем читать в val int currVal = 0, val = 0; // прочитать первое число и удостовериться в наличии данных // для обработки if (std::cin >> currVal) { int cnt = 1; // сохранить счет для текущего значения while (std::cin >> val) { // читать остальные числа Page 25/1103 if (val == currVal) // если значение то же ++cnt; // добавить 1 к cnt else { // в противном случае вывести счет для // предыдущего значения std::cout << currVal << " occurs " << ent << " times" << std::endl; currVal = val; // запомнить новое значение cnt = 1; // сбросить счетчик } } // цикл while заканчивается здесь // не забыть вывести счет для последнего значения std::cout << currVal << " occurs " << cnt << " times" << std::endl; } // первый оператор if заканчивается здесь return 0; } Если задать этой программе следующий ввод: 42 42 42 42 42 55 55 62 100 100 100 то результат будет таким: 42 occurs 5 times 55 occurs 2 times Page 26/1103 62 occurs 1 times 100 occurs 3 times Большая часть кода в этой программе должна быть уже знакома по прежним программам. Сначала определяются переменные val и currVal: currVal будет содержать подсчитываемое число, а переменная val — каждое число, читаемое из ввода. Новыми являются два оператора if. Первый гарантирует, что ввод не пуст. if (std::cin >> currVal) { // ... } // первый оператор if заканчивается здесь Подобно оператору while, оператор if проверяет условие. Условие в первом операторе if читает значение в переменную currVal. Если чтение успешно, то условие истинно и выполняется блок кода, начинающийся с открытой фигурной скобки после условия. Этот блок завершается закрывающей фигурной скобкой непосредственно перед оператором return. Как только подсчитываемое стало известно, определяется переменная cnt, содержащая счет совпадений данного числа. Для многократного чтения чисел со стандартного устройства ввода используется цикл while, подобный приведенному в предыдущем разделе. Телом цикла while является блок, содержащий второй оператор if: if (val == currVal) // если значение то же ++cnt; // добавить 1 к cnt else { // в противном случае вывести счет для // предыдущего значения std::cout << currVal << " occurs " << cnt << " times" << std::endl; currVal = val; // запомнить новое значение cnt = 1; // сбросить счетчик } Условие в этом операторе if использует для проверки равенства значений переменных val и Page 27/1103 currVal оператор равенства (equality operator) (оператор ==). Если условие истинно, выполняется оператор, следующий непосредственно за условием. Этот оператор осуществляет инкремент значения переменной cnt, означая очередное повторение значения переменной currVal. Если условие ложно (т.е. значения переменных val и currVal не равны), выполняется оператор после ключевого слова else. Этот оператор также является блоком, состоящим из оператора вывода и двух присвоений. Оператор вывода отображает счет для значения, которое мы только что закончили обрабатывать. Операторы присвоения возвращают переменной cnt значение 1, а переменной currVal — значение переменной val, которое ныне является новым подсчитываемым числом. В языке С++ для присвоения используется оператор =, а для про верки равенства — оператор ==. В условии могут присутствовать оба оператора. Довольно распространена ошибка, когда в условии пишут =, а подразумевают ==. Упражнения раздела 1.4.4 Упражнение 1.17. Что произойдет, если в рассматриваемой здесь программе все введенные значения будут равны? Что если никаких совпадающих значений нет? Упражнение 1.18. Откомпилируйте и запустите на выполнение программу этого раздела, а затем вводите только равные значения. Запустите ее снова и вводите только не повторяющиеся числа. Совпадает ли ваше предположение с реальностью? Упражнение 1.19. Пересмотрите свою программу, написанную для упражнения раздела 1.4.1, которая выводила бы диапазон чисел, обрабатывая ввод, так, чтобы первым отображалось меньше число из двух введенных. Ключевая концепция. Выравнивание и форматирование кода программ C++ Оформление исходного кода программ на языке С++ не имеет жестких правил, поэтому расположение фигурных скобок, отступ, выравнивание, комментарии и разрыв строк, как правило, никак не влияет на полученную в результате компиляции программу. Например, фигурная скобка, обозначающая начало тела функции main(), может находиться в одной строке со словом main (как в этой книге), в начале следующей строки или где-нибудь дальше. Единственное требование — чтобы открывающая фигурная скобка была первым печатным символом, за исключением комментария, после списка параметров функции main(). Хотя исходный код вполне можно оформлять по своему усмотрению, необходимо все же позаботиться о его удобочитаемости. Можно, например, написать всю функцию main() в одной длинной строке. Такая форма записи вполне допустима, но читать подобный код будет крайне неудобно. До сих пор не стихают бесконечные дебаты по поводу наилучшего способа оформления кода программ на языках С++ и С. Авторы убеждены, что единственно правильного стиля не существует, но единообразие все же важно. Большинство программистов выравнивают элементы своих программ так же, как мы в функции main() и телах наших циклов. Однако в коде этой книги принято размещать фигурные скобки, которые разграничивают функции, в собственных строках, а выравнивание составных операторов ввода и вывода осуществлять так, чтобы совпадал отступ операндов. Другие соглашения будут описаны по мере усложнения программ. Не забывайте, что существуют и другие способы оформления кода. При выборе стиля оформления учитывайте удобочитаемость кода, а выбрав стиль, придерживайтесь его неукоснительно на протяжении всей программы. Page 28/1103 1.5. Введение в классы Единственное средство, которое осталось изучить перед переходом к решению проблемы книжного магазина, — это определение структуры данных для хранения данных транзакций. Для определения собственных структур данных язык С++ предоставляет классы (class). Класс определяет тип данных и набор операций, связанных с этим типом. Механизм классов — это одно из важнейших средств языка С++. Фактически основное внимание при проектировании приложения на языке С++ уделяют именно определению различных типов классов (class type), которые ведут себя так же, как встроенные типы данных. В этом разделе описан простой класс, который можно использовать при решении проблемы книжного магазина. Реализован этот класс будет в следующих главах, когда читатель больше узнает о типах, выражениях, операторах и функциях. Чтобы использовать класс, необходимо знать следующее. |