Компания ALT Linux
Опубликован: 07.03.2015 | Доступ: свободный | Студентов: 2136 / 487 | Длительность: 24:14:00
Лекция 5:

Массивы

5.6 Совместное использование динамических массивов, указателей, функций в сложных задачах обработки массивов

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

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

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

Назовём функцию udal. Её входными параметрами будут:

  • массив (x),
  • его размер (n),
  • номер удаляемого элемента (k).

Функция возвращает:

  • модифицированный массив (x),
  • размер массива после удаления (n).

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

Заголовок (прототип) функции udal может быть таким:

int udal( float *x, int k, int n )

Здесь x — массив, k — номер удаляемого элемента, n — размер массива.

Весь текст функции можно записать так:

int udal ( float *x, int k, int n )
{
	int i;
	if ( k>n-1) return n;
	else
	{
		for ( i=k; i<n-1; i++)
			x [ i ]=x [ i + 1 ];
		n--;
		return n;
	}
}

Ниже приведён весь текст программы удаления положительных элементов из массива x c использованием функции udal и комментарии к нему.

#include <iostream>
#include <math.h>
using namespace std;
int udal ( float *x, int k, int n )
{
	int i;
		//Если номер удаляемого элемента больше номера последнего элемента,
		//то удалять нечего, в этом случае возвращается неизменённый размер массива
		if ( k>n-1) return n;
		else
		{
			for ( i=k; i<n-1; i++) //Удаляем элемент с номером k.
				x [ i ]=x [ i + 1 ];
			n--;
			return n; //Возвращаем изменённый размер массива.
	}
	}
	int main ( int argc, char **argv )
	{
	int i, n;
	cout<<" n = "; cin>>n;
	float x [ n ]; //Выделяем память для динамического массива x.
	cout<<"Введите элементы массива X \n "; //Ввод элементов массива.
	for ( i =0; i<n; i++)
		cin>>x [ i ];
	for ( i =0; i<n; )
		if ( x [ i ] >0)
	//Если текущий элемент положителен, то для удаления элемента с индексом i вызываем
	//функцию udal, которая изменяет элементы, хранящиеся по адресу x,
			n=udal ( x, i, n ); //и возвращает размер массива.
		else i ++; //иначе (x[i]<=0) — переходим к следующему элементу массива.
	cout<<"Преобразованный массив X \n "; //Вывод элементов массива после удаления.
	for ( i =0; i<n; i++)
		cout<<x [ i ]<<" \t ";
	cout<<endl;
	return 0;
}

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

void udal ( float *x, int k, int *n )
{
	int i;
	for ( i=k; i <*n-1; i++)
		x [ i ]=x [ i + 1 ];
	if ( k<*n ) --*n;
}

В этом случае изменится и обращение к udal в функции main.

Ниже приведён модифицированный текст программы удаления положительных элементов из массива x c использованием функции udal(float *x, int k, int *n) и комментарии к нему.

#include <iostream>
#include <math.h>
using namespace std;
void udal ( float *x, int k, int*n )
{
	int i;
	//Если номер удаляемого элемента больше номера последнего элемента, то удалять нечего,
	//в этом случае возвращается неизменённый размер массива. Удаляем элемент с номером k.
	for ( i=k; i <-n-1; i++)
		x [ i ]=x [ i + 1 ];
	//Уменьшаем на 1 значение, хранящееся по адресу n.
	//Обратите внимание, что надо писать именно --*n, *n-- — НЕПРАВИЛЬНО!!!!!!!!!!!!!!!
	if ( k<*n ) --*n;
}
int main ( int argc, char **argv )
{
	int i, n;
	cout<<" n = "; cin>>n;
	float x [ n ]; //Выделяем память для динамического массива x.
	cout<<"Введите элементы массива X \n "; //Ввод элементов массива.
	for ( i =0; i<n; i++)
		cin>>x [ i ];
	for ( i =0; i<n; )
	if ( x [ i ] >0) //Если текущий элемент положителен, то удаление элемента с индексом i,
		//Вызываем функцию udal, которая изменяет элементы, хранящиеся по адресу x,
		//и изменяет значение переменной n.
		udal ( x, i,&n );
		else i ++; //иначе (x[i]<=0) — переходим к следующему элементу массива.
	cout<<"Преобразованный массив X \n "; //Вывод элементов массива после удаления.
	for ( i =0; i<n; i++)
		cout<<x [ i ]<<" \t ";
	cout<<endl;
	return 0;
}

Авторы рекомендуют разобраться с этими примерами для понимания механизма передачи параметров по адресу.

Задача 5.14. Из массива целых чисел удалить все простые числа, значение которых меньше среднего арифметического элементов массива. Полученный массив упорядочить по возрастанию.

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

  • вычисление среднего арифметического элементов массива;
  • проверка, является ли число простым;
  • удаление элемента из массива;
  • упорядочивание массива.

Прототипы функций, которые предназначены для решения подзадач, могут выглядеть так:

  • float sr_arifm(int *x, int n) — вычисляет среднее арифметическое массива x из n элементов;
  • bool prostoe(int n) — проверяет, является ли целое число n простым, результат — логическое значение true, если число простое, и false в противном случае;
  • void udal(int *x, int m, int *n) — удаляет элемент с номером m в массиве x из n элементов (рис. 6.4);
  • void upor(int *x, int N, bool pr=true) — сортирует массив x из n элементов по возрастанию или по убыванию, направление сортировки зависит от значения параметра pr, если pr=true, то выполняется сортировка по возрастанию, если pr=false, то по убыванию.

Текст программы с комментариями:

#include <iostream>
using namespace std;
float sr_arifm ( int *x, int n ) //Функция вычисления среднего значения.
{
	int i; float s =0;
	for ( i =0; i<n; s+=x [ i ], i++);
	if ( n>0) return ( s /n );
	else return 0;
}
bool prostoe ( int n ) //Функция для проверки, является ли число n простым.
{
	bool pr; int i;
	for ( pr=true, i =2; i<=n / 2; i++)
	if ( n%i ==0) { pr=false; break; }
	return ( pr );
}
void ud a l ( int *x, int m, int *n ) //Функция удаления элемента из массива.
{
	int i;
	for ( i=m; i <*n-1;*( x+ i ) =*(x+ i +1), i++);
	--*n;
	realloc ( ( int *) x, *n*sizeof ( int ) );
}
void upor ( int *x, int n, bool pr=true ) //Функция сортировки массива.
{
	int i, j, b;
	if ( pr )
	{
	for ( j =1; j<=n-1; j++)
	for ( i =0; i<=n-1-j; i++)
	if ( *( x+ i ) >*(x+ i +1) )
	{
		b=*(x+ i );
		*( x+ i ) =*(x+ i +1);
		*( x+ i +1)=b;
	}
	}
	else
		for ( j =1; j<=n-1; j++)
	for ( i =0; i<=n-1-j; i++)
		if ( * ( x+ i ) <*(x+ i +1) )
		{
			b=*(x+ i );
			*( x+ i ) =*(x+ i +1);
			*( x+ i +1)=b;
		}
}
int main ( )
{
	int *a, n, i; float sr;
	cout<<" n = "; cin>>n; //Ввод размерности массива.
	a=( int *) calloc ( n, sizeof ( int ) ); //Выделение памяти.
	cout << "Введите массив A \n ";
	for ( i =0; i<n; i++) cin >>*(a+ i ); //Ввод массива.
	sr=sr_arifm ( a, n ); //Вычисление среднего арифметического.
	cout<<" sr = "<<s r<<" \n "; //Вывод среднего арифметического.
	for ( i =0; i<n; )
	{
		if ( prostoe ( * ( a+ i ) )&& *( a+ i )<sr ) //Если число простое и меньше среднего,
			udal ( a, i,&n ); //удалить его из массива,
		else i ++; //иначе, перейти к следующему элементу.
	}
	cout << "Массив A \n "; //Вывод модифицированного массива.
	for ( i =0; i<n; i++) cout <<*(a+ i )<<" \t ";
	cout<<" \n ";
	upor ( a, n ); //Сортировка массива.
	cout<<"Упорядоченный массив A \n "; //Вывод упорядоченного массива.
	for ( i =0; i<n; i++) cout <<*(a+ i )<<" \t ";
	cout<<" \n ";
	free ( a ); //Освобождение памяти.
	return 0;
}

Задача 5.15. Все положительные элементы целочисленного массива G переписать в массив W. В массиве W упорядочить по убыванию элементы, которые расположены между наибольшим и наименьшим числами-палиндромами.

Для создания этой программы напишем следующие функции:

  • int form (int *a, int n, int *b) — из массива целых чисел a формирует массив положительных чисел b, n — количество чисел в массиве a, функция возвращает число элементов в массиве b.
  • bool palindrom (int n) — проверяет, является ли число n палиндромом.
  • sort (int *x, int n, int k, int p) — сортирует по возрастанию элементы массива x[n], расположенные между k-м и p-м элементами массива.

Рекомендуем читателю самостоятельно разобрать текст программы, реализующей решение задачи 5.15.

#include <iostream>
#include <stdlib .h>
#include <math.h>
using namespace std;
int kvo_razryad ( int M)
{
	long int k;
	for ( k=1;M>9;M/=10,k++);
	return k;
}
bool palindrom ( int n )
{
	int k=kvo_razryad ( n ), s, p=n;
	for ( s =0;p != 0; p/=10,k--)
		s+=(p%10)*pow ( 10, k-1);
	if ( s==n ) return true; else return false;
}
int form ( int *a, int n, int *b )
{
	int i, k;
	for ( i=k=0; i<n; i++)
		if ( a [ i ] >0)
			b [ k++]=a [ i ];
	return k;
}
void sort ( int *x, int n, int k, int p )
{
	int i, nom, j;
	int b;
	for ( i=k+1; i<p; )
	{
		nom= i;
		for ( j= i +1; j<p; j++)
			if ( x [ j ]<x [ nom ] ) nom=j;
		b=x [ p -1 ]; x [ p-1]=x [ nom ]; x [ nom]=b;
		p--;
	}
}
int main ( int argc, char **argv )
{
	int *G, *W;
	int nmax, nmin, kp, i,N, k;
	cout<<" N = ";
	cin>>N;
	G=( int *) calloc (N, sizeof ( int ) );
	W=( int *) calloc (N, sizeof ( int ) );
	cout<<"Ввод массива G \n ";
	for ( i =0; i<N; i++)
		cin>>G[ i ];
	k=form (G,N,W);
	cout<<"Вывод массива W \n ";
	for ( i =0; i<k; i++)
	cout<<W[ i ]<<" ";
	cout<<endl;
	for ( kp= i =0; i<k; i++)
		if ( palindrom (W[ i ] ) )
		{
			kp++;
			if ( kp==1) {nmax= i; nmin= i; }
			else
			{
				if (W[ i ]<W[ nmin ] ) nmin= i;
				if (W[ i ]>W[ nmax ] ) nmax= i;
			}
		}
	if (nmax<nmin )
		sort (W, k, nmax, nmin );
	else
		sort (W, k, nmin, nmax );
	cout<<"Вывод преобразованного массива W \n ";
	for ( i =0; i<k; i++)
		cout<<W[ i ]<<" ";
	cout<<endl;
	return 0;
}

Результаты работы программы представлены ниже.

N=17
Ввод массива G
-5 -6 191 121 12 -13 14 15 -5 100 666 -666 15251 16261 16262 991 -724
Вывод массива W
191 121 12 14 15 100 666 15251 16261 16262 991
Вывод преобразованного массива W
191 121 15251 666 100 15 14 12 16261 16262 991
Сергей Радыгин
Сергей Радыгин

Символы кириллицы выводит некорректно. Как сделать чтобы выводился читабельный текст на русском языке?

Тип приложения - не Qt,

Qt Creator 4.5.0 основан на Qt 5.10.0. Win7.

 

Юрий Герко
Юрий Герко

Кому удалось собрать пример из раздела 13.2 Компоновка (Layouts)? Если создавать проект по изложенному алгоритму, автоматически не создается  файл mainwindow.cpp. Если создавать этот файл вручную и добавлять в проект, сборка не получается - компилятор сообщает об отсутствии класса MainWindow. Как правильно выполнить пример?