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(тип_производного_класса_от_базового)
возвращает размер базового класса.