Передача матрицы в функцию

 

Один из способов передачи матрицы в функцию был рассмотрен в первом семестре. Его недостаток в том, что в прототипе функции для матрицы необходимо было обязательно указывать вторую размерность матрицы, то есть количество элементов в строках. Здесь предлагается более профессиональный способ, основанный на понятиях указатель на указатель и динамические “матрицы”.

Вспомним, что обычный одномерный, например, числовой массив в качестве параметра функции можно передать двумя способами:

void FunArr1 (int *z, int , … );

или

void FunArr1 (int z[], int,… );

но в любом из вариантов мы передаём адрес начала массива.

Аналогично матрица как параметр функции объявляется в прототипе, например, одним из следующих способов:

void FunMatr (int **X, int , int );

или

void FunMatr (int *X[], int , int );

То есть если сравнивать с одномерным массивом, то просто вместо типа int, что означает числовой целочисленный массив, записали тип int *, что означает, что в функцию передаём адрес начала массива указателей. Как правило, в качестве параметров включаются и две размерности матрицы.

Для лучшего понимания необходимо сделать следующее замечание. Как и в случае одномерного массива (FunArr), из такого прототипа ещё не следует, что мы обязательно в качестве фактического параметра должны передавать матрицу. Можно, например, передать указатель на указатель на целое число.

Для передачи матрицы в вызывающей функции создаём полностью динамическую или частично динамическую “матрицу”, а при вызове функции записываем её имя (см. дальше пример). Если продолжить аналогию с обычным одномерным числовым массивом, то в качестве первого параметра функции FunArr1 можно передать как статический массив:

int A[10]; FunArr1(A, 10,…);

так и динамический:

int *d; int n; cin>>n; d= new int[n]; FunArr1(d, n, …);

В случае с матрицей это не совсем так. Пусть объявлена статическая матрица

const Nc=5, Mc=10; int C[Nc][Mc];

Передать её в функцию нельзя, то есть вызов

FunMatr(C, Nc, Mc);

приведёт к ошибке компиляции.

Пример. (+).

Передача матрицы в функцию с помощью указателей. Кроме этого здесь дополнительно показано, как передать формат стандартной функции вывода printf в качестве параметра функции.

void MyFun1 (int **X, int , int ); // или void MyFun1 (int *X[],int , int );

void MyFun2 (int *X[],int , int ); //или void MyFun2 (int **X,int , int );

void MyPrint (int **X, int , int, int, char* );

int main()

{

/* Создание “полностью” динамической матрицы, которая объявляется как динами­ческий массив из N указателей. Обе размерности – переменные.*/

int ** A; int N=3, M=4;

A=new int *[N];

for (int i=0; i<N; i++) A[i]=new int [M];

/* В функцию передаём полностью динамическую матрицу. */

MyFun1(A,N,M);

/* Сначала выводим указанный текст, а затем матрицу A размерности N*M по формату “%5d” */

MyPrint(A,N,M,5,"\n The first test\n");

//Удаляем матрицу

for (int i=0;i<N;i++) delete[]A[i];

delete[]A;

/* Создание “частично” динамической матрицы, которая объявляется как статический массив указателей фиксированной размерности Nconst. Другими словами, количество “строк” фиксировано (Nconst), а количество элементов в строке произвольное: M2 – не константа, а переменная (см. 5.3) */

const Nconst=8; int M2=6;

int *B[Nconst];

/* Для такой полудинамической матрицы B=new int *[Nconst]; не надо. Будет ошибка!!!!!!! */

for (int i=0; i<Nconst; i++)

B[i]=new int [M2];

/* Передаём полудинамическую матрицу */

MyFun2(B,Nconst,M2);

MyPrint(B,Nconst,M2,3,"\n \nThe second test\n");

//Удаляем матрицу B

for (int i=0;i<Nconst;i++)

delete[]B[i];

getch(); return 0;

}

void MyFun1 (int **X, int n, int m)

// или void MyFun1 (int *X[], int n, int m)

{

for (int i=0; i<n; i++)

for (int j=0;j<m;j++)

X[i][j]=(i+1)*(j+1);

// Перестановка 0-й и (n-1)-й строк

int* t;

t=X[0]; X[0]=X[n-1]; X[n-1]=t;

}

void MyFun2 (int *X[], int n, int m)

// или void MyFun2 (int **X, int n, int m)

/* Определяем элементы матрицы. */

{

for (int i=0; i<n; i++)

for (int j=0;j<m;j++)

if (i%2) X[i][j]=(i+1)-(j+1);

else X[i][j]=(i+1)+(j+1);

}

void MyPrint (int **X, int n , int m, int L, char* t )

{

// Формируем формат “%Ld”, где L – переданное в функцию целое число…

char AnyFormat[10],

StrL[3];

/* … для хранения переведенного в строку c помощью встроенной функции itoa целого числа L */

strcpy(AnyFormat, "%");

strcat(AnyFormat,itoa(L,StrL,10));

strcat(AnyFormat,"d");

puts(t);

for (int i=0; i<n; i++)

{

for (int j=0;j<m;j++)

printf(AnyFormat,X[i][j]);

cout<<endl;

}

}

§5. Динамический массив строк (+).

 

По аналогии с динамической матрицей можно создать динамический массив строк. Для этого объявляем динамический массив указателей как указатель на указатель символа:

char **s;

Объявляем и определяем количество строк:

int n; cin>>n;

Аналогично резервируем память для массива указателей на строки

s=new char*[n];

В цикле резервируем память для каждой строки. Размер её определяем с помощью вспомогательной строки t, длина которой, а, значит и длина каждой строки, не более 40 символов:

char *t; t=new char[40];

for(int i=0; i<n; i++)

{ gets(t);

s[i]=new char[strlen(t)];

strcpy(s[i], t);

}

Работу с таким массивом рассмотрим на примере сортировки массива строк.

for(int j=0; j<n; j++)

for(int i=0; i<n-1; i++)

if (s[i][0]>s[i+1][0]) //Сравнение первых символов строк

//if (strcmp(s[i],s[i+1])>0) // или сравнение двух строк

{ // Перестановка двух строк

strcpy(t,s[i]); // копирование s[i] в t

strcpy(s[i],s[i+1]);

strcpy(s[i+1],t);

}

for(int i=0;i<n;i++) puts(s[i]); // Вывод строк

//Освобождение памяти особенностей не имеет.

for (int i=0; i<n;i++)

delete []s[i]; // Удаление каждой строки из памяти

delete []s; // Удаление массива указателей из памяти

delete []t; // Удаление вспомогательной строки из памяти

В качестве повторения заметим, что два варианта сравнения

if (s[i][0]>s[i+1][0])

и

if(strcmp(s[i],s[i+1])>0) …

выполняются по-разному. В первом случае сравниваются только первые символы соседних в массиве строк и независимо от результата следующие символы в сравнении не участвуют. С помощью функции strcmp выполняется сравнение так, как было описано в теме “строки” (см. §5 главы 2).

 

Упражнения, тесты.

1. Дан код:

const n=5; int * ap[n];

int v; ap[1]=&v; //1

ap[2]= v; //2

int * p; p= new int; ap[3]=*p; //3

int *q; q=new int[10]; ap[9]=q; //4

ap[4]=new int; //5

ap[4]=new int[6]; //6

unsigned m=10; ap[7]=new int[m]; //7

const k=10; int a[k]; ap[8]=a; //8

В каких строках (//1—//8) нет ошибок? Что означают правильные операторы? Объяснить ошибки.

2. Дан код:

const п=5, m=10; int M [n][m], *p, *ap[n];

int k1=0, k2=n-1;

p=M[k1]; //1

M[k1]=M[k2]; //2

M[k2]=p; //3

for (int i=0; i<n; i++) ap[i]=M[i]; //4

int i1=0, i2=m-1;

p=ap[i1]; //5

ap[i1]=ap[i2]; //6

ap[i2]=p; //7

В каких строках (//1—//7) есть ошибки? Объяснить их.

3. Как будет работать следующий фрагмент программы

void MyOut(int *u, int size)

{ cout<<endl;

for (int i=0; i<size;i++) cout<<u[i]<<" ";

}

int main()

{ const n=5;

int ar1[n]={10, -2, 33, 4},

ar2[n]={111, -222, 333, 0, 555};

int *t, *t1, *t2; t1=ar1; t2=ar2;

t=t1; t1=t2; t2=t;

MyOut(ar1,n); MyOut(ar2,n);

MyOut(t1,n); MyOut(t2,n);

return 0; getch();

}

Зачем объявили и использовали указатели t1 и t2? Почему не использовали операции new и delete для t, t1 и t2?

4. Что означает цикл for (int I=0; w[I]; I++); если 1) w – числовой одномерный массив, 2) w -- строка; 3) w – массив указателей.

5. Дана программа.

void MyFun1 (float **X, int , int );

void MyOut (float *X[], int, int);

int main()

{ int ** A; int N=6, M=4; A=new float [M];

for (int i=0; i<N; i++) A[i]=new float* [N];

MyFun1(A,N,M); MyOut(A,N,M);

delete[]A; for (int i=0;i<N;i++) delete[]A[i];

getch(); return 0; }

void MyFun1 (float *X[], int n, int m)

// В динамической “матрице” размерности n* m надо поменять местами первую по порядку и последнюю строки, не перемещая их физически в памяти.

{ for (int i=0; i<n; i++)

for (int j=0;j<m;j++)

X[i][j]=(i+1)*(j+1);

int t; t=X[0]; X[0]=X[n-1]; X[n-1]=t;

}

void MyOut (float **X, int n, int m)

// Вывод динамической “матрицы” размерности n* m

{ for (int i=0; i<n; i++)

{ cout<<endl;

for (int j=0;j<m;j++) printf("%7.2f", X[i][j]);

} }

Найти ошибки, объяснить их и исправить.

7. Почему не работает следующий фрагмент программы?

const n=5; int ar1[n]={10, -2, 33, 4},

ar2[n]={111, -222, 333, 0, 555};

int *t; t=ar1; ar1=ar2; ar2=t;

Как её изменить, чтобы “переставить” местами два массива, не перемещая их элементы “физически” в памяти ? Как это сделать для динамических массивов?

8. Дан код

const n=3; char *S[n],

*t=new char[10]; //1

for(int i=0;i<n;i++)

{ gets(t); S[i]=t; //2

}

for(int i=0;i<n;i++) printf(“%s ”, S[i]);

Что будет выведено, если последовательно введём:

Иванов

Петров

Сидоров?

Варианты ответов:

1) Иванов Петров Сидоров

2) Сидоров Петров Иванов

3) Иванов Иванов Иванов

4) Сидоров Сидоров Сидоров

5) Ошибка в //1

6) Ошибка в //2: нет new для S[i].

9. Дан заголовок функции:

void FunMatr (int **X, int , int );

Какой из следующих вызовов правильный?

/*1*/ const Nc=5, Mc=10; int C[Nc][Mc]; FunMatr(C, Nc, Mc);

/*2*/ int ** A; int N=6, M=4; A=new float [N];

for (int i=0; i<N; i++) A[i]=new float* [M];

FunMatr(C, Nc, Mc);

Варианты ответов:

1) только /*1*/ ; 2) только /*2*/ ; 3) /*1*/ и /*2*/ ; 4) нет правильного.

10. Дан заголовок функции:

void FunArr1 (int *z, int );

Какой из следующих вызовов правильный?

int A[10]; FunArr1(A, 10); //1

int *d; int n; cin>>n; d= new int[n]; FunArr1(d, n); //2

Варианты ответов:

1) только //1; 2) только //2; 3) //1 и //2; 4) нет правильного.

11. Пусть объявлены следующие переменные: int x=10, *p, **q;

Какие из следующих операторов синтаксически правильные?

1) p=x; 2) p=&x; 3) q=p; 4) q=&p; 5) q=&(&p); 6) q=&x; 7) q=&(&x);

12. Пусть объявлена переменная int **u2;

Какие из следующих последовательностей операторов синтаксически правильные?

1) u2=new *int; *u2=new int; cin>>(**u2);

2) u2=new *int; *u2=new int; cout>>(**u2);

3) u2=new *int; cin>>*u2;

4) u2=new *int; cout>>*u2;

5) u2=new *int; *u2=new int; cout>>(**u2);

13. Как можно объявить D, чтобы D =new float *[n]; компилировалось и выполнялось? Выбери правильные варианты из предложенных:

1) const n=5; float *D;

2) const n=5; float **D;

3) int n; cin>>n; float *D;

4) int n; cin>>n; float **D;

5) int n; cin>>n; float f; float &D=f;

6) int n; cin>>n; float f; float &&D=f;

14. Что надо записать, чтобы было достаточно для того, чтобы

for (int i=0; i<n1; i++) D[i]=new int [n2];

компилировался и выполнялся? Выбери правильные варианты из предложенных:

1) const n1=5, n2=4; int *D[n1];

2) int n1=5; const n2=4; int *D[n1];

3) const n1=5; int n2=4; int *D[n1];

4) int n1=5, n2=4; int *D[n1];

5) const n1=5; int n2=4; int **D;

6) const n1=5; int n2=4; int **D; D=new int [n1];

7) const n1=5; const n2=4; int **D; D=new int [n2];

15. Пусть const n=5, m=6; int A[n][m]; В каких строках есть ошибки?

1) for (int i=0; i<n; i++) delete [] A[i]; delete [] A;

2) delete [] A; for (int i=0; i<n; i++) delete [] A[i];

3) for (int i=0; i<n; i++) delete [m] A[i]; delete [n] A;

4) for (int i=0; i<n; i++) for (int j=0; j<m; j++) delete A[i][j];

5) for (int i=0; i<n; i++) delete [] A[i]; delete A;

16. Дан код:

const n=5; char t[20]; char **S; S=new *char [n];

for (int i=0; i<n; i++)

{gets(t); S[i]=new char [strlen(t)];

//оператор1

… }

Какие из следующих операторов на месте оператора “оператор1” формально синтаксически правильные, хотя выполняют не обязательно одно и тоже и могут не иметь практического значения? Выбери правильные варианты из предложенных:

1) strcpy(S[i],t); 2) strcpy(S[i],t[i]); 3) strcpy(S, t); 4) S[i]=t;

5) t=S[i]; 6) S[i]=t[i]; 7) S[i][0]=t[0];

8) for (int j=0; j<strlen(t); j++) S[i][j]=t[j];

9) for (int j=0; j<=20; j++) S[i][j]=t[j];

17. Пусть const n=5, m=6; int A[n][m]; В каких строках есть ошибки?

1) int S[n]; for(int i=0; i<n; i++) S[i]=A[i];

2) int *S[n]; for(int i=0; i<n; i++) { S[i]=new int [m]; A[i]=S[i]; }

3) int **S; S=new *int [n]; for(int i=0; i<n; i++) S[i]=A[i];

4) int *S; S= A[0]; A[0]=A[n-1]; A[n-1]=S;

5) int S; S= A[0]; A[0]=A[n-1]; A[n-1]=S;

6) int S;for(int j=0; j<m; j++) {S=A[0][j]; A[0][j]=A[n-1][j];A[n-1][j]=S; }

18. Дан код.

const n=5; int * M[n]; //1

int arr_of_size[n];

for (int i=0; i<n; i++) cin>>arr_of_size[i];

for (int i=0; i<n; i++)

M[i]= new int[arr_of_size[i]]; //2

for (int i=0; i<n; i++)

for (int j=0; j<n; j++)

M [i][j]=i*j; //3

При каких значениях введённых элементов массива arr_of_size будет правильно работать приведённый фрагмент программы?

Варианты ответов:

1) При любых целых положительных значениях.

2) При любых значениях.

3) Если введём пять значений, каждое из которых равно 5.

4) Ошибка компиляции строки //1, так как n не должна быть константой.

5) Ошибка компиляции строки //2, так как в new нельзя использовать элемент массива.

6) Ошибка компиляции строки //3, так как для M нельзя использовать два индекса, это одномерный массив.