6 Структурированные типы данных 2012
.pdf
  | 
	
  | 
	21  | 
	
  | 
	
  | 
	
  | 
|
Ymiddle2=Ymiddle2/N;  | 
	// Расчет  | 
	среднеквадратичного  | 
	значения Y  | 
|||
Xmiddle=Xmiddle/N;  | 
	
  | 
	// Расчет  | 
	среднего  | 
	значения X  | 
	
  | 
|
Xmiddle2=Xmiddle2/N;  | 
	// Расчет  | 
	среднеквадратичного  | 
	значения X  | 
|||
cout << "Число точек=" << N << endl;  | 
	
  | 
	
  | 
	
  | 
|||
cout << "Максимум  | 
	Y =" << Ymax <<  | 
	"при  | 
	X="  | 
	<< Xmax <<  | 
	endl;  | 
|
cout << "Минимум  | 
	Y =" << Ymin <<  | 
	"при  | 
	X="  | 
	<< Xmin <<  | 
	endl;  | 
|
cout << "Среднее значение  | 
	Y  | 
	=" << Ymiddle << endl;  | 
||||
cout << "Среднеквадратичное значение Y  | 
	=" << Ymiddle2  | 
	<< endl;  | 
||||
cout << "Сумма значений1 аргумента X  | 
	="  | 
	<< Xsum <<  | 
	endl;  | 
|||
cout << "Сумма значений1 функции Y  | 
	
  | 
	="  | 
	<< Ysum <<  | 
	endl;  | 
||
cout << "Среднее значение  | 
	X  | 
	=" << Xmiddle << endl;  | 
||||
cout << "Среднеквадратичное значение X  | 
	=" << Xmiddle2  | 
	<< endl;  | 
||||
getch();  | 
	// Приостановка  | 
	закрытия  | 
	консоли  | 
	
  | 
||
return 0;  | 
	// Возврат кода  | 
	успешного завершения;  | 
||||
}// -------------------------------------- Конец программы
6.8.7 Перемещения по файлу в С++
С файловым потоком связан указатель позиционирования, который изменяет свое значение в результате операции ввода или вывода. Для выполнения операций произвольного доступа файл должен открываться в двоичном режиме.
Для перемещения по файлу используются встроенные в потоки функции:
1)seekg(номер_байта,ios:: смещение) – перемещение текущей позиции чтения данных (позиция "get");
2)seekp(номер_байта,ios:: смещение) – перемещение текущей позиции записи
данных (позиция "put");
3)tellg() – возвращает значение текущей позиции внутри файла для ввода данных (позиция “get”);
4)tellp() - возвращает значение текущей позиции внутри файла для вывода
данных (позиция “put”);
5) eof() – возвращает true если достигнут конец файла.
В seekp и seekg искомые позиции в относительных смещениях задаются как:
-ios::beg - перемещение от начала файла;
-ios::cur - перемещение вперед от текущей позиции;
-ios::end - перемещение от конца файла.
Пример возможности позиционирования потока ввода информации:
#include < iostream.h > #include < fstream.h >
void main(int argc, char* argv[])
{int size = 0; if (argc > 1)
{const char *FileName = argv[1]; ofstream of;
of.open( FileName, ios::binary );
for(int i = 0; i<100; i++) of.put ((char)(i+27)); of.close();
ifstream file;
file.open(FileName, ios::in | ios::binary); if (file){file.seekg(0, ios::end);
size = file.tellg(); if (size < 0)
{cerr << FileName << " не найден."; return;
}
cout << FileName << " size = " << size<
}
}
else cout << "Вы не задали имя файла.";
} // -------------------- конец программы -------------------------------
22
6.8.9 Буферизированный ввод-вывод с файлами в языке С++
В С++ имеется возможность использовать два способа буферезированного ввода-вывода:
1)с помощью строкового (текстового) буфера потоков ifstream и ofstream;
2)с помощью нетипизированного буфера модулей filebuf и stdiobuf.
Если использовать файловый ввод данных через текстовый буфер модуля fstream.h, то чтение информации из файла осуществляется встроенной функцией read() потока istream, которая имеет следующие прототипы:
Фаловый_поток_типа_istream.read(строковый_буфер,число_символов_буфера); Запись данных через строковый буфер осуществляет функция-член write() для
класса потока ofstrem: Фаловый_поток_типа_ostream.read(строковый_буфер,число_символов_буфера); Эта функция получает n символов из буфера, адрес которого задан параметром
s, и вставляет их в поток вывода. Пример: #include< iostream.h >
#include< fstream.h > void main()
{int x = 255;
char str[80] = "Тестирование двоичного ввода-вывода."; // Открываем файл для вывода в двоичном режиме ofstream ofs("Test.dat");
if (!ofs) { cout << "Файл не открыт!"; return -1; } ofs.write((char*)&x, sizeof(int)); ofs.write((char*)&str, sizeof(str));
ofs.close();
ifstream ifs("Test.dat");// Открывается файл для вывода if (!ifs) { cout << "Файл не открыт.\n"; return -10; } ifs.read((char*)&x, sizeof(int));
ifs.read((char*) str, sizeof(str));
cout << x << '\n' << str << '\n';
}
Класс streambuf используется для потокового буферизованного ввода/вывода. Ниже описываются два элемента типа filebuf, после этого открываются текстовые
файлы,  | 
	Затем каждый элемент filebuf связывается с соответствующим объектом.  | 
|
Далее выполняется печать из входного потока в выходной.  | 
||
#include <fstream.h>  | 
	
  | 
|
#include <fcntl.h>  | 
	
  | 
|
void main()  | 
	
  | 
|
{ char  | 
	ch;  | 
	
  | 
long  | 
	linecount=0;  | 
	
  | 
filebuf inbuf,outbuf;  | 
	//объявление буферов  | 
|
inbuf.open(“infile.dat”,_O_RDONLY | _O_TEXT); //открытие входного файла if(inbuf.is_open()==0)
{ cerr<<”cant open input file”; return -1;
}
istream is(&inbuf); //объявление входного потока outbuf.open(“outfile.dat”,_O_WDONLY | _O_TEXT); //открытие выходного файла if(outbuf.is_open()==0)
{ cerr<<”cant open output file”; return -1;
}
ostream os(&outbuf); //объявление выходного потока while(is)
{is.get(ch); //чтение из потока os.put(ch); //запись в поток if(ch==’\n’) linecount++;
}
inbuf.close(); //закрытие файлов outbuf.close();
cout<<”\nYou had: “<<linecount<<” lines”;
23
}
Допустимо использовать следующие методы класса filebuf: attach(), close(), fd(), is_open(), open(), overflow(), seekoff(), setbuf(), sync(), underflow().
6.8.10 Поиск файлов во внешней памяти
В составе модуля dir имеются две функции, которые позволяют выполнять поиск файлов по заданной строке шаблона имени файлов (маске) используя ресурсы операционной системы MS-DOS:
1)findfirst производит поиск первого файла в текущем каталоге диска (или по указанному в маске пути);
2)findnext – продолжает поиск по заданным в findfirst критериям следующего файла.
Формат finfirst:
int findfirst(char* pathname, struct ffblk *ffblk, int attrib);
где pathname - строка, содержащая маршрут поиска и маску искомого файла (можно использовать символы ? или *)
ffblk - структура с информацией о файле и каталоге, его содержащем; attrib – атрибуты фавйла.
Структура ffblk содержит следующее описание: struct ffblk
{char ff_reserved[21]; /* зарезервировано DOS */
char  | 
	ff_attrib;  | 
	/* атрибуты */  | 
||
int  | 
	ff_ftime;  | 
	/* время  | 
	*/  | 
|
int  | 
	ff_fdate;  | 
	/* дата  | 
	*/  | 
|
long  | 
	ff_fsize;  | 
	/*  | 
	размер */  | 
|
char  | 
	ff_fname[13];  | 
	/*  | 
	имя файла */  | 
|
};
Параметр attrib может быть задан с помощью констант, определенных в файле dos.h:
-FA_RDONLY - АТРИБУТ "только чтение";
-FA_HIDDEN - скрытый файл;
-FA_SYSTEM - системный файл;
- FA_LABEL  | 
	- метка тома;  | 
||
-  | 
	FA_DIREC  | 
	-  | 
	каталог;  | 
-  | 
	FA_ARCH  | 
	-  | 
	архив.  | 
При успешном завершении, то есть при успешном значение поиске файла, соответствующего параметру pathname, функция findfirst возвращает значение 0. Если подходящих файлов больше не существует, или в имени файла допущена ошибка, функция возвращают значение -1 и глобальная переменная errno получает одно из следующих значений:
1)ENOENT - маршрут доступа или имя файла не найдены;
2)ENMFILE - нет больше файлов.
//--------------  | 
	Пример поиска файлов -------------------------------------  | 
|
#include  | 
	<fstream.h>  | 
	
  | 
#include  | 
	<iostream.h>  | 
	
  | 
#include  | 
	<iomanip.h>  | 
	
  | 
#include  | 
	<conio.h>  | 
	
  | 
#include  | 
	<dir.h>  | 
	
  | 
#pragma hdrstop  | 
	
  | 
|
//---------------------------------------------------------------------------  | 
	
  | 
	
  | 
#pragma argsused  | 
	
  | 
|
int main(int argc, char* argv[])  | 
||
{ char Mask[100];  | 
	// Маска поиска  | 
|
int iAttributes = 0;  | 
	// Атрибуты файла  | 
|
int count=0; // счетчик найденных файлов  | 
||
struct  | 
	ffblk ffblk;  | 
	// Структура с данными о файле  | 
int Done;  | 
	// Код завершения поиска  | 
|
cout << " \n The Mask : ";  | 
	// Вывод запроса на  | 
	ввод маски  | 
||
cin >> Mask;  | 
	//  | 
	Ввод маски  | 
	поиска с клавиатуры  | 
|
cout << endl;  | 
	//  | 
	Перевод на  | 
	новую  | 
	строку  | 
Done=findfirst(Mask, &ffblk, iAttributes);  | 
	
  | 
	// Поиск первого файла  | 
||
  | 
	
  | 
	24  | 
	
  | 
	
  | 
while(!Done)  | 
	
  | 
	// Цикл поиска файлов  | 
||
{ count++;  | 
	// Инкрементация счетчика файлов  | 
|||
  | 
	cout.width(25);  | 
	
  | 
	
  | 
	
  | 
  | 
	cout<< ffblk.ff_name<<" - "<<ffblk.ff_fsize<<"b"<<endl;  | 
	// Вывод  | 
||
результата  | 
	
  | 
	
  | 
	
  | 
|
  | 
	Done=findnext(&ffblk);  | 
	
  | 
	// Поиск следующего файла  | 
|
}  | 
	// конец тела цикла  | 
	
  | 
	
  | 
	
  | 
cout << "\n Number files - " << count;  | 
	// Вывод числа найденных файлов  | 
|||
cout << "\nKEY PRESSED !!!" ;  | 
	
  | 
	// Сообщение о завершении работы  | 
||
getch();  | 
	
  | 
	// Приостановка закрытия окна  | 
||
return 0;  | 
	
  | 
	// Возврат кода успешного завершения  | 
||
} //  | 
	---------------------------------------------------------------------------  | 
	
  | 
	
  | 
	
  | 
6.9 Тип перечисления
Этот тип в языках С и С++ описывает перечисления – набор именованных целочисленных констант. Объявляется перечисление с помощью зарезервированного слова enum в следующем формате
enum имя_перчисления {нумеератор1, нумеератор2, … } имя_переменной;
Например enum Tdays {sun, mon, tues, wed, thur, fri, sat} anyday;
устанавливает тип Tdays, переменную anyday этого типа и набор нумераторов (sun, mon, ...), которым соответствуют целочисленные константы.
Данные типа enum всегда int, но при использовании их в выражениях
выполняется этих данных преобразования  | 
	к типу int. Идентификаторы, используемые  | 
в списке нумераторов, неявно получают  | 
	тип unsigned char или int, в зависимости  | 
от значений нумераторов. Если все значения могут быть представлены типом unsigned char, то это и будет типом каждого нумератора.
В C переменной  | 
	типа  | 
	enum  | 
	может  | 
	быть  | 
	присвоено любое  | 
	значение типа  | 
	int. В  | 
|
С++ переменной  | 
	перечислимого типа может присваиваться только значение одного из  | 
|||||||
ее нумераторов. Таким образом,  | 
	
  | 
	
  | 
	
  | 
	
  | 
	
  | 
|||
anyday =  | 
	mon;  | 
	// так  | 
	можно  | 
	
  | 
	
  | 
	
  | 
	
  | 
|
anyday =  | 
	1;  | 
	// так  | 
	нельзя, даже хотя mon == 1  | 
	
  | 
	
  | 
|||
Идентификатор  | 
	days  | 
	является  | 
	тегом  | 
	перечислимого  | 
	типа, который  | 
	можно  | 
||
использовать в последующих объявлениях переменных перечислимого типа enum days: enum days payday, holiday; // объявление двух переменных
В С++ имя перечисления, следующее за словом enum, можно опустить, если в пределах данного контекста имя переменной не дублируется. В этом случае используется так называемый анонимный перечислимый тип, пример:
enum { sun, mon, tues, wed, thur, fri, sat } anyday; // анонимный тип enum
Нумераторы, перечисленные внутри фигурных скобок, называются перечислимыми константами. Каждой из них назначается фиксированное целочисленное значение. При отсутствии явно заданных инициализаторов первый нумератор (sun) устанавливается в ноль, а каждый последующий нумератор имеет значение на единицу больше, чем предыдущий (mon = 1, tue = 2 и т.д.).
Можно установить нумераторы в конкретные значения. Любые последующие имена без инициализаторов будут получать приращение в единицу. Например, в следующем объявлении:
enum coins { penny=1, tuppence, nickel=penny+4, dime=10, quarter=nickel*nickel } smallchange;
tuppence примет значение 2, nickel - значение 5, а quarter - значение 25. Инициализатор может быть любым выражением, дающим целочисленное значение.
Обычно такие значения бывают уникальными, но дублирование их также не запрещено.
enum  | 
	может  | 
	участвовать во всех конструкциях, допускающих использование  | 
|
типов int.  | 
	
  | 
	
  | 
|
enum days {  | 
	sun,  | 
	mon, tues, wed, thur, fri, sat } anyday;  | 
|
enum  | 
	days payday;  | 
||
typedef  | 
	enum  | 
	days DAYS;  | 
|
DAYS  | 
	*daysptr;  | 
||
int  | 
	i  | 
	= tues;  | 
|
anyday  | 
	= mon;  | 
	// так можно  | 
|
25
*daysptr = anyday; // так можно
mon = tues; // неверно: mon - это константа
Пример программы с перечислением для обозначения типа компьютера: #include <iostream.h> // Подключение потоков консоли
#include <conio.h>  | 
	// Подключение функции getch  | 
||||
enum PCtype {  | 
	server, desktop, notebook, netbook, handbook} pc;  | 
||||
int main()  | 
	
  | 
	// Начало главной функция программы  | 
|||
{ int code;  | 
	
  | 
	// Переменная кода ввода типа ПК  | 
|||
cout <<  | 
	"Выберите тип компьютера : " << endl; // Вывод меню  | 
||||
cout <<  | 
	"0  | 
	-  | 
	сервер "  | 
	<< endl;  | 
|
cout <<  | 
	"1  | 
	-  | 
	настольный  | 
	ПК " << endl;  | 
|
cout <<  | 
	"2  | 
	-  | 
	ноутбук "  | 
	<< endl;  | 
|
cout <<  | 
	"3  | 
	-  | 
	нетбук  | 
	"  | 
	<< endl;  | 
cout <<  | 
	"4  | 
	-  | 
	КПК "  | 
	<< endl;  | 
|
cin >> code;  | 
	// Чтение кода типа ПК с клавиатуры  | 
||||
pc=code;  | 
	
  | 
	
  | 
	// Присваивание перечислению значения  | 
||
switch(pc)  | 
	
  | 
	
  | 
	
  | 
	// Переключатель вывода типа ПК  | 
|
{ case server : cout << "тип компьютера-сервер" << endl;  | 
|||||
  | 
	
  | 
	
  | 
	break;  | 
	
  | 
|
case desktop : cout<<"тип компьютера-настольный ПК"<<endl; break;
case notebook : cout << "тип компьютера-ноутбук" << endl; break;
case netbook : cout << "тип компьютера - нетбук" << endl; break;
case handbook : cout << "тип компьютера - КПК" << endl; break;
} // Конец блока оператора switсh
getch(); // Приостановка закрытия консоли return 0; // Возврат кода успешного завершения
}//-----------------------------------------------------------
6.10 Совместимость и преобразование типов данных
6.10.1 Основные понятия совместимости типов
Различают следующие понятия при сравнении различных типов данных:
1)тождественность типов данных двух переменных означает, что две переменные используют один идентификатор типа данных;
2)совместимость двух типов данных означает, что вместо переменной одного
типа данных можно использовать переменную второго типа данных без потери
информации;  | 
	
  | 
	
  | 
	
  | 
	
  | 
	
  | 
	
  | 
3) совместимость  | 
	по  | 
	присваиванию  | 
	(A=B)  | 
	двух  | 
	типов  | 
	данных  | 
(typeА A и typeB B) означает, что при операции присваивания переменной A можно передать значение переменной B без потери или искажения данных.
Два структурных типа являются различными даже, когда они имеют одинаковые поля или члены. Например:
struct s1 { int a; }; // Первый тип структура struct s2 { int a; }; // Второй тип структура
Описаны два различных типа, поэтому имеется несовместимость по присваиванию. Структурные типы отличны также от основных типов:
s1  | 
	x;  | 
	
  | 
	
  | 
s2  | 
	y = x;  | 
	// Ошибка несоответствия типов  | 
|
int i  | 
	= x;  | 
	// Ошибка несоответствия типов.  | 
|
26
6.10.2 Определение нового имени типа с помощью typedef
Имеется возможность выполнить описание нового имени для существующего типа с использованием префикса typedef, которое при этом не приводит к созданию нового типа. Формат описания нового имени типа:
typedef имя_типа новое_имя_типа;
Пример:
typedef int length;
Это описание делает имя length синонимом стандартного типа int. «Тип» length может быть использован в описаниях, преобразованиях типов и т.д. аналогично применению типа int. Примеры:
length  | 
	len, maxlen;  | 
	//  | 
	Объявление переменных типа int  | 
length  | 
	*lengths[];  | 
	//  | 
	Формирование указателя на вектор  | 
Пример описания нового имени string:
typedef *char string;
Это делает string синонимом для *char, то есть для указателя на символы, что затем можно использовать в описаниях вида
string p, lineptr[lines], alloc();
6.10.3 Преобразование типов данных в языке С++
Для придания программе гибкости и универсальности используется преобразование одного типа данных в другой. В языках С и С++ это можно выполнить тремя способами:
1)с помощью операций неявного преобразования типов в выражениях;
2)с помощью явного приведения типа;
3) с помощью переменных, хранящих  | 
	данные  | 
	в  | 
	одной области памяти  | 
(например, объединений или указателей).  | 
	
  | 
	
  | 
	
  | 
Неявные преобразования используются,  | 
	если  | 
	в  | 
	выражениях встречаются  | 
операнды различных типов, которые автоматически преобразуются к общему типу в соответствии с определенными правилами. Производятся только преобразования, имеющие смысл, такие как, например, преобразование целого в действительное в выражениях с арифметическими операциями. Правила неявного преобразования:
1)типы char и int могут свободно смешиваться в арифметических выражениях: переменная типа char автоматически преобразуется в int;
2)выражения отношения и логические выражения, связанные операциями && и
||, по определению имеют значение 1, если они истинны, и 0, если они ложны; 3) к арифметической операции применяется следующая последовательность
правил преобразования:
−типы char и short преобразуются в int, а float в double;
−если один из операндов имеет тип double, то другой преобразуется в double и результат имеет тип double;
− если  | 
	один из операндов имеет тип long, то другой преобразуется в  | 
long  | 
	и результат имеет тип long;  | 
−если один из операндов имеет тип unsigned, то другой преобразуется в unsigned и результат имеет тип unsigned;
4)все переменные типа float в выражениях преобразуются в double − вся вещественная арифметика выполняется с двойной точностью.
Преобразования автоматически выполняются при присваиваниях: значение правой части преобразуется к типу левой, который и является типом результата. Символьные переменные преобразуются в целые либо со знаковым расширением, либо
без него. При обратном преобразовании типа int в char лишние биты высокого порядка отбрасываются.
Если тип float присваивается типу int, то преобразование float в int выполняется отбрасыванием дробной части. Тип double преобразуется во float округлением. Длинные целые преобразуются в более короткие целые и в переменные типа char посредством отбрасывания лишних битов высокого порядка.
27
При передаче в качестве параметров функций также происходит неявное преобразование типов фактического параметра в указанный тип формального параметра.
Внутри выражения может осуществляться явное преобразование типа с помощью конструкции, называемой переводом (CAST), имеющей следующий формат:
(имя_типа)выражение
Смысл этой операции в том, что выражение условно присваивается некоторой переменной указанного типа, используемой вместо всей конструкции. Это может быть необходимо, например, для функции извлечения корня sqrt, которая ожидает аргумента типа double. Если N - целое, то можно выполнить выражение
int N=3; // Объявление целочисленной переменной cout << sqrt((double) N); // Вывод значения корня от 3
В примере N преобразует к типу double до передачи аргумента функции sqrt. Явное приведение типов также используется при преобразовании указателей.
При этом используется следующий формат:
(имя_базового_типа*) указатель;
Так обозначение (базовый_тип*) преобразует указатель в тип «указатель на базовый тип данных». Ниже показан пример преобразования указателей на базовые типы char и int.
char  | 
	*str;  | 
	// Указатель  | 
	символьного типа  | 
|||
int *ip;  | 
	// Указатель  | 
	целочисленного типа  | 
||||
str = (char*) ip;  | 
	//  | 
	Приведение  | 
	второго  | 
	указателя  | 
||
ip =  | 
	(int*) str;  | 
	//  | 
	Приведение  | 
	первого  | 
	указателя  | 
|
6.10.4 Определение размера типа
Специальная операция sizeof(тип-операнд) позволяет определить размер в байтах стандартного или определяемого пользователем типа данных. Возможны два способа использования операции sizeof:
1)унарное sizeof выражение;
2)sizeof (имя_типа).
Впервом случае тип выражения операнда определяется без расчета выражения (без побочных эффектов). Если операнд имеет тип char (signed или unsigned), то операция sizeof дает в результате 1. Если операнд не является параметром и имеет тип массива, то результат представляет собой общее количество байтов в массиве (имя массива не преобразовывается к типу указателя). Число элементов массива можно определить как
Число_элементов = sizeof (массив) / sizeof (массив[0]);
Если операнд sizeof является массивом или функцией, то sizeof дает размер указателя. Применительно к структурам и объединениям sizeof дает общее число байтов, включающее любые символы-заполнители.
В языке С++ для объектно-ориентированных программ операция
sizeof(тип_производного_класса_от_базового)
возвращает размер базового класса.
