Примеры функций для работы с двумерным массивом
Примеры функций рассмотрим в двух частях: в первой части будем использовать «традиционную» форму обращения к элементу двумерного массива (используя квадратные скобки), а во второй части – «комбинированную» (обращение через адреса и традиционно).
Часть первая («традиционная» форма использования обращения к элементу двумерного массива).
Двумерный массив декларируем в виде
int x[R][C];
где константами R и C обозначим количество строк и столбцов массива, соответственно.
// 1. Заполнение двумерного массива
// m – адрес начала двумерного массива,
// Nr - количество строк в двумерном массиве
void Filling_2 ( int m[][C], int Nr, int range1, int range2)
{ int i;
for ( i = 0; i<Nr; i++ ) //Для каждой строки массива…
{ //… заполняем одномерный массив
Filling(m[i], C, range1, range2);
}
}
Обратите внимание на то, что при передаче в качестве параметра массива m количество строк не указано. Но при этом обязательным будет указание размера одно строки ([C]). Другая особенность данного примера состоит в том, что при вызове на исполнение функции заполнения одномерного массива, в нее в качестве параметра передается одна строка двумерного массива (m[i]).
// 2. Распечатка двумерного массива
// m - адрес начала двумерного массива,
//Nr - окличество строк
void Print_2 ( int m[][C], int Nr)
{ int i;
for ( i = 0; i<Nr; i++ )
{
Print (m[i],С);
printf("\n");
}
}
Распечатка двумерного массива дополнительных комментариев не требует, так как все особенности описаны в комментариях к предыдущей функции.
// 3. Сохранение массива в файл
// m - адрес начала двумерного массива,
// Nr - окличество строк
// Name - имя файла
void Save( int m[][C], int Nr, char Name[])
{ FILE *out; int i;
// Открыть файл для записи.
out = fopen(Name,"wb");
// Проверить: файл открыт?
if (out != NULL)
{ // Для каждой строки двуменого массива...
for (i=0; i<Nr; i++)
// ... записать строку массива в файл.
fwrite ( m[i], 1, C*sizeof(int), out );
// Закрыть файл.
fclose(out);
}
else puts("\nОшибка открытия файла для записи");
}
Для сохранения массива в файл потребуется файловая переменная out, являющаяся указателем на библиотечную структуру FILE.
Файл открывается для записи функцией fopen, в которую в качестве параметров передаются две строки: первая – имя файла, вторая – режим доступа к файлу. В строке режима доступа к файлу две буквы: w – означает запись (write), b – тип файла (бинарный, то есть двоичный – binary). Функция fopen возвращает указатель на структуру типа FILE, если открытие файла прошло успешно, или NULL, если произошла ошибка.
Запись в файл проводится функцией fwrite. Эта функция принимает четыре параметра: первый – нетипированный указатель на блок данных (в качестве фактического параметра может быть указатель любого типа, в данном примере – это адрес начала одномерного массива), второй параметр – количество блоков данных для записи, третий – размер одного блока данных (в примере вычисляется по размеру одномерного массива), четвертый – файловая переменная.
Обязательным действием по завершении записи в файл всех блоков данных служит закрытие файла функцией fclose, которая в качестве параметра принимает файловую переменную.
// 4. Загрузка массива из файла
// m - адрес начала двумерного массива,
// Nr - окличество строк
// Name - имя файла
void Load( int m[][C], int Nr, char Name[])
{ FILE *in; int i;
// Открыть файл для записи.
in = fopen(Name,"rb");
// Проверить: файл открыт?
if (in!=NULL)
{ // Для каждой строки двуменого массива...
for (i=0; i<Nr; i++)
// ... записать строку массива в файл.
fread(m[i], 1, C*sizeof(int), in);
// Закрыть файл.
fclose(in);
}
else puts("\nОшибка открытия файла для чтения");
}
При чтении данных из файла меняется режим доступа к файлу (rb) и используется функция чтения из файла fread, параметры которой полностью совпадают с параметрами функции fwrite.
Для проверки работоспособности приведенных функций была использована приведенная ниже программа. Не забудьте после главной функции добавить представленные ранее функции Filling_2, Print_2, Save и Load.
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include "5.h"
#define R 5
#define C 7
// Предварительное описание функций
void Filling_2(int m[][C], int Nr, int range1, int range2);
void Print_2(int m[][C], int Nr);
void Save( int m[][C], int Nr, char Name[]);
void Load( int m[][C], int Nr, char Name[]);
// Главная функция
int main()
{
int m[R][C], x[R][C];
Randomize();
Title();
puts("\nЗаполнение двумерного массива");
Filling_2(m,R,-30,70);
puts("\nРаспечатка двумерного массива\n");
Print_2 (m,R);
puts("\nСохранить содержимое массива в файл\n");
Save(m, R, "7.dat");
puts("\nЗагрузить данные из файла в новый массив\n");
Load(x, R, "7.dat");
puts("\nРаспечатка нового двумерного массива\n");
Print_2 (x,R);
return 0;
}
Обратите внимание на то, что функции Filling_2, Print_2, Save и Load могут быть использованы для двумерных массивов с произвольным количеством строк, но каждая строка должна быть строго определенной длины. В этом состоит основной недостаток «традиционной» формы обращения к двумерному массиву.
Часть вторая (обращение к двумерному массиву через указатели).
Двумерный массив декларируем как указатель на указатель
int **x;
Так массив задан через указатель, то необходимо его инициализировать, то есть зарезервировать место под массив в динамической области оперативной памяти.
// 1. Инициализация массива
// Nr - количество строк, Nc - количество столбцов
int ** Init(int Nr, int Nc)
{ int i; int **x;
// Задать массив указателей на строки
x = (int**)calloc ( Nr, sizeof(int*));
// Задать указатели на элементы в строке
for ( i = 0; i<Nr; i++)
x[i]=(int *)calloc(Nc,sizeof(int));
return x;
}
Инициализация проводится функцией calloc, в которой первым параметром указывается количество элементов, а вторым – размер одного элемента. Как видно из текста программы, сначала задается массив указателей на строки, а потом в каждый из указателей помещаются адреса элементов массива.
// 2. Заполнение массива
// Nr - количество строк, Nc - количество столбцов
void Fill(int **x, int Nr, int Nc)
{ int i,j; static int k=1;
for ( i=0; i<Nr; i++)
{
for ( j=0; j<Nc; j++)
x[i][j]=k++;
}
}
Заполнение массива проводим целыми числами, начиная с единицы. Обратите внимание на то, что для переменной k выбран статический класс памяти (инициализация значением 1 будет сделана только при первом вызове функции на исполнение), а также на то, что к элементам массива, на который указывает x, можно обращаться «традиционным» способом.
Ниже приведены функции для распечатки двумерного массива и работы с бинарным файлом. Распечатка проводится циклом в цикле, а сохранение и загрузка данных при работе с файлом – построчно.
// 3. Распечатка массива
// Nr - количество строк, Nc - количество столбцов
void Print (int **x, int Nr, int Nc)
{ int i,j;
printf("\n");
for ( i=0; i<Nr; i++)
{
for ( j=0; j<Nc; j++)
printf("%4d",x[i][j]);
printf("\n");
printf("\n");
}
printf("\n");
}
// 4. Сохранение массива в файл
// Nr - количество строк, Nc - количество столбцов
// Name - имя файла
void Save( int **x, int Nr, int Nc, char Name[])
{ FILE *out; int i;
out = fopen(Name,"wb");
if (out != NULL)
{
for ( i=0; i<Nr; i++)
fwrite (x[i], 1, Nc*sizeof(int), out);
fclose(out);
}
else puts("\nОшибка открытия файла для записи");
}
// 5. Загрузка массива из файла
// Nr - количество строк, Nc - количество столбцов
// Name - имя файла
void Load( int **x, int Nr, int Nc, char Name[])
{ FILE *in; int i;
in = fopen (Name,"rb");
if (in != NULL)
{
for ( i=0; i<Nr; i++)
fread ( x[i], 1, Nc*sizeof(int), in);
fclose(in);
}
else puts("\nОшибка открытия файла для чтения");
}
Проверка работоспособности функций проводилась приведенной ниже программой. Можете заметить, что функции применимы как для массива размером 4х5 (массивы x и x1), так и для массива размером 2х3 (массивы y и y1).
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
int ** Init(int Nr, int Nc);
void Fill(int **x, int Nr, int Nc);
void Print (int **x, int Nr, int Nc);
void Save( int **x, int Nr, int Nc, char Name[]);
void Load( int **x, int Nr, int Nc, char Name[]);
inline void Rus() // Русификация вывода в консольное окно
{ setlocale( LC_CTYPE, ".1251" ); }
int main()
{ int **x,**y,**x1,**y1;
Rus();
puts("\nИнициализация массива x\n");
x = Init (4, 5);
puts("\nЗаполнение массива x\n");
Fill(x,4,5);
puts("\nРаспечатка массива x\n");
Print(x,4,5);
puts("\nИнициализация массива y\n");
y = Init (2, 3);
puts("\nЗаполнение массива y\n");
Fill(y,2,3);
puts("\nРаспечатка массива y\n");
Print(y,2,3);
puts("Сохранение массива x в файл");
Save(x,4,5,"x.dat");
puts("\nИнициализация массива x1\n");
x1 = Init (4, 5);
puts("Загрузка массива x1 из файла");
Load(x1,4,5,"x.dat");
puts("\nРаспечатка массива x1\n");
Print(x1,4,5);
puts("Сохранение массива y в файл");
Save(y,2,3,"y.dat");
puts("\nИнициализация массива y1\n");
y1 = Init (2, 3);
puts("Загрузка массива y1 из файла");
Load(y1,2,3,"y.dat");
puts("\nРаспечатка массива y1\n");
Print(y1,2,3);
return 0;
}
Естественно, что при использовании приведенных функций нельзя путать размерность массивов. То есть, если массив x был инициализирован размером 4х5, то в нем нельзя использовать количество строк более 4 и столбцов более 5.
Чтобы было меньше ошибок можно ввести свой тип данных, содержащий в одной структуре не только указатель на указатель, например, такой
typedef struct {int **x; int Nr; int Nc;} TArray;
В этой записи слово typedef зарезервировано для обозначения того, что после его будет описание тип. Введенный тип данных представляет собой структуру (обозначено зарезервированным словом struct), а в фигурных скобках указано, что структура содержит три поля x – указатель на указатель на данные типа int, Nr и Nc – поля целого типа, для хранения информации о количестве строк и столбцов двумерного массива соответственно. TArray – имя типа данных.
Теперь, если задать переменную типа TArray, например,
TArray m;
то можно обратиться отдельно к каждому из полей (x,Nr,Nc) структуры типа TArray, записывая после имени переменной (в данном примере m) знак точка, а потом идентификатор поля (x,Nr,Nc), напрмер,
m.Nr = 4;
m.Nc = 5;
m.x = (int**) calloc ( m.Nr, sizeof(int*) );
Теперь функции для работы с двумерным массивом можно переписать к следующему виду:
// 1. Инициализация массива
// r - количество строк, c - количество столбцов
void Init ( TArray *m, int r, int c)
{ int i;
// Определить размеры массива
(*m).Nr = r; (*m).Nc = c;
// Задать массив указателей на строки
(*m).x = (int**)calloc ( r, sizeof(int*));
// Задать указатели на элементы в строке
for ( i = 0; i<r; i++)
(*m).x[i]=(int *)calloc ( c, sizeof(int));
}
Обратите внимание на то, что параметр m типа (TArray *) – это параметр –переменная (передается в функцию адресом). Поэтому при обращении к полям структуры используется разыменование, например, (*m).Nr = r. Аналогично используется параметр-переменная при заполнении массива и загрузке массива из файла. При распечатке и сохранении массива в файл можно использовать параметр-значение.
// 2. Заполнение массива
// Nr - количество строк, Nc - количество столбцов
void Fill ( TArray *m)
{ int i, j; static int k=1;
for ( i=0; i<(*m).Nr; i++)
{
for ( j=0; j<(*m).Nc; j++)
(*m).x[i][j] = k++;
}
}
// 3. Распечатка массива
// Nr - количество строк, Nc - количество столбцов
void Print (TArray m)
{ int i, j;
printf("\n");
for ( i=0; i<m.Nr; i++)
{
for ( j=0; j<m.Nc; j++)
printf("%4d",m.x[i][j]);
printf("\n");
printf("\n");
}
printf("\n");
}
// 4. Сохранение массива в файл
// Nr - количество строк, Nc - количество столбцов
// Name - имя файла
void Save( TArray m, char Name[])
{ FILE *out; int i;
out = fopen(Name,"wb");
if (out != NULL)
{
for ( i=0; i<m.Nr; i++)
fwrite (m.x[i], 1, m.Nc*sizeof(int), out);
fclose(out);
}
else puts("\nОшибка открытия файла для записи");
}
// 5. Загрузка массива из файла
// Nr - количество строк, Nc - количество столбцов
// Name - имя файла
void Load( TArray *m, char Name[])
{ FILE *in; int i;
in = fopen (Name,"rb");
if (in != NULL)
{
for ( i=0; i<(*m).Nr; i++)
fread ( (*m).x[i], 1, (*m).Nc*sizeof(int), in);
fclose(in);
}
else puts("\nОшибка открытия файла для чтения");
}
В главной части программы размер двумерного массива задается только при инициализации. Не забудьте также в функции Init, Fill и Load передавать в качестве фактического параметра адрес размещения переменной в оперативной памяти.
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
typedef struct {int **x; int Nr; int Nc;} TArray;
void Init(TArray *m, int r, int c);
void Fill(TArray *m);
void Print (TArray m);
void Save( TArray m, char Name[]);
void Load( TArray *m, char Name[]);
inline void Rus() // Русификация вывода в консольное окно
{ setlocale( LC_CTYPE, ".1251" ); }
int main()
{ TArray x, y, x1, y1;
Rus();
puts("\nИнициализация массива x\n");
Init (&x,4, 5);
puts("\nЗаполнение массива x\n");
Fill(&x);
puts("\nРаспечатка массива x\n");
Print(x);
puts("\nИнициализация массива y\n");
Init (&y,2, 3);
puts("\nЗаполнение массива y\n");
Fill(&y);
puts("\nРаспечатка массива y\n");
Print(y);
puts("Сохранение массива x в файл");
Save(x,"x.dat");
puts("\nИнициализация массива x1\n");
Init (&x1,4, 5);
puts("Загрузка массива x1 из файла");
Load(&x1,"x.dat");
puts("\nРаспечатка массива x1\n");
Print(x1);
puts("Сохранение массива y в файл");
Save(y,"y.dat");
puts("\nИнициализация массива y1\n");
Init (&y1,2, 3);
puts("Загрузка массива y1 из файла");
Load(&y1,"y.dat");
puts("\nРаспечатка массива y1\n");
Print(y1);
return 0;
}
- Министерство образования и науки Российской федерации
- Пример выполнения задания по теме № 1
- Формулы для решения задач
- Варианты заданий
- Контрольные вопросы по теме № 1
- Тема №2. Ветвление программы (использование операторов ветвления и переключателяswitch)
- Варианты заданий
- Пример выполнения лабораторного задания № 2
- Блок-схема алгоритма вычисления составной функции
- Контрольные вопросы к лабораторной работе № 2
- Тема №3. Циклы, символьный (литерный) тип данных
- Примеры выполнения задания по теме №3.
- Блок-схема к программе
- Варианты заданий
- Контрольные вопросы к теме № 3
- Тема №4. Функции в языке Си
- Контрольные вопросы по теме №4
- Тема №5. Обработка одномерных массивов Задание:
- Краткая теоретическая справка и рекомендации по выполнению
- Примеры функций для работы с одномерными массивами
- Варианты заданий
- Контрольные вопросы по теме № 5
- Тема №6. Строки и использование библиотечных функций для их обработки
- Краткая теоретическая справка и рекомендации по выполнению
- Пример выполнения лабораторного задания № 6
- Варианты заданий
- Контрольные вопросы к теме № 6
- Тема №7. Двумерные массивы. Файловый (бинарный) ввод-вывод Задание:
- Краткая теоретическая справка
- Примеры функций для работы с двумерным массивом
- Тема №8. Односвязные списки. Файловый (текстовый) ввод-вывод Задание:
- Краткая теоретическая справка и пример решения задачи
- Оглавление