Free(X);//освобождение памяти из-под массива
}
Многомерные массивы. Двумерный массив трактуется как одномерный массив, элементами которого является массив с указанным в описании типом элементов. Например, оператор
float R[5][10];
объявляет массив из пяти элементов, каждый из которых есть массив из 10-ти вещественных чисел. Отдельные величины этого массива обозначаются именами с двумя индексами: R[0][0], R[0][1], … , R[4][9]. Объединять индексы в одну пару скобок нельзя, т.е. запись R[2,3] – ошибочна.
Пример описания трехмерного массива:
double X[3][7][20];
Порядок расположения элементов многомерного массива в памяти такой, что быстрее всего меняется последний индекс, затем предпоследний и т.д., и лишь один раз пробегает свои значения первый индекс.
При описании многомерных массивов их также можно инициализировать. Делать это удобно так:
int M[3][3] = { {11, 12, 13},
{21, 22, 23},
{31, 32, 33} };
Если при описании двумерного массива его элементы инициализируются, то второй размер можно опускать, а указание количества строк обязательно:
int M[3][] = { {11, 12, 13},
{21, 22, 23},
{31, 32, 33} };
Память под двумерный массив можно выделять динамически. В этом случае надо помнить о том, что имя двумерного массива — это указатель на массив указателе.
Пример 3.
#include <stdlib.h>
#include <stdio.h>
Void main()
{float **a,b;
int n,m,i,j;
printf("\n n,m-?");//ввод размера матрицы
scanf("%d%d",&n,&m);
//выделение динамической памяти под массив указателей
a=(float**)malloc(n*sizeof(float*));
//выделение динамической памяти под элементы матрицы
for (i=0;i<n;i++)
{a[i]=(float*)malloc(m*sizeof(float));
}
//ввод значений
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{ printf("%d,%d:",i,j);
scanf("%f",&b);
a[i][j]=b;
}
//печать матрицы по строкам на экране
for(i=0;i<n;i++)
{printf("\n");
for(j=0;j<m;j++)
printf("%4.2f ",a[i][j]);
}
printf("\n");
//освобождение динамической памяти
for (i=0;i<n;i++)
free(a[i]);
free(a);
}
Упражнения
1. Дан вектор {zi}, i = 1,...,n. Вычислить длину этого вектора:
2. Вычислить полином n-й степени по формуле Горнера:
anxn + an-1xn-1 + ... + a1x + a0 = ((...(anx + an-1)x + an-2)x + ... + a1)x + a0.
3. Дан массив, состоящий из n целых чисел.
а) вывести все числа, которые встречаются в этом массиве несколько раз;
б) вывести все числа, которые встречаются в массиве только по одному разу.
4. Транспонировать целочисленную матрицу n ´ n, т.е. перевернуть вокруг главной диагонали.
5. В двумерном массиве n´n удалить все строки и столбцы, на пересечении которых стоят отрицательные элементы.
Указатели
Указатель – это адрес поля памяти, занимаемого программным объектом.
Пусть в программе определены три переменные разных типов:
int a=5;
char c=’G’;
float r=1.2E8;
Эти величины следующим образом разместились в памяти компьютера:
Память | FFC0 | FFC1 | FFC2 | FFC3 | FFC4 | FFC5 | FFC6 |
Переменные | a | c | r | ||||
Значения | ‘G’ | 1.2*108 |
Операция & – адрес. Применение этой операции к имени переменной дает в результате ее адрес в памяти. Для переменных из данного выше примера: &a равно FFC0, &c — FFC2, &r — FFC3.
Описание указателей. Для хранения адресов используются переменные типа «указатель». Формат описания таких переменных следующий:
<тип> *<имя_переменной>
Примеры описания указателей:
int *pti; char*ptc; float *ptf;
После такого описания переменная pti — указатель на величину целого типа; переменная ptc — указатель на величину типа char; переменная ptf — на величину типа float.
Указателям могут присваиваться значения адресов объектов только того типа, с которым они описаны. В нашем примере допустимы операторы.
pti=&a; ptc=&c; ptf=&r;
В результате указатели примут следующие значения: pti — FFC0, ptc — FFC2, ptf — FFC3.
Как и для других типов данных, значения указателей могут инициализироваться при описании. Например:
int a=5; int *pti=&a;
char c=’G’; char *ptc=&c;
float r=1.2E8; float *ptf=&r;
В заголовочном файле stdio.h определена константа – нулевой указатель с именем NULL. Ее значение можно присваивать указателю. Например:
ptf=NULL;
Нулевой указатель обозначает отсутствие конкретного адреса ссылки.
Использованный в описаниях указателей символ “*” (звездочка) в данном контексте является знаком операции разадресации. С ее помощью можно сослаться через указатель на значение соответствующей переменной. Например, два следующих оператора:
x=a+2; и x=*pti+2;
тождественны друг другу. В результате выполнения оператора
cout<<*pti<<a;
на экран выведется : 55.
Операции над указателями. Записывая выражения и операторы, изменяющие значения указателей, необходимо помнить главное правило: единицей изменения значения указателя является размер соответствующего ему типа.
Продемонстрируем это правило на определенных выше указателях. Выполнение операторов
pti=pti+1; или pti++;
изменит значение указателя pi на 2, в результате чего он примет значение FFC2. В результате выполнения оператора pti--; значение указателя уменьшится на 2 и станет равным FFBE.
Аналогично для указателей других типов:
ptc++; увеличит значение указателя на 1;
ptf++; увеличит значение указателя на 4.
Использование указателей. Выше указатели использовались для передачи в функцию адресов фактических параметров для того, чтобы изменять значения соответствующих переменных (функция swap), для выделения памяти под одномерные и двумерные массивы.
Так как имя массива — указатель-константа, то для доступа к элементам массива кроме индексированных имен можно использовать разадресованные указатели по принципу:
<имя>[<индекс>] тождественно *(<имя> + <индекс>)
Например, X[5] или *(X+5) или *(5+X).
Напоминаем, что для указателей работают свои правила сложения. Поскольку Х – указатель на величину целого типа, то Х+5 увеличивает значение адреса на 10=2байта*5.
В языке Си символ “[” играет роль знака операции сложения адреса массива с индексом элемента массива.
Из сказанного должно быть понятно, почему индекс первого элемента массива всегда нуль. Его адрес должен совпадать с адресом массива:
X[0] == *(X+0).
Рассмотрим двумерные массивы. Пусть в программе присутствует описание:
int P[5][10];
Это матрица из пяти строк и десяти чисел в каждой строке. Двумерный массив расположен в памяти в последовательности по строкам. По-прежнему P является указателем-константой на массив, т.е. на элемент P[0][0]. Индексированное имя P[i] обозначает i-ю строку. Ему тождественно следующее обозначение в форме разадресованного указателя:
*(P+i*10)
Обращение к элементу массива P[2][4] можно заменить на *(P+2*10+4). В общем случае эквивалентны обозначения:
P[i][j] и *(P+i*10+j)
Здесь дважды работает операция «квадратная скобка». Последнее выражение можно записать иначе без явного указания на длину строки матрицы P: *(*(P+i)+j).Очевидно, по индукции, для ссылки на элемент трехмерного массива A[i][j][k] справедливо выражение: *(*(*(A+i)+j)+k). и т.д.
Упражнения
1. В оперативной памяти вектор int X[10] расположен, начиная с адреса B7F0. Какие значения примут выражения:
а) X+1 б) X+5 в) X–4
2. В программе объявлен массив:
int P[]={0,2,4,5,6,7,9,12};
Какие значения примут выражения:
а) P[3] б)*P в)*(P+4) г)*(P+P[2])
3. Определите значения переменных x,y и z после выполнения следующего фрагмента программы:
int *x, *y, *z; *x=3; *y=4; *z=5;*у = 7; *x *=5;(*z)++;4. Составить функцию сортировки значений трех переменных a, b, c в порядке возрастания.
Символьные строки
Язык Си не поддерживает отдельный строковый тип данных, но он позволяет определить строки двумя различными способами. В первом используется массив символов, а во втором - указатель на первый символ массива.
Определение char а[10]; указывает компилятору на необходимость резервирования места для максимум 10 символов. Константа а содержит адрес ячейки памяти, в которой помещено значение первого из десяти объектов типа char. Процедуры, связанные с занесением конкретной строки в массив а, копируют ее по одному символу в область памяти, на которую указывает константа а, до тех пор, пока не будет скопирован нулевой символ, оканчивающий строку. Когда выполняется функция типа printf("%s", а), ей передается значение а, т.е. адрес первого символа, на который указывает а. Если первый символ - нулевой, то работа функции printf() заканчивается, а если нет, то она выводит его на экран, прибавляет к адресу единицу и снова начинает проверку на нулевой символ. Такая обработка позволяет снять ограничения на длину строки (конечно, в пределах объявленной размерности): строка может иметь любую длину, но в пределах доступной памяти. Инициализировать строку при таком способе определения можно следующим образом:
char array[7] = "Строка";
char s[ ] = {'С', 'т', 'р', 'о', 'к', 'а', '\0'};
(при определении массива с одновременной инициализацией пределы изменения индекса можно не указывать).
Второй способ определения строки - это использование указателя на символ. Определение char *b; задает переменную b, которая может содержать адрес некоторого объекта. Однако в данном случае компилятор не резервирует место для хранения символов и не инициализирует переменную b конкретным значением. Когда компилятор встречает оператор вида
b ="IBM PC" он производит следующие действия. Во-первых, как и в предыдущем случае, он создает в каком-либо месте объектного модуля строку "IBM PC", за которой следует нулевой символ ('\0'). Во-вторых, он присваивает значение начального адреса этой строки (адрес символа 'I') переменной b. Функция printf("%s", b) работает так же, как и в предыдущем случае, осуществляя вывод символов до тех пор, пока не встретится заключительный нуль.
Для ввода и вывода строк символов помимо scanf( ) и printf() могут использоваться функции gets( ) и puts( ) (их прототипы находятся в файле stdio.h).
Если string - массив символов, то ввести строку с клавиатуры можно так:
gets(string); (ввод оканчивается нажатием клавиши <Enter>).
Вывести строку на экран можно следующим образом: puts(string);
Отметим также, что для работы со строками существует специальная библиотека функций, прототипы которых находятся в файле string.h.
Наиболее часто используются функции strcpy( ), strcat( ), strlen( ) и strcmp( ).
Пример 1. Ввести символьную строку. Перевернуть (обратить) эту строку. Например, если ввели строку “abcdef”, то в результате в ней должны получить “fedcba”.
//Обращение строки
#include <stdio.h>
#include <string.h>
#include <conio.h>
voidmain()
{ char C, S[10];
int i;
clrscr();
printf("Введите строку");
gets(S);
for(i=0; i<=(strlen(S)-1)/2; i++)
{ C=S[i]; S[i]=S[strlen(S)-i-1];
S[strlen(S)-i-1]=C;}
printf("\nПеревернутая строка :");
puts(S);
}
Упражнения
1. Составить программу подсчета в данной строке количества цифр.
2. Составить функцию, определяющую тождественность двух данных строк.
3. Составить программу, удаляющую в данной строке каждый символ, код которого четное число.
Структуры
Структура - это объединение одного или нескольких объектов (переменных, массивов, указателей, других структур и т.д.). Как и массив, она представляет собой совокупность данных. Отличием является то, что к ее элементам необходимо обращаться по имени и что различные элементы структуры не обязательно должны принадлежать одному типу.
Объявление структуры осуществляется с помощью ключевого слова struct, за которым идет ее тип и далее список элементов, заключенных в фигурные скобки:
struct <тип> { <тип элемента_1> < имя элемента_1>;
.........
<тип элемента_n> <имя элемента_n>;
};
Именем элемента может быть любой идентификатор. Как и выше, в одной строке можно записывать через запятую несколько идентификаторов одного типа.
Рассмотрим пример:
sruct date { int day;
int month;
int year;
};
Следом за фигурной скобкой, заканчивающей список элементов, могут записываться переменные данного типа, например: struct date {...} a, b, c;
(при этом выделяется соответствующая память). Описание без последующего списка не выделяет никакой памяти; оно просто задает форму структуры. Введенное имя типа позже можно использовать для объявления структуры, например: struct date days;
Теперь переменная days имеет тип date.
При необходимости структуры можно инициализировать, помещая вслед за описанием список начальных значений элементов.
Разрешается вкладывать структуры друг в друга, например:
struct man { char name[20], fam[20];
struct date bd;
int age;
};
Определенный выше тип data включает три элемента: day, month, year, содержащий целые значения (int). Структура man включает элементы name, fam, bd и voz. Первые два - name[20] и fam[20] - это символьные массивы из 20 элементов каждый. Переменная bd представлена составным элементом (вложенной структурой) типа data. Элемент age содержит значения целого типа int). Теперь можно определить переменные, значения которых принадлежат введенному типу: struct man man_[100];
Здесь определен массив man_, состоящий из 100 структур типа man.
Чтобы обратиться к отдельному элементу структуры, необходимо указать его имя, поставить точку и сразу же за ней записать имя нужного элемента, например:
man_[i].age = 19; man_[j].bd.day = 22; man_[j].bd.year = 1988;
При работе со структурами необходимо помнить, что тип элемента определяется соответствующей строкой описания в фигурных скобках. Например, массив man_ имеет тип man, year является целым числом и т.п. Поскольку каждый элемент структуры относится к определенному типу, его имя может появиться везде, где разрешено использование значений этого типа. Допускаются конструкции вида man_[i]=man_[j]; где man_[i] и man_[j] - объекты, соответствующие единому описанию структуры. Другими словами, разрешается присваивать одну структуру другой по их именам.
Унарная операция & позволяет взять адрес структуры. Предположим, что определена переменная day:
struct date {int d, m, у;} day;
Здесь day - это структура типа date, включающая три элемента: d, m, у. Другое определение struct date *db; устанавливает тот факт, что db - это указатель на структуру типа date.
Запишем выражение: db = &day;
В этом случае для выбора элементов d, m, у структуры необходимо использовать конструкции: (*db).d; (*db).m; (*db).y;
Действительно, db - это адрес структуры, *db - сама структура. Круглые скобки здесь необходимы, так как точка имеет более высокий, чем звездочка, приоритет. Для аналогичных целей в языке Си предусмотрена специальная операция ->. Эта операция выбирает элемент структуры и позволяет представить рассмотренные выше конструкции в более простом виде:
db -> d; db -> m; db -> у;
Пример 1. Ввести сведения об N студентах. Определить фамилии студентов, получающих самую высокую стипендию.
#include<stdio.h>
#include<conio.h>
voidmain()
{
const N=30; int i; float maxs;
struct student {char fam[15];
int kurs;
char grup[3];
float stip;
};
student stud[N]; //массив структур
clrscr();
for(i=0; i<N; i++)
{ printf("%d-й студент",i);
printf("\n”Фамилия:");scanf("%s",&stud[i].fam);
printf("Курс:"); scanf("%d",&stud[i].kurs);
printf("Группа :"); scanf("%s",&stud[i].grup);
printf("Стипендия:"); scanf("%f",&stud[i].stip);
}
maxs=0;
for(i=0; i<N; i++)
if(stud[i].stip>maxs) maxs=stud[i].stip;
printf("\nСтуденты, получающие максимальную стипендию %f руб.",maxs);
for(i=0; i<N; i++)
if(stud[i].stip==maxs) printf("\n%s",stud[i].fam);
}
Упражнения
1.Сведения о каждом химическом элементе из таблицы Д.И.Менделеева представить в виде структуры. Написать программу ввода таблицы в память компьютера.
2.Представить координаты точки в трехмерном пространстве в виде структуры, состоящей из трех вещественных полей. Написать программу ввода координат двух точек и вычисления расстояния между ними.
3. Рассматривая комплексное число как структуру, состоящую из двух вещественных полей, составить функции выполнения четырех арифметических операций с комплексными числами.
Оператор typedef
Рассмотрим описание структуры: struct data {int d, m, у;};
Здесь фактически вводится новый тип данных - data. Теперь его можно использовать для объявления конкретных экземпляров структуры, например:
struct data а, b, с;
В язык Си введено специальное средство, позволяющее назначать имена типам данных (переименовывать). Таким средством является оператор typedef. Он записывается в следующем виде: typedef <тип> <имя>;
Здесь "тип" - любой разрешенный тип данных и "имя" - любой разрешенный идентификатор.
Рассмотрим пример: typedef int INTEGER; После этого можно сделать объявление: INTEGER а, b; Оно будет выполнять то же самое, что и привычное объявление int a,b;. INTEGER можно использовать как синоним ключевого слова int.
А структурный тип data можно ввести следующим образом:
typedef struct data {int int d, m, у;} data_new;
и вместо struct data а, b, с; можно писать data_new a, b, c;
Упражнения
1.Определить новый тип для описания сведения о каждом химическом элементе из таблицы Д.И.Менделеева. Написать программу ввода таблицы в память компьютера.
2. Рассматривая комплексное число как структуру, состоящую из двух вещественных полей, определить новый тип для его описания. Составить функции выполнения четырех арифметических операций с комплексными числами.
3. Определить новый тип «простая дробь». Написать функции для ввода и вывода простых дробей, и для реализации четырех арифметических операций над дробями. Написать программу, вычисляющую выражение, записанное с помощью простых дробей.
Файлы
Файлом называют способ хранения информации на физическом устройстве. Файл - это понятие, которое применимо ко всему - от файла на диске до терминала.
В языке Си отсутствуют операторы для работы с файлами. Все необходимые действия выполняются с помощью функций, включенных в стандартную библиотеку. Они позволяют работать с различными устройствами, такими, как диски, принтер, коммуникационные каналы и т.д. Эти устройства сильно отличаются друг от друга. Однако файловая система преобразует их в единое абстрактное логическое устройство, называемое потоком.
В Си существует два типа потоков: текстовые (text) и двоичные (binary).
Текстовый поток - это последовательность символов. При передаче символов из потока на экран, часть из них не выводится (например, символ возврата каретки, перевода строки).
Двоичный поток - это последовательность байтов, которые однозначно соответствуют тому, что находится на внешнем устройстве.
Прежде чем читать или записывать информацию в файл, он должен быть открыт и тем самым связан с потоком. Это можно сделать с помощью библиотечной функции fopen( ). Она берет внешнее представление файла (например, c:\my_prog.txt) и связывает его с внутренним логическим именем, которое используется далее в программе. Логическое имя - это указатель на требуемый файл. Его необходимо определить; делается это, например, так:
FILE *fp;
Здесь FILE - имя типа, описанное в стандартном заголовочном файле stdio.h, fp - указатель на файл. Обращение к функции fopen( ) в программе осуществляется выражением:
fp = fopen(<спецификация файла>, "<способ использования файла>");
Спецификация файла (т.е. имя файла и путь к нему) может, например, иметь вид: "c:\\my_prog.txt" - для файла my_prog.txt на диске с:.
Способ использования файла задается символами. Укажем некоторые из них:
r - открыть существующий файл для чтения;
w - создать новый файл для записи (если файл с указанным именем существует, то он будет переписан);
а - дополнить файл (открыть существующий файл для записи информации, начиная с конца файла, или создать файл, если он не существует);
r+ - открыть существующий файл для чтения и записи;
w+ - создать новый файл для чтения и записи;
a+ - дополнить или создать файл с возможностью чтения и записи;
rb - открыть двоичный файл для чтения;
wb - создать двоичный файл для записи;
аb - дополнить двоичный файл;
Если в результате обращения к функции fopen( ) возникает ошибка, то она возвращает указатель на константу NULL.
Рекомендуется использовать следующий способ открытия файла:
FILE *fp;
if (fp=fopen(“test.dat”,”r”)==NULL
{puts(“Не могу открыть файл\n”);
return;
}
После окончания работы с файлом он должен быть закрыт. Это делается с помощью библиотечной функции fclose( ). Она имеет следующий прототип:
int fclose(FILE *fp);
При успешном завершении операции функция fclose( ) возвращает значение нуль. Любое другое значение свидетельствует об ошибке.
Рассмотрим другие библиотечные функции, используемые для работы с файлами (все они описаны в файле stdio.h):
1. Функция putc( ) записывает символ в файл и имеет следующий прототип: int putc(int с, FILE *fp);
Здесь fp - указатель на файл, возвращенный функцией fopen( ), с - символ для записи (переменная с имеет тип int, но используется только младший байт). При успешном завершении putc( ) возвращает записанный символ, в противном случае возвращается константа EOF. Она определена в файле stdio.h и имеет значение -1.
2. Функция getc( ) читает символ из файла и имеет следующий прототип:
int getc(FILE *fp);
Здесь fp - указатель на файл, возвращенный функцией fopen( ). Эта функция возвращает прочитанный символ. Соответствующее значение имеет тип int, но старший байт равен нулю. Если достигнут конец файла, то getc( ) возвращает значение ЕОF.
3. Функция fputs( ) записывает строку символов в файл. Она отличается от функции puts( ) только тем, что в качестве второго параметра должен быть записан указатель на переменную файлового типа.
Например: fputs("Ехаmple", fp);
При возникновении ошибки возвращается значение EOF.
4. Функция fgets( ) читает строку символов из файла. Она отличается от функции gets( ) тем, что в качестве второго параметра должно быть указано максимальное число вводимых символов плюс единица, а в качестве третьего - указатель на переменную файлового типа. Строка считывается целиком, если ее длина не превышает указанного числа символов, в противном случае функция возвращает только заданное число символов.
Рассмотрим пример: fgets(string, n, fp);
Функция возвращает указатель на строку string при успешном завершении и константу NULL в случае ошибки либо достижения конца файла.
5. Функция fprintf( ) выполняет те же действия, что и функция printf( ), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа.
Например: fprintf(fp, "%х",а);
6. Функция fscanf( ) выполняет те же действия, что и функция scanf(), но работает с файлом. Ее отличием является то, что в качестве первого параметра задается указатель на переменную файлового типа.
Например: fscanf(fp, "%х", &a);
При достижении конца файла возвращается значение EOF.
7. Функция fseek( ) позволяет выполнять чтение и запись с произвольным доступом и имеет следующий прототип:
int fseek(FILE *fp, long count, int access);
Здесь fp - указатель на файл, возвращенный функцией fopen( ), count - номер байта относительно заданной начальной позиции, начиная с которого будет выполняться операция, access - способ задания начальной позиции.
Переменная access может принимать следующие значения:
SEEK_SET (имеет значение 0) - начало файла;
SEEK_CUR (имеет значение 1) - начальная позиция считается текущей;
SEEK_END (имеет значение 2) - конц файла.
При успешном завершении возвращается нуль, при ошибке - ненулевое значение.
8. Функция fread( ) предназначена для чтения блоков данных из потока. Имеет прототип:
unsigned fread(void *ptr, unsigned size, unsigned n, FILE *fp);
Она читает n элементов данных, длиной size байт каждый, из заданного входного потока fp в блок, на который указывает указатель ptr. Общее число прочитанных байтов равно произведению n*size. При успешном завершении функция fread( ) возвращает число прочитанных элементов данных, при ошибке - 0.
9. Функция fwrite( ) предназначена для записи в файл блоков данных. Имеет прототип:
unsigned fwrite(void *ptr, unsigned size, unsigned n, FILE *fp);
Она добавляет n элементов данных, длиной size байт каждый, в заданный выходной файл fp. Данные записываются с позиции, на которую указывает указатель ptr. При успешном завершении операции функция
fwrite( ) возвращает число записанных элементов данных, при ошибке - неверное число элементов данных.
В языке Си имеются пять стандартных файлов со следующими логическими именами:
stdin - для ввода данных из стандартного входного потока (по умолчанию - c клавиатуры);
stdout - для вывода данных в стандартный выходной поток (по умолчанию - на экран дисплея);
stderr - файл для вывода сообщений об ошибках (всегда связан с экраном дисплея);
stdprn - для вывода данных на принтер;
stdaus - для ввода и вывода данных в коммуникационный канал.
В языке Си имеется также система низкоуровневого ввода/вывода (без буферизации и форматирования данных), соответствующая стандарту системы UNIX. Прототипы составляющих ее функций находятся в файле io.h. К этим функциям относятся:
open( ) - открыть файл;
close( ) - закрыть файл;
read( ) - читать данные;
write( ) - записать данные;
lseek( ) - поиск определенного байта в файле;
unlink( ) - уничтожить файл.
Пример 1. Составим программу, по которой в файл запишется последовательность целых чисел, вводимых с клавиатуры, а затем эта последовательность будет прочитана и выведена на экран. Признаком конца ввода пусть будет число 9999.
//Запись и чтение целых чисел
#include<stdio.h>
#include<conio.h>
void main()
{ FILE *fp;
int x;
clrscr();
// Открытие потока для записи
if((fp=fopen("test.dat","w"))==NULL)
{puts("Ошибка открытия файла для записи!\n");
return ;
}
puts("Вводите числа. Признак конца - 9999");
scanf("%d",&x);
while(x!=9999)
{ fprintf(fp,”%d”,x); scanf("%d",&x);
}
fclose(fp);//Закрытие потока в режиме записи
//Открытие потока для чтения
if((fp=fopen("test.dat","r"))==NULL)
{puts("Ошибка открытия файла для чтения!\n");
return ;
}
while((fscanf(fp,”%d”,&x)))!=EOF) printf("\n%d",x);
fclose(fp);
}
Пример 2. Следующая программа организует запись блоком в файл строки (символьного массива), а также чтение и вывод на экран записанной информации.
#include <string.h>
#include <stdio.h>
void main()
{ FILE *stream;
char msg[] = "this is a test";
char buf[20];
if((stream = fopen("DUMMY.FIL", "w+"))== NULL)
{ puts("Ошибка открытия файла\n");
return;
}
//Запись строки в файл
fwrite(msg, strlen(msg)+1, 1, stream);
//Установка указателя на начало файла
fseek(stream, 0, SEEK_SET);
//Чтение строки из файла
fread(buf, strlen(msg)+1, 1, stream);
printf("%s\n", buf);
fclose(stream);
}
Упражнения
1. С помощью встроенного редактора создать текстовый файл, в который записать произвольные целые числа. Написать программу, которая анализируя данные из созданного файла, записывает отдельно в новые текстовые файлы четные и нечетные числа.
2. Сведения о деталях, хранящихся на складе, содержат следующие атрибуты: название, количество, стоимость одной детали. Составить программы, решающие следующие задачи:
а) заполнить файл с информацией о деталях на складе;
б) вычислить общую стоимость деталей;
в) выяснить, какие детали имеются в наибольшем количестве, какие — в наименьшем;
г) вывести информацию о наличии на складе деталей данного типа и их количестве;
д) внести изменения в файл после выдачи со склада определенного количества данного вида деталей. Если какой-то тип деталей полностью выбран со склада, то уничтожить запись о ней в файле.