Параметры со значениями по умолчанию
Чтобы упростить вызов функции, в ее заголовке можно указать значения параметров по умолчанию. Эти параметры должны быть последними в списке и могут опускаться при вызове функции. В качестве значений параметров по умолчанию могут использоваться константы, глобальные переменные и выражения:
int f(int a, int b = 0);void f1(int, int = 100, char* = 0); void err(int errValue = errno); // errno - глобальная переменная f(100); f(a, 1); // варианты вызова функции f// варианты вызова функции f1f1(a); f1(a, 10); f1(a, 10, "Vasia"); f1(a,,"Vasia") // неверно!Функции стандартной библиотеки
Любая программа на С++ содержит обращения к стандартной библиотеке, в которой находятся определения типов, констант, макросов, функций и классов.
Чтобы использовать их в программе, требуется с помощью директивы #include включить в исходный текст программы заголовочные файлы, в которых находятся соответствующие объявления.
Сами библиотечные функции хранятся в скомпилированном виде и подключаются к программе на этапе компоновки. Функции библиотеки можно разбить на группы по их назначению: ввод/вывод, обработка строк, математические функции, работа с динамической памятью, поиск и сортировка и т.д.
Ниже приведен краткий обзор основных функций.
Функции ввода/вывода
Ввод/вывод в С++ реализуется либо с помощью функций, унаследованных от библиотек С, либо с помощью потоков С++. Смешивать эти два способа в одной программе не рекомендуется.
Для использования функций ввода/вывода в стиле С необходимо подключить к программе заголовочный файл <stdio.h> или <cstdio>. При вводе/выводе данные рассматриваются как поток байтов. Физически поток представляет собой файл или устройство, например, клавиатуру или дисплей, рассматривающиеся как частный случай файла.
Открытие потока
Работа с потоком начинается с его открытия. Поток можно открыть для чтения и/или записи в двоичном или текстовом режиме. Функция открытия потока имеет формат:
FILE* fopen(const char* filename, const char* mode);
При успешном открытии потока функция возвращает указатель на предопределенную структуру типа FILE, содержащую всю необходимую для работы с потоком информацию, или NULL в противном случае. Первый параметр - имя открываемого файла в виде С-строки, второй - режим открытия файла:
"r" - файл открывается для чтения;
"w" - открывается пустой файл для записи (если файл существует, он стирается);
"a+" - файл открывается для добавления информации в его конец;
"r+" - файл открывается для чтения и записи (файл должен существовать);
"w+" - открывается пустой файл для чтения и записи (если файл существует, он стирается);
"a" - файл открывается для чтения и добавления информации в его конец.
Режим открытия может также содержать символы t (текстовый режим) или b (двоичный режим), отличающиеся обработкой символов перехода на новую строку. По умолчанию файл открывается в текстовом режиме, при котором комбинация символов "возврат каретки" и "перевод строки" (0x13 0x10) при вводе преобразуются в одиночный символ перевода строки (при выводе выполняется обратное преобразование). В двоичном режиме преобразования не выполняются.
Пример:
FILE *f = fopen("d:\\cpp\\data.txt", "rb+");
Указатель f используется в дальнейших операциях с потоком. Его передают функциям ввода/вывода в качестве параметра.
При открытии потока с ним связывается область памяти, называемая буфером. При выводе вся информация направляется в буфер и накапливается там до заполнения буфера или до закрытия потока. Чтение осуществляется блоками, равными размеру буфера, и данные читаются из буфера.
Существует пять предопределенных потоков, которые открываются в начале работы программы: стандартный ввод stdin, стандартный вывод stdout, стандартный вывод сообщений об ошибках stderr, стандартный дополнительный поток stdaux и стандартная печать stdprn.
Ввод/вывод в поток
Ввод/вывод в поток можно осуществлять различными способами: в виде последовательности байтов, в виде символов и строк или с использованием форматных преобразований. Для каждого вида операций определен свой набор функций.
Операции ввода/вывода выполняются начиная с текущей позиции потока, определяемой положением указателя потока. Указатель устанавливается при открытии на начало или конец файла (в соответствии с режимом открытия) и изменяется автоматически после каждой операции ввода/вывода.
Ниже перечислены основные функции ввода/вывода потока.
· Чтение и запись потока байтов выполняют функции fread и fwrite.
· Чтение символа из потока - getc, fgetc, из стандартного потока stdin - getchar.
· Запись символа в поток - putc, fputc, в стандартный поток stdout - putchar.
· Чтение строки из потока - fgets, из стандартного потока stdin - gets.
· Запись строки в поток - fputs, в стандартный поток stdout - puts.
· Форматированный ввод из потока - fscanf, из стандартного потока stdin - scanf, из строки - sscanf.
· Форматированный вывод в поток - fprintf, в стандартный поток stdout - printf, в строку - sprintf.
Закрытие потока
Поток закрывается либо при завершении программы, либо явным образом с помощью функции fclose:
int fclose(FILE*);
Перед закрытием потока информация из связанных с ним буферов выгружается на диск. Рекомендуется явным образом закрывать потоки, открытые для записи.
Пример работы с потоками
Приведенная ниже программа построчно считывает данные из текстового файла в буферную переменную s, формирует из них структуру mon и записывает ее в двоичном режиме в выходной файл. Далее иллюстрируется считывание из этого файла произвольной записи.
#include <cstdio>
#include <cstdlib>
#include <cconio>
#include <iostream>
using namespace std;
int main()
{
FILE *fi, *fo;
if((fi = fopen("d:\\c\\binfile.txt", "r"))==0)
{
cout << "Ошибка открытия входного файла";
return 1;
};
if((fo = fopen("d:\\c\\binfile.out", "w+b"))==0)
{
cout << "Ошибка открытия выходного файла";
return 1;
};
const int dl = 80;
char s[dl];
struct
{
char type[20];
int opt, rozn;
char comm[40];
}mon;
while (fgets(s, dl, fi))
{
// Преобразование строки в структуру:
strncpy(mon.type, s, 19); //Описание strncpy см. в след. разделе
mon.opt = atoi(&s[20]); //Описание atoi см. в след. разделе
mon.rozn = atoi(&s[25]);
strncpy(mon.comm, &s[30], 40);
fwrite(&mon, sizeof mon, 1, fo);
}
fclose(fi);
int i; cin >> i; // Номер записи
// Установка указателя текущей позиции файла на запись i
fseek(fo, (sizeof mon)*i, SEEK_SET);
fread(&mon, sizeof mon, 1, fo);
cout << "mon.type "
<< mon.type << " opt "
<< mon.opt << " rozn "
<< mon.rozn << endl;
fclose(fo);
}
Допустим, что в файле хранятся сведения о мониторах. В каждой строке указан тип, оптовая и розничная цены и примечание. Для простоты данные в каждой строке записаны единообразно: первые 20 символов занимает тип монитора, далее по 5 символов оптовая и розничная цена, затем примечание длиной не более 40 символов.
Функции работы со строками и символами
Строка представляет собой массив символов, заканчивающийся нуль-символом. В С++ есть две возможности работы со строками: функции, унаследованные из библиотеки С (заголовочный файл <string.h> или <cstring>), и библиотечный класс С++ string, предоставляющий более широкие возможности представления, обработки и контроля строк.
Библиотека С содержит функции копирования строк ( strcpy, strncpy ), сравнения ( strcmp, strncmp ), объединения строк ( strcat, strncat ), поиска подстроки ( strstr ), поиска вхождения символа ( strchr, strrchr, strpbrk ), определения длины строки ( strlen ) и др.
В заголовочных файлах <stdlib.h> и <cstdlib> содержатся полезные функции преобразования строк в числа:
double atof(const char* p); //преобразует переданную строку в double
int atoi(const char* p); //преобразует переданную строку в int
long atol(const char* p); //преобразует переданную строку в long
Пробелы и табуляции в начале строки пропускаются. Преобразование прекращается при встрече недопустимого символа или конца строки. Если строку нельзя преобразовать в число, возвращается 0.
Для работы с символами в стандартной библиотеке (заголовочные файлы <ctype.h> и <cctype>) есть следующие функции:
Таблица 4.1. | |
Имя | Проверка на принадлежность символа множеству |
isalnum | букв и цифр (A-Z, a-z, 0-9) |
isalfa | букв (A-Z, a-z) |
iscntrl | управляющих символов (с кодами 0..31 и 127) |
isdigit | цифр (0-9) |
isgraph | Печатаемых символов, кроме пробела ( isalfa | isdigit | ispunct ) |
islower | букв нижнего регистра (a-z) |
isprint | Печатаемых символов |
ispunct | знаков пунктуации |
isspace | символов-разделителей |
isupper | букв верхнего регистра (A-Z) |
isxdigit | шестнадцатеричных цифр (A-F, a-f, 0-9) |
Математические функции
С++ унаследовал из С стандартные математические функции, описание которых находится в заголовочных файлах <stdlib.h>, <math.h> и <cmath>. Они позволяют получить абсолютное значение ( abs, fabs ), округленное число ( ceil, floor ), квадратный корень ( sqrt ), степень ( pow ), значения тригонометрических функций ( sin, cos, tan, sinh, и т. п.), экспоненту ( exp ), логарифм ( log, log10 ), дробную и целую часть числа ( modf ), остаток от деления ( fmod ) и некоторые другие.
Модульное программирование
С увеличением объема программы становится неудобно хранить ее в одном файле. Разбиение программы на функции является первым шагом в повышении уровня абстракции программы, следующий - группировка функций и связанных с ними данных в отдельные файлы (модули), компилируемые раздельно.
Получившиеся в результате компиляции объектные модули объединяются в исполняемую программу с помощью компоновщика. Разбиение на модули уменьшает время перекомпиляции и облегчает процесс отладки. Чем более независимы модули, тем легче отлаживать программу.
Модуль содержит данные и функции их обработки. Для того чтобы использовать модуль, нужно знать только его интерфейс. Интерфейсом модуля являются заголовки всех функций и описания доступных извне типов, переменных и констант. Описания глобальных программных объектов во всех модулях программы должны быть согласованы.
Модульность в языке С++ поддерживается с помощью директив препроцессора, пространств имен, классов памяти, исключений и раздельной компиляции.
Директивы препроцессора
Препроцессором называется первая фаза компилятора. Инструкции препроцессора называются директивами. Они должны начинаться с символа #, перед которым в строке могут находиться только пробельные символы.
Директива #include
Директива #include <имя_файла> вставляет содержимое указанного файла в ту точку исходного файла, где она записана. Включаемый файл также может содержать директивы #include.
Поиск файла, если не указан полный путь, ведется в стандартных каталогах включаемых файлов. Вместо угловых скобок могут использоваться кавычки (" ") - в этом случае поиск файла ведется в каталоге, содержащем исходный файл, а затем уже в стандартных каталогах.
Директива #include является простейшим средством обеспечения согласованности объявлений в различных файлах, включая в них информацию об интерфейсе из заголовочных файлов. Заголовочные файлы обычно имеют расширение .h и могут содержать:
· определения типов, констант, встроенных функций, шаблонов, перечислений;
· объявления функций, данных, имен, шаблонов;
· пространства имен;
· директивы препроцессора;
· комментарии.
В заголовочном файле не должно быть определений функций и данных.
Директива #define
Директива#define определяет подстановку в тексте программы. Она используется для определения:
· символических констант. Формат определения символической константы:
· /* Все вхождения имени заменяются на текст подстановки */#define имя текст_подстановки· макросов, которые выглядят как функции, но реализуются подстановкой их текста в текст программы. Формат определения макроса:
#define имя( параметры ) текст_подстановки· символов, управляющих условной компиляцией. Они используются вместе с директивами #ifdef и #ifndef. Формат:
#define имяПримеры:
#define M 1000#define Vasia "Василий Иванович"#define MAX(a,b) ((x)>(y)?(x):(y))#define __cplusplusПараметры используются при макроподстановке, например, если в тексте программы используется вызов макроса y = MAX(sum1, sum2);, он будет заменен на
y=((sum1)>(sum2)?(sum1):(sum2));Использования макросов и символических констант в программах следует избегать.