- •Введение
- •Структура программы на языке Си
- •Директивы препроцессора
- •Константы
- •Переменные
- •Арифметические выражения
- •Операция присваивания
- •Ввод информации с клавиатуры и вывод на экран
- •Логические выражения
- •Операторы управления
- •Оператор условного перехода if
- •Оператор выбора варианта
- •Перечисляемый тип данных
- •Циклы
- •Оператор цикла while
- •Оператор цикла do-while
- •Оператор цикла for
- •Локальные и глобальные переменные
- •Переменные с индексами (массивы)
- •Примеры обработки одномерных массивов
- •Многомерные массивы
- •Массивы и указатели
- •Арифметические и логические операции с указателями
- •Обработка массивов с помощью указателей
- •Обработка массивов переменной размерности
- •Обработка матриц переменной размерности
- •Обработка текстовой информации
- •Стандартные строки языка С++
- •Пример 1. Определение длины строки.
- •Пример 2. Копирование одной строки в другую
- •Пример 3. Исключение из строки символа.
- •Пример 4. Вставка в строку символа
- •Пример 5. Проверка правильности расстановки скобок.
- •Строки типа string
- •Массивы указателей
- •Указатели на функции
- •Сводная таблица форм объявления указателей
- •Преобразование типов
- •Ссылки
- •Структуры
- •Объединения
- •Файлы
- •Чтение файла в матрицу
- •Чтение файла в структуру
- •Функции для обработки произвольных полей структур
- •Связные списки
- •Контейнерные классы
- •Стеки
- •Рекурсия
- •Вопросы для самопроверки
- •Литература
5
иск для «ISO/IEC 14882»). Новый и старый языки С++ различаются существенно, хотя в новом языке обеспечена некоторая совместимость со старым языком. Для подчеркивания аспектов, связанных с языком Visual С++, далее будет просто указываться «консольное приложение». Методические вопросы разработки проектов консольных приложений см. в [1].
Некоторые примеры, приведенные ниже, безусловно, проще реализуются методами обобщенного программирования (использование функциональных объектов, предикатов, адаптеров, шаблонов и т. д.). Цель же данного учебного пособия показать приемы процедурного программирования, и эти примеры являются хорошей практикой обучения навыкам программирования.
Структура программы на языке Си
Программа, написанная на языке Си, состоит из директив препроцессора и отдельных модулей, которые называются подпрограммами или функциями. В программе могут быть также комментарии и некоторые другие элементы. В общем виде структура программы может быть представлена в таком виде:
1.Комментарий, описывающий назначение программы, ее версию и т. д.
2.Директивы препроцессора
3.Объявления глобальных переменных и прототипов функции
4.Главный модуль программы
5.Функции, уникальные для данного приложения
Впрограмму можно вставлять комментарии, которые могут быть двух типов. Комментарии первого типа начинаются с косой черты, за которой следует звездочка (/*). Его конец отмечается этими же символами, размещенными в обратном порядке (*/). Эти комментарии могут занимать несколько строк и размещаться в любом месте программы, в том числе и внутри операторов, например:
x/* это сложение */ = a + b;
6
Символы начала комментария (/*) записывают перед его началом, а символы, закрывающие комментарий, в требуемом месте.
Комментарии второго типа начинаются с двух последовательных косых черт (//) и комментируют только то, что записано после них в одной строке
Любой текст, размещенный в комментарии, не влияет на выполнение программы. Этим можно пользоваться, когда необходимо на время отладки исключить из текста программы какие-нибудь операторы или части программы.
Перед созданием готового к запуску приложения текст программы проходит несколько этапов обработки.
Препроцессорная обработка, во время которой из текста программы удаляются все пробелы и комментарии и выполняются директивы препроцессора.
Во время компиляции текст программы преобразуется в машинные инструкции компьютера и отмечается вызов всех функций, но собственно функции не подключаются.
При компоновке к тексту программы изо всех доступных библиотек извлекаются и подключаются все требуемые функции, и формируется готовое к выполнению приложение.
Директивы препроцессора
Если написана программа некоторой функции, она может быть помещена в библиотеку, и ею смогут пользоваться другие программисты.
Для подключения библиотек стандартных функций и функций пользователя используется директива препроцессора #include, форма записи которого приведена в примере ниже. Обратите внимание, что обратная косая черта при указании папок для пути к файлу записывается одна.
Пути к файлам в директиве препроцессора #include, заключают либо в угловые скобки (например, <conio.h>), либо в кавычки. Угловые
7
скобки указывают на стандартный заголовочный файл, который размещен в строго определенном месте, обычно в папке с именем include. Кавычки обозначают, что заголовочный файл пользовательский и может быть размещен в любом месте. Его поиск начинается с папки, в которой находится исходный текст проекта. Если файл не найден в местах, которые заданы для угловых скобок, то автоматически включается поиск в местах для файлов, указанных в
По мере развития языка С появлялись новые версии одних и тех же функций, поэтому функция с одним и тем же именем может существовать в разных библиотеках. Например, в консольных приложениях может использоваться ряд функций, имеющие одинаковые имена с функциями старого языка С. В таких случаях в консольных приложениях обычно указывают пространство имен, в котором указывают библиотеку. Для этого случая используется директива препроцессора using namespace, чаще всего работают с библиотекой std. Таким образом, эта директива имеет вид:
using namespace std;
Вместо указания пространства имен можно библиотеку указать, задав ее видимость. Для этого перед каждым объектом надо указать имя библиотеки, а после двух двоеточий объект. Например, для вывода на экран часто используется перегруженный знак операции сдвига cout <<. Этот объект существует как в старом языке С++, так и в консольном приложении. Чтобы вызвать его из библиотеки std для вывода строки “Введите число \n” требуется записать:
std :: cout << “Введите число \n”;
Аналогичная ситуация имеет место и с заголовочными файлами. Для обращения к библиотеке консольного приложения после имени библиотеки не надо записывать расширение ”.h”. Например
#include <iostream.h> // Библиотека старого языка С++ #include <iostream> // Библиотека консольного приложения
Заголовочные файлы по ошибке можно вставить несколько раз, поскольку подключаемые файлы также могут ссылаться на свои биб-
8
лиотеки. Для исключения подобной проблемы следует использовать директиву препроцессора:
#pragma once
Эта директива пропускает обработку директивы, если выполняется попытка повторного подключения одного и того же файла.
Для повышения наглядности и надежности программ желательно указывать числовые и тестовые константы в виде осмысленных слов (записанных заглавными буквами латинского алфавита). Например, если в программе записано 60, то понять, что обозначает это число без анализа программы трудно, но если вместо этого числа записать MAX_SPEED, то можно догадаться, что разговор идет о максимальной скорости.
Для замены числовых и текстовых констант буквенными обозначениями применяется директива препроцессора #define. Эта директива находит в тексте программы заданное имя константы и заменяет его текстом или числом, записанным после директивы. Примеры записи директивы (директивы препроцессора всегда записываются с первой позиции строки):
#define COUNT 20
#define PARAM_FILE_NAME “C:\\WORKC\\param.prm”
Особенности оформления директивы #define:
Точка с запятой после этой директивы НЕ ЗАПИСЫВАЕТСЯ.
Если в текстовой константе записана обратная косая черта, как, например, при указании путей к файлам, то в этом случае записываются ДВЕ обратных косых черты, а сама текстовая константа записывается в кавычках.
Директива воспринимает имя константы через пробел после слова #define, и значение константы, следующее за пробелом после имени константы. Любые записи после пробела за значением константы воспринимаются как комментарий (пробелы допускаются только внутри кавычек, обозначающих строку текста). Например, запись директивы
9
#define COUNT 20 25
Обеспечит замену в тексте программы слова COUNT числом
20и проигнорирует число 25.
Директива воспринимает текст только одной строки. Если требуется продолжить текст на следующей строке, то в конце предыдущей строки без пробела надо записать одинарную обратную косую черту.
В тексте программы замене подлежать только слова обозначения константы целиком. Если имя одной константы входит в виде составляющей другой константы, то замена не производится. Например, если оформить директиву с именем N, то эта буква в слове COUNT заменяться не будет.
Имя константы принято записывать ТОЛЬКО заглавными латинскими буквами.
Директива препроцессора #define применяется также для создания макросов, которые представляют собой фрагменты текста программы или даже полные функции, выполняющие заданные действия при разных значениях переменных. В этом случае после имени константы в круглых скобках записывают имена переменных. Например, приведенный ниже макрос позволяет определить максимальное значение из двух чисел. При записи макроса в тексте программы имена переменных могут быть любыми.
#define max(a, b) ((a>b)?(a):(b))
При определении макроса пробел между именем макроса (max) и скобками после него не допускается, поскольку заменяется то, что записано между первым и вторым пробелами, но внутри скобок допускается любое количество пробелов.
Если требуется разместить макрос на нескольких строках, то в конце каждой строки записывают обратную косую черту (‘\’).
Скобки могут быть фигурными. Тогда макрос позволяет вставлять блоки, а также полную функцию. В этом случае после круглых скобок обозначения макроса через пробел следует имя функции, затем
10
в круглых скобках имя параметра, а затем в фигурных скобках текст программы.
В некоторых случаях возникает потребность проверки условия выполнения каких-либо действий в зависимости от режимов работы, условий выполнения программы и т. д. Например, можно включить дополнительную печать в режиме отладки с помощью директив препроцессора:
#ifdef DEBUG
cout<<“Переменная цикла = ”<< i <<“ значение = ”<<f<< endl;
#endif
Собственно программа на языке С++ состоит из одной или большего количества функций. Ниже приведен текст программы для вычисления максимального значения двух чисел, представленной ранее в виде директивы препроцессора.
Каждая функция состоит из заголовка функции и пары фигурных скобок, между которыми размещается текст функции. Заголовок функции состоит из типа возвращаемого значения, имени функции и списка формальных параметров, заключенного в круглые скобки.
Хорошей практикой программирования является запись атрибута const перед формальными параметрами. Это исключает ошибки с
11
обработкой формальных параметров, особенно, если параметр является массивом.
В функции выделяется память для передаваемых в нее параметров, в которую копируются их значения. Поэтому любые изменения этих значений не могут изменить фактические параметры.
Имя функции вместе со списком возвращаемых значений (без типа возвращаемого значения) называется сигнатурой или подписью функции. Более подробно о функциях см. в параграфе «Ссылки».
Между собой функции могут размещаться в произвольном порядке, но в любой законченной программе должна быть одна и только одна главная функция, с которой начинается выполнение программы. Эта функция в зависимости от типа приложения имеет имя main, Main, WinMain и т. д. Для консольных приложений, рассматриваемых далее, эта функция имеет имя _tmain.
Если в программу добавлены функции пользователя, то желательно их реализацию (код функции) размещать после главной функции. Однако в этом случае для каждой добавляемой функции перед главной процедурой надо записать ее прототип, который представляет собой заголовок функции, после которого записана точка с запятой (имена формальных параметров можно не записывать, но последовательность следования типов параметров должна быть указана). Прототипы функций позволяют компилятору обеспечить проверку соответствия типов и количества фактических и формальных параметров при вызове функции. Фактическими называются параметры, задаваемые при вызове функции, а формальными являются параметры, использованные при оформлении функции. В качестве фактических параметров могут быть константы, имена переменных и выражения, а формальные параметры это всегда только имена с указанием типа. При оформлении функции формальным параметрам можно присваивать значения по умолчанию. Для приведенного выше примера вычисления максимального значения прототип функции имеет вид:
double max (const double a, const double b);
12
Если функция состоит из одной главной функции, ее структура (для консольных приложений Win32) представлена на рисунке ниже.
Если вместо типа int основной программы указать тип void, то оператор return 0; не требуется.
Вычисленное значение может быть возвращено через имя функции (в примере ниже оператор return something; выполняет эту операцию), но возможно возвращение вычисленных значений и через список аргументов. Этот случай будет рассмотрен далее в параграфах «Ссылки» и «Указатели на функции».
Если функция не должна ничего возвращать, то тип возвращаемого значения для нее void.
Ниже приведена схема оформления программы консольного приложения, состоящего из основной программы и двух функций разных типов.
//Схема оформления программы с двумя функциями. Здесь
//вставить информацию о программе (назначение, автор и т. д.)
#include "stdafx.h" #include <iostream> #include <conio.h>
//Здесь вставляются дополнительные директивы #include using namespace std;
#define SIZE 20
//Объявление глобальных переменных и прототипов функций double MyFunc (const double x, const int k);
void MyProc (const char *st); int nVal = 5;
int _tmain (int argc, _TCHAR* argv[])
{
//Текст основной программы вставляется после данной строки
_getch(); // Вставьте эту функцию для остановки окна вывода return 0;
}