Структура программы. Глобальные и локальные данные (области видимости и время жизни) 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Структура программы. Глобальные и локальные данные (области видимости и время жизни)



Структура программы. Глобальные и локальные данные (области видимости и время жизни)

Структура программы

Любая программа на языке C++ представляет собой одну или множество функций. Среди этих функций должна обязательно присутствовать главная функция main (), являющаяся точкой входа в программу (выполнение программы начинается с выполнения этой функции).

Основное требование по использованию функций состоит в том, что определение любой функции должно предшествовать ее вызову (компилятору необходимо “знать”, как вызвать ту или иную функцию: какое значение она возвращает, что собой представляют ее параметры). Возникновение коллизий, связанных с этим правилом, разрешается использованием прототипов функций.

В языке C++ отсутствует понятие вложенных функций, то есть внутри определять другие функции нельзя.

Глобальные и локальные данные

В языках программирования очень большое значение имеют понятия область видимости и время жизни объектов программы.

Область видимости это часть текста программы, в котором может быть использован данный объект.

Время жизни переменной - интервал выполнения программы, в течение которого программный объект существует в памяти.

Оба эти понятия тесно связаны с понятием блока программы. Блоком в программе является последовательность объявлений или операторов, заключенных в фигурные скобки {}. Существуют два типа блоков:

1) составной оператор;

2) определение функции.

Блоки могут включать в себя составные операторы и другие блоки, но не могут содержать определения функции, то есть внутри функции нельзя определить другую функцию.

Объекты, объявленные вне блоков, обычно называют глобальными объектами. С этой точки зрения все функции программы являются глобальными объектами. Время жизни функций – все время выполнения программы. Область видимости функций начинается с точки ее объявления (это определение самой функции или объявление ее прототипа) и заканчивается при завершении программы. Кроме функций на глобальном уровне можно определять и другие программные объекты – константы, переменные, типы данных и т.д. Их время жизни и область видимости также начинаются с точки объявления.

Переменные и другие программные объекты, определенные внутри блока называют локальными объектами. Временем жизни таких объектов обычно является блок, в котором эти объекты определены. Область видимости локальных объектов – промежуток между объявлением локального объекта и концом блока, в котором эти объекты определены. В функции все формальные параметры и другие объекты, которые определены внутри тела функции являются локальными. Почти все они создаются в памяти (в стеке) на время работы функции и уничтожаются при ее завершении.

Рассмотрим пример:

 

int I = 10; // Глобальная переменная

…….

void F (int a) // Параметр а – локальная переменная

{

cout << I;   // На экране 10 – значение глобальной переменной I

……

if (I)        // Анализируется значение глобальной переменной I

{

            int I = 200, J = 3000; // Локальные переменные

            cout << I; //  На экране 200 – значение локальной переменной I

            …….

   }

cout << I; // На экране 10 – значение глобальной переменной I

cout << J; // Ошибка – переменной J больше не существует

……

}

 

При совпадении имен глобального и локального объекта локальный объект “перекрывает“ глобальный объект (в нашем примере – переменные I).

Время жизни и область видимости локальных переменных, определенных на уровне заголовка цикла for, – весь оператор for:

 

for (int a = 0, b = 10; a < 20; ++ a, -- b)

{

…….

cout << a * b;

…….

}

 

Здесь, хотя переменные a и b определены вне блока, их можно использовать внутри блока тела цикла.

Областью видимости и временем жизни программных объектов можно управлять, используя модификаторов классов памяти.

Классы памяти

Модификаторы (спецификаторы) классов памяти используются для управления временем жизни и областью видимости программных объектов. Чаще всего они применяются к переменным. Например, формат определения переменной включает в себя возможность указания класса памяти переменной:

 

[< класс памяти >] < тип данных > < идентификатор > [< инициализация >]

В языке C++ определены 4 основные класса памяти:

· auto

· register

· static

· extern

Спецификатор auto используется при определении локальных объектов (переменных внутри функций или блоков). Указание класса памяти auto используется крайне редко, поскольку по умолчанию (когда класс памяти не указывается) все локальные переменные являются автоматическими, т.е. принадлежат классу памяти auto. Объекты, имена которых объявляются со спецификатором auto, размещаются в динамически распределяемой памяти (в стеке) непосредственно перед началом выполнения функции или блока операторов. При выходе из блока или при возвращении из функции, соответствующая область памяти освобождается и все ранее размещённые в ней объекты уничтожаются. Таким образом, спецификатор влияет на время жизни объекта (это время локально). Например:

 

Void F (int i)

{

int j = 20;

if ( i == 10)

{

       double Pi = 3.14;

            ……..

     }

}

 

В этой функции все переменные (i, j, Pi) являются локальными автоматическими объектами. Переменные i и j создаются в памяти в начале выполнения функции и уничтожаются при выходе из функции. Время жизни и область видимости этих переменных одинаковы и соответствуют всему телу функции. Переменная Pi создается в памяти, когда начинает выполняться соответствующий блок инструкции if, и уничтожаются при выходе из этого блока.

Спецификатор register используется довольно часто для увеличения быстродействия программы. Этот спецификатор “просит” компилятор поместить соответствующую переменную в памяти так, чтобы доступ к ней осуществлялся как можно быстрее. Регистровые переменные размещаются либо в регистрах процессора, либо в кэш-памяти компьютера. Поскольку объем такой памяти ограничен, компилятор эту “просьбу” может и не выполнить. В этом случае регистровые переменные создаются как обычные автоматические переменные. Пример определения регистровой переменной:

 

register double d = 1.23;

 

С помощью спецификатора класса памяти extern осуществляется обращение к глобальным переменным, определенным либо в других файлах проекта, либо далее в этом же файле. Например:

 

Void F ()

{

extern int ArrSize;

……..

}

………

int ArrSize = 100;

………

Когда компилятор встречает спецификатор extern, он не создает в памяти новую переменную, а пытается найти переменную ArrSize далее в этом файле, а затем в других файлах проекта. Если такая глобальная переменная находится, то она и используется в функции F. Говорят, что строка extern int ArrSize; является объявлением переменной (переменная только объявляется, но не создается в памяти), а строка int ArrSize = 100; - определением переменной, при котором создается сама переменная.

Спецификатор static позволяет определять переменные, имеющие глобальное время жизни (они существуют до конца работы программы), но ограниченную область видимости. С точки зрения области видимости static- переменные делятся на два вида:

· локальные static- переменные, определяемые внутри функций;

· внешние static- переменные, определяемые вне функций.

Локальные static- переменные определяются внутри функций и после своего определения существуют до конца работы программы, сохраняя свои значения. После завершения функции они не уничтожаются и сохраняют свои значения до следующего вызова этой функции. Область видимости таких переменных ограничена только блоком функции, в которой они определены, и в других функциях они не видны. Хорошей иллюстрацией использования локальных статических переменных является функция для получения текущего среднего значения числовой последовательности, вычисляемого в процессе ввода элементов этой последовательности:

 

double Srednee (int n);

Int main ()

{

setlocale (0, "");

int n;

do

{

            cin  >>  n;

            if (n >= 0)

                       cout << "Среднее значение: " << Srednee (n);

            cout  <<  endl;

}

while (n >= 0);

  return 0;

}

Double Srednee (int n)

{

static int Sum = 0, Count = 0;

++ Count;

Sum += n;

return double (Sum) / Count;

}

 

Локальные static- переменные Sum и Count инициализируются значениями 0 один раз при первом вызове функции Srednee. Затем их значения изменяются, и эти измененные значения после завершения работы функции сохраняются до ее следующего вызова.

Альтернативным решением этой задачи является использование глобальных переменных. Однако использование глобальных переменных чревато нежелательными побочными эффектами, связанными с возможными скрытыми изменениями их значений в других функциях программы.

Внешние static- переменные определяются вне функций. Они, так же как и локальные static- переменные имеют глобальное время жизни. Но их область видимости ограничена файлом проекта, в котором они определены (они могут быть доступны только в функциях, расположенных в этом же файле и не видны из других файлов проекта). Таким образом, их можно рассматривать как глобальные переменные с областью видимости, ограниченной файлом, в котором они определены.

Спецификатор static можно применять и к функциям.

Многофайловые проекты

 

Напишу позже!!!

Структуры

Определение структур

Структуры представляют собой группы связанных между собой, как правило, разнотипных переменных, объединенных в единый объект.

Для работы с некоторой структурой в программе необходимо:

1. описать тип данных этой структуры;

2. определить переменные этого типа для хранения соответствующих данных в памяти.

Рассмотрим пример: сведения о студенте содержат следующие данные:

· фамилия - Fam;

· имя - Name;

· год рождения - Year;

· пол - Sex;

· средний балл - Grade.

Представим все эти данные в виде единой структуры. Введем новый тип данных (назовем его t_ Student)  для описания этой структуры:

 

struct t_Student {

char Fam [20],

         Name [16];

short Year;

bool Sex;

float Grade;

};

Описание структуры начинается ключевым словом struct. Каждая входящая в структуру переменная называется членом (полем, элементом) структуры и описывается типом данных и именем. Поля структуры могут быть любого типа данных. Их количество не лимитировано.

Вся эта конструкция является инструкцией языка программирования, поэтому после нее всегда должен ставиться символ;.

При описании структуры память для размещения данных не выделяется. Работать с описанной структурой можно только после того, как будет определена переменная (переменные) этого типа данных, только при этом компилятор выделит необходимую память:

 

t_ Student St1, St2; // Определены две переменные типа t_Student 

 

Определение переменных можно осуществить одновременно с описанием типа данных структуры:

 

struct  t_Student {

char Fam [20],

         Name [16];

short Year;

bool Sex;

float Grade;

} St1, St2;

 

Все поля структурных переменных располагаются в непрерывной области памяти одно за другим. Общий объем памяти, занимаемый структурой, равен сумме размеров всех полей структуры. Для определения размера структуры следует использовать инструкцию sizeof (): sizeof (t_ Student)  или sizeof (St2).

Доступ к полям структур

Для того чтобы записать данные в структурную переменную, необходимо каждому полю структуры присвоить определенное значение. Для этого необходимо научиться получать доступ к полям. Для этого используется оператор “точка”. Например:

 

strcpy (St1.Fam, “Иванов ”);

strcpy (St1.Name, “Владимир ”);

St1.Year = 1995;

St1. Sex = true; // Надо договориться какое значение соответствует значению пола

St1. Grade = 4.67;

При доступе к определенному полю его следует рассматривать как обычную переменную, тип данных которой соответствует типу этого поля. Поля структур могут участвовать в качестве операндов любых выражений, допускающих использование операндов соответствующего типа данных.

Копирование данных из одной структурной переменной в другую осуществляется простой операцией присваивания не зависимо от количества полей и размера структуры (это можно делать только в том случае, когда обе переменные одного и того же типа):

 

St2 = St1;

Теперь переменная St2 содержит те же данные, что и переменная St1.

В программировании очень часто используются такие конструкции, как массивы структур. Например, сведения о студентах некоторой учебной группы можно хранить в массиве студентов:

 

t_ Student Gruppa [25];

Здесь мы определили 25-ти элементный массив, каждый элемент которого предназначен для хранения данных одного студента. Получение доступа к данным некоторого студента из группы осуществляется обычной индексацией переменной массива:

 

St1 = Gruppa [10]; // Переменная St1 содержит сведения об 11-ом студенте

 

Доступ к некоторому полю студента внутри массива делается так:

 

double grade = Gruppa[10].Grade; // Переменная grade содержит среднюю оценку 11-ого студента

 

Если некоторое поле структуры представляет собой массив (например, поле Fam – это массив символов), доступ к отдельному элементу этого массива можно выполнить так:

 

St1. Fam[5] = ‘ш’;

 

Или так:

 

Gruppa[10]. Fam[5] = ‘ш’;

 

Поскольку поля структуры могут быть любого типа данных, то они могут в свою очередь быть другой структурой или массивом других структур. Определим группу, как структуру, содержащую три поля – номер группы, количество студентов в группе и массив студентов этой группы.

Определяем новый тип данных и переменные для двух групп:

 

struct t_Gruppa {

short NumGr;                  // Номер группы

short Count;                          // Количество студентов в группе

t_ Student Students[25];    // Массив студентов группы

} Gr1372, St1373;

 

Получим данные о некотором студенте из группы Gr1372:

 

St1 = Gr1372. Students[10];         // Переменная St1 содержит сведения об 11-ом студенте

 

А вот его средний балл:

 

grade = Gr1372. Students[10]. Grade;

В структуре в качестве поля нельзя использовать элемент, тип которого совпадает с типом самой структуры (рекурсивное использование структур запрещено).

Указатели на структуры

Любая структурная переменная занимает в памяти определенное положение, характеризующееся конкретным адресом. Для работы с адресами структурных переменных (как и для простых переменных) можно использовать указатели. Указатели на структурные переменные определяются точно так же, как и для обычных переменных:

 

t_ Student * p_ Stud; // Переменная p_ Stud указатель на тип данных t_ Student

p_ Stud = & St1;     // Переменной p_ Stud присвоен адрес переменной St1

 

Разыменование указателя (обращение к данным по адресу, хранящемуся в указателе) осуществляется обычным образом:

 

St2 = * p_ Stud;      // Данные по адресу p_ Stud скопированы в переменную St2

Через указатели можно работать с отдельными полями структур. Для доступа к полю структуры через указатель используется оператор “стрелка”, а не “точка”:

 

grade = St1.Grade;  // Доступ к полю Grade через обычную структурную переменную с помощью оператора “точка”

grade = p_Stud -> Grade;  // Доступ к полю Grade через указатель на структурную переменную с помощью оператора “ стрелка”

p_ Stud -> Grade = 3.75; // Полю Grade через указатель на структурную переменную присвоено новое значение

Битовые поля структур

Для более экономного использования памяти очень часто используют побитное хранение числовых и логических значений. Такой способ хранения данных можно реализовать на основе использования побитовых операций. Однако существует и другой метод доступа к отдельным битам памяти – с помощью битовых полей структур. Например:

 

Struct Date

{

unsigned WeekDay  : 3; // 0..7      (3 бита)

unsigned MonthDay: 5; // 0..31     (5 бит)

unsigned Month       : 4; // 0..12    (4 бита)

unsigned Year     : 12; // 0..4048 (12 бит)

} D1;

 

Под переменную D1 в памяти будет отведено всего 4 байта:

 

0     … 2 3         …         7 8      …      11 12                    …                 23 24       …        31
WeekDay MonthDay Month Year Не используется
3 бита 5 бит 4 бита 12 бит 8 бит

 

Число, указанное в определении поля после двоеточия, определяет количество бит, отведенных под соответствующее битовое поле. Тип битового поля определяет способ интерпретации этого поля в программе.

Для хранения логического значения под битовое поле отводится 1 бит:

 

Struct Flags

{

  bool  b1: 1;

  bool  b2: 1;

  bool  b3: 1;

  bool  b4: 1;

  bool  b5: 1;

  bool  b6: 1;

  bool  b7: 1;

bool b8: 1;

} Fl;

 

Под переменную Fl в памяти будет отведен 1 байт. Таким образом, в одном байте памяти можно разместить 8 логических значений.

Обращение к битовым полям осуществляется так же, как и к обычным полям структур:

 

D1.WeekDay = 4;

D1.MontthDay = 25;

D1.Month = 9;

D1. Year = 2004;

 

В одной структуре можно совместно использовать и обычные и битовые поля.

 

Объединения

Обычные объединения

Объединения – это две или более переменных расположенных по одному адресу (говорят, что они разделяют одну и ту же память). Объединения определяются подобно структурам, но только с использованием ключевого слова union:

 

union t_num{

int i;

double d;

};

 

Здесь описан новый тип данных с именем t_ num, представляющий собой объединение двух членов (полей, элементов), каждый из которых задан типом данных и именем. Члены объединений могут быть любых типов, а их количество – любым.

Для работы с объединением следует определить соответствующие переменные:

 

t_ num A, B; // Определены две переменные А и В

Создание переменных модно совместить с описанием объединения:

 

union t_num{

int i;

double d;

} A, B;    // Описан тип данных t_ num и определены две переменные А и В

 

Обращение к полям объединения осуществляется так же, как и к элементам структуры – с использованием оператора “точка”:

 

A.i = 13;

B.d = 3.14;

cout << A.i + B.d;

 

Если обращение к полям объединения осуществляется через указатель, то необходимо использовать оператор “стрелка”:

 

t_ num * p = & A; // Указатель на объединение р инициализирован адресом переменной А

p -> d = 234.33; // Полю d переменной А присвоено новое значение

 

Передача объединений в качестве аргументов функций осуществляется точно так же, как и для структур.

В памяти члены объединения “накрывают” друг друга (располагаются по одному адресу):

 

Если вывести на экран адреса переменной А и полей i и d

 

cout << &A << " " << &A.i << " " << &A.d << endl;

то будут выведены три одинаковых адреса.

Объединения не могут хранить одновременно несколько различных значений, они позволяют интерпретировать несколькими различными способами содержимое одной и той же области памяти.

Например, необходимо написать функцию обеспечивающую перестановку младшего и старшего байта короткого целого:

 

Short Swap (short I)

{

union t_U {

      short i;

char c [2];

} u;

u.i = I;

char ch = u.c [0];

u.c [0] = u.c [1];

u.c [1] = ch;

return u.i;

}

cout << Swap (1234) << endl; // На экране -11772

Переменная u из этой функции интерпретируется в памяти так:

 

Анонимные объединения

Имеется специальный вид объединений – анонимные объединения. Анонимные объединения объявляются без указания имени типа:

 

union {

      short i;

char c [2];

};

 

Создавать переменные такого типа нельзя. Анонимные объединения позволяют обращаться к своим полям как к обычным переменным – без применения оператора “точка”. Они просто указывают на то, что переменные, соответствующие их полям, разделяют одну и ту же память.

Функция Swap с использованием анонимного объединения выглядит так:

 

Short Swap (short I)

{

union {

      short i;

char c [2];

};

i = I;

char ch = c [0];

c [0] = c [1];

c [1] = ch;

return i;

}

Перечисления

Перечисления (enumeration) служат для определения пользовательских типов данных, значения которых принадлежат одному из элементов в некотором списке именованных целочисленных констант. Перечисление задается ключевым словом enum:

 

enum  тип_Спектр {

красный, оранжевый, желтый, зеленый, голубой, синий, фиолетовый

}  Цвет1, Цвет2;

Новый пользовательский тип данных тип_Спектр определяет список именованных целочисленных констант (этот список находится внутри фигурных скобок {}). Значениями переменных Цвет1 и Цвет2 могут быть только те значения, имена которых перечислены в определении этого типа данных. Переменной этого типа может быть присвоено любое из перечисленных имен:

 

Цвет1 = желтый;

Цвет2 = синий;

Каждое имя в списке представляет собой некоторое целое значение. По умолчанию целые значения констант перечисления начинаются с 0 и последовательно увеличиваются на единицу:

 

for (Цвет1 = красный; Цвет1 <= фиолетовый; Цвет1 = тип_Спектр(Цвет1 + 1))

cout << Цвет1 << endl;

Этот фрагмент программы выведет на экран целые значения от 0 до 6.

При вводе-выводе значений этих типов данных используются именно числовые значения констант, а не их имена. Если необходимо вывести значение перечисления в виде его имени, а не числового значения, необходимо реализовать это программно, например, написав функцию (вывод данных):

 

void WriteColor (тип_Спектр C)

{

switch (C)

{

            case красный: cout << "красный"; break;

            case оранжевый: cout << "оранжевый"; break;

            case желтый: cout << "желтый"; break;

            case зеленый: cout << "зеленый"; break;

            case голубой: cout << "голубой"; break;

            case синий: cout << "синий"; break;

            case фиолетовый: cout << "фиолетовый"; break;

            default: cout << "Неправильный цвет\ n";

}

}

Числовые значения констант перечисления можно изменять, например, так:

 

enum  тип_Спектр {

красный, оранжевый, желтый = 10, зеленый, голубой, синий, фиолетовый

};

 

После такого переопределения числового значения константы желтый, числовые значения всех констант будут следующими:

 

красный оранжевый желтый зеленый голубой синий фиолетовый
0 1 10 11 12 13 14

 

Перечисления обычно используются для того, чтобы сделать тексты программ более понятными, и как следствие, избавиться от дополнительных возможных ошибок.

Потоки для работы с файлами

Общие сведения

Для работы с файлами в языке C++ используются потоки трех видов:

· поток ввода (класс ifstream);

· поток вывода (класс ofstream);

· поток ввода-вывода (класс fstream).

Класс ifstream используется для выполнения чтения данных из файлов. Поток ofstream – для записи данных в файлы. Поток fstream – для чтения и записи данных в файлы.

Для использования этих классов потоков необходимо в программу включить заголовочный файл < fstream >.

Для выполнения любых работ с файлом необходимо:

1) создать поток одного из классов файловых потоков;

2) связать созданный поток с конкретным файлом (открыть конкретный файл для работы);

3) выполнить необходимые действия с файлом (чтение / запись данных);

4) закрыть файл.

Пример работы с файлом

 

bool Запись_данных_в_текстовый_файл (char * FileName)

{

ofstream File;   // Создали поток вывода для записи данных в файл

File.open (FileName); // Открыли файл

if (! File. is_ open ()) // Проверили удалось ли открыть файл

{

   cout << "Открыть файл не удалось! \ n";

   return 0; // Файл не открыт

}

for (int I = 1; I <= 10; ++ I) // Записываем 10 строк в файл

  File << "Строка " << I << endl;

File. close (); // Закрываем файл

return 1;

}

 

bool Чтение_данных_из_текстового_файла (char * FileName)

{

ifstream File; // Создали поток ввода для чтения данных из файла

File. open (FileName); // Открыли файл

if (! File. is_ open ()) // Проверили удалось ли открыть файл

{

   cout << “Открыть файл не удалось! \ n”;

   return 0; // Файл не открыт

}

char S[100];

while (! File. eof ()) // Читаем все строки из файла и выводим их на экран

{

   File.getline (S, 100);

   cout << S << endl;

}

File.close (); // Закрываем файл

return 1;

}

 

Int main ()

{

setlocale (0, "");

if (Запись _данных _в _текстовый _файл ("E:\\test.txt"))

            Чтение_данных_из_текстового_файла(" E:\\ test. txt");

system("pause");

return 0;

}

 

Работа с файлами

Создание потока, открытие и закрытие файла

 

Либо последовательность из двух инструкций:

 

ofstream File; - создали поток

File. open (" E:\\ test. txt"); - связали поток с файлом (открыли файл)

 

Либо так:

 

ofstream File (" E:\\ test. txt"); - создали поток и открыли файл

 

После открытия файла необходимо обязательно проверить открылся ли файл.

Если файл открыть не удалось, то переменная потока (File) принимает значение false, если файл открыт – true. Следовательно, проверку успешного открытия файла можно выполнит так:

 

if (! File)

// Ошибка

 

Еще один способ – использовать функцию потока is_ open (), которая также возвращает логическое значение в зависимости от результата операции открытия файла:

 

if (! File. is_ open ())

// Ошибка

 

Файл закрывается с помощью функции потока close ():

 

File. close ();

Подробнее об открытии файла. Каждый поток использует свой вариант функции open.   Их прототипы выглядят так:

 

void ifstream::open (const char * FileName,

                               ios::openmode Mode = ios::in);

void ofstream::open (const char * FileName,

                                ios::openmode Mode = ios::out | ios::trunc);

void fstream::open (const char * FileName,

                               ios::openmode Mode = ios::in | ios::out);

 

Первый параметр определяет имя открываемого файла (представляет собой массив символов).

Второй параметр определяет режим открытия файла. Этот параметр имеет значение по умолчанию, то есть является необязательным. Возможные значения этого параметра:

· ios:: app – при открытии файла на запись (поток ofstream) обеспечивает добавление всех выводимых в файл данных в конец файла;

· ios:: ate – обеспечивает начало поиска данных в файле начиная с конца файла;

· ios:: in – файл открывается для чтения из него данных;

· ios:: out – файл открывается для записи данных в файл;

· ios:: binary – открытие файла в двоичном режиме (по умолчанию все файлы открываются в текстовом режиме);

· ios:: trunc – содержимое открываемого файла уничтожается (его длина становится равной 0).

Эти флаги можно комбинировать с помощью побитовой операции ИЛИ (|).

Если файл открывается без использования функции open, эти флаги тоже можно использовать:

fstream File (" E:\\ test. txt", ios:: out | ios:: app); - файл открывается на вывод с добавлением записываемых данных в конец файла.

 

Int main ()

{

setlocale (0, "");

// Запись массива А в_файл "E:\test.dat"

ofstream o_ File; // Создали поток вывода для записи данных в файл

       o_File.open ("E:\\test.dat", ios::binary); // Открыли файл

if (! o_ File. is_ open()) // Проверили, удалось ли открыть файл

{

            cout << "Открыть файл не удалось! \ n";

            return 0;

}

// Записываем данные из массива А в файл

int A[5] = {1, 2, 3, 4, 5};

char * ch = (char *) A; // ch – ссылка на массив А, как на массив символов (байт)

for (int I = 0; I < sizeof(A); ++ I)

            o_File.put(ch[I]);

o_ File. close(); // Закрываем файл

// Чтение данных из_файла "E:\test.dat" в массив В;

ifstream i_ File; // Создали поток ввода для чтения данных из файла

i_File.open ("E:\\test.dat", ios::binary); // Открыли файл

if (! i_ File. is_ open()) // Проверили, удалось ли открыть файл

{

            cout << "Открыть файл не удалось! \ n";

            return 0;

}

// Читаем данные из файла в массив B

int B[5];

ch = (char *) B; // ch – ссылка на массив В, как на массив символов (байт)

int I = 0;

while (i_File)

            i_File.get(ch[I++]);

// Предыдущий цикл можно записать короче, например, так:

// while (i_File.get(*ch++);

i_ File. close(); // Закрываем файл

// Выводим массив В на экран

for (I = 0; I < 5; ++ I)

            cout << B[I] << " ";

cout << endl;

system("pause");

return 0;

}

 

А вот как выглядит та же программа при использовании блочных функций чтения и записи (read () и write ()) в двоичном режиме:

 

Int main ()

{

setlocale (0, "");

// Запись массива А в_файл "E:\test.dat"

ofstream o_ File; // Создали поток вывода для записи данных в файл

o_File.open ("E:\\test.dat", ios::binary); // Открыли файл

if (! o_ File. is_ open()) // Проверили, удалось ли открыть файл

{

            cout << "Открыть файл не удалось! \ n";

            return 0;

}

int A[5] = {1, 2, 3, 4, 5};

o_ File. write ((char *) A, sizeof (A)); // Записываем данные из массива А в файл

o_ File. close (); // Закрываем файл

// Чтение данных из_файла "E:\test.dat" в массив В;

ifstream i_ File; // Создали поток ввода для чтения данных из файла

i_File.open ("E:\\test.dat", ios::binary); // Открыли файл

if (! i_ File. is_ open()) // Проверили, удалось ли открыть файл

{

            cout << "Открыть файл не удалось! \ n";

            return 0;

}

    

int B[5];

i_ File. read ((char *) B, sizeof (B));  // Читаем данные из файла в массив B

i_ File. close (); // Закрываем файл

// Выводим массив В на экран

for (int I = 0; I < 5; ++ I)

            cout << B[I] << " ";

cout << endl;

system("pause");

return 0;

}

 

Функции read () и write () имеют следующие прототипы:

 

ifstream & read (char * buf, streamsize n);

ofstream & write (const char * buf, streamsize n);

 

Первый параметр этих функций определяет адрес буфера (некоторого массива символов - байт) для чтения или записи данных в соответствующий файловый поток. Второй параметр задает количество символов – байт, которые необходимо взять из потока или записать в поток (тип данных streamsize – целый тип данных). Размер буфера должен соответствовать значению второго параметра.

Функция write () обеспечивает запись из буфера, адрес которого указан в первом параметре функции, n символов данных в поток вывода и возвращает ссылку на поток.

Функция read () обеспечивает запись из потока ввода n символов данных в память по адресу, указанному в первом параметре buf. При достижении конца файла функция возвращает ссылку на поток, равную 0, а фактическое количество взятых из потока символов может быть меньше, чем значение n второго параметра (буфер заполнен не полностью). Фактическое количество считанных из потока ввода символов после выполнения последней операции чтения можно определить с помощью функции потока ввода gcount(). Следующий пример иллюстрирует использование функций блочного чтения и записи данных из произвольного файла.

 

void FileToScr (char * FileName)

{

fstream File (FileName, ios::in | ios::binary);

if (! File) // Проверили удалось ли открыть файл



Поделиться:


Последнее изменение этой страницы: 2021-09-26; просмотров: 68; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.144.25.74 (0.464 с.)