Сделай Сам Свою Работу на 5

Операторы передачи управления





 

К операторам передачи управления относятся:

· оператор безусловного перехода (goto);

· оператор выхода из цикла или переключателя (break);

· оператор перехода к следующей итерации цикла (continue);

· оператор возврата из функции (return).

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

Оператор безусловного перехода имеет вид:

goto <метка>; .

Передача управления разрешена на любой помеченный оператор в теле функции. Однако существует одно важное ограничение: запреще­но "перескакивать" через описания, содержащие инициализацию объ­ектов.

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

· не входить внутрь блока извне;

· не входить внутрь условного оператора, то есть не передавать управление операторам, размещенным после служебных слов if или else;

· не входить извне внутрь переключателя switch;

· не передавать управление внутрь цикла.

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



Оператор break служит для принудительного выхода из цикла или переключателя. Например, в случае цикла не проверяются и не учитываются условия дальнейшего продолжения итераций. Оператор break прекращает выполнение оператора цикла или переключателя и осуществляет передачу управления (переход) к следующему за циклом или переключателем оператору. При этом в отличие от перехода с помощью goto оператор, к которому выполняется передача управле­ния, не должен быть помечен. Оператор break нельзя использовать нигде, кроме циклов и переключателей.



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

#include <iostream.h>

void main()

{

int ic;

cout << "\n Введите восьмеричную цифру:" ;

cin >> ic;

cout << "\n" << ic;

switch (ic)

{

case 0: cout << " - нуль"; break;

case 1: cout << " - один"; break;

case 2: cout << " - два"; break;

case 3: cout << " - три"; break;

case 4: cout << " - четыре"; break;

case 5: cout << " - пять"; break;

case 6: cout << " - шесть"; break;

case 7: cout << " - семь "; break;

default: cout << " – это не восьмеричная цифра!\n";

}

}

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

Циклы и переключатели могут быть многократно вложенными. Операторbreak позволяет выйти только из самого внутреннего цикла или переключателя.

Пример: в символьном массиве подсчитать ко­личество нулей (k0) и единиц (kl).

#include <iostream.h>

void main()

{

char *c;

int k0=0,k1=0;

c = new char [80];

cout << "\nЗадайте строку: ";

cin >> c;

cout << '\n';

for (int i=0;c[i]!='\0';i++)

switch (c[i])

{

case '0' : k0++; break ;

case '1' : k1++; break;

}

cout << "\nВ строке "<< k0 << " нуля, "<<

k1 << " единицы.\n";

delete [] c;

}

Операторbreak в данном примере передает управление из пере­ключателя, но не за пределы цикла. При многократном вложении циклов и переключателей оператор break не может вызвать передачу управления из самого внутреннего уровня непосредственно на самый внешний.



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

Пример: подсчитать среднее значение только положительных элементов одномерного массива.

#include <iostream.h>

void main()

{

int a[20],n,k,i;

float s;

cout << "\nЗадайте количество чисел: ";

cin >> n;

cout << '\n';

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

{

cout << i+1 <<"-е число: ";

cin >> a[i];

cout << '\n';

}

for (s=0,k=0,i=0;i<n;i++)

{ if (a[i]<=0) continue;

k++;

s+=a[i];

}

if (k>0) { s=s/k;

cout << "\nСреднее значение: "<<s ; }

else

cout << "\nВсе члены массива не положительны\n";

}

Оператор возврата из функции имеет вид:

return <выражение>;

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

#include <iostream.h>

void main()

{

float cube (float); //Прототип функции возведения в куб

float n;

cout << "\nЗадайте число: ";

cin >> n;

cout << '\n';

cout << "\nЕго куб: "<< cube (n) << '\n';

}

 

float cube (float z)

//Функция возведения числа в куб

{

return z*z*z;

}

Выражение в оператореreturn не может присутствовать в том слу­чае, если возвращаемое функцией значение имеет типvoid. Например, в следующей программе функция выводит на экран дисплея, связанный с потоком cout, значение третьей степени своего аргумента и не возвращает в точку вызова никакого значения:

#include <iostream.h>

void main()

{

void cube (float); //Прототип функции возведения в куб

float n;

cout << "\nЗадайте число: ";

cin >> n;

cout << '\n';

cube (n);

}

 

void cube (float z)

//Функция возведения числа в куб

{

cout << "\nКуб заданного числа: "<< z*z*z;

return;

}

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

 

3. Указатели и массивы в Си++

 

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

Определение указателя на некоторый объект имеет вид:

type *<имя указателя>;

где type - обозначение типа; имя указателя – идентификатор. Таким образом признаком указателя при лексическом разборе определения описания служит символ '*', помещенный перед именем. Например, определение:

int *ilp, *i2p, *i3p, i;

вводит три указателя на объекты целого типа ilp, i2p, i3p и одну переменную i целого типа.

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

· явно заданный адрес участка памяти;

· указатель, уже имеющий значение;

· выражение, позволяющее получать адрес объекта с помощью операции ‘&’.

Если значение константного выражения равно нулю, то это нулевое значение преобразуется к пустому (иначе нулевому) указателю. Примеры:

char cc = 'd'; // Символьная переменная (типа char).

char *pc = &cc; // Инициализированный указатель на объект типа char.

char *ptr (NULL); // Нулевой указатель на объект типа char.

char *p; // Неинициализированный указатель на объект типа char.

Переменная cc инициализирована значением символьной константы 'd'. После определения (с инициализацией) указателя pc доступ к значению переменной cc возможен как с помощью ее имени, так и с помощью адреса, являющегося значением указателя переменной pc. В последнем случае должна применяться операция разыменования '*' (получение значения через указатель). Например, при выполнении строки:

cout <<”\n cc=”<< cc <<” и *pc = “<< *pc;

будет выведено: cc=d и *pc=d.

Присвоив указателю адрес конкретного участка памяти, можно с помощью операции разыменования не только получать, но и изменять содержимое этого участка памяти. Например, оператор присваивания: *pc=’+’ сделает значением переменной сс символ ' + '.

Чтобы связать неинициализированный указатель с новым участком памяти, еще не занятым никаким объектом программы, используется операторnew или присваивается указателю явный адрес, например:

p = new char; // Выделили память для переменной типа char

// и связали указатель р с этим участком памяти

p = (char *)0xb8000000; // Начальный адрес видеопамяти

// ПЭВМ для цветного дисплея в текстовом режиме

Числовое значение в последнем случае преобразуется к типу указателя (char *).

После любого из таких операторов можно использовать *p для записи в память нужных символьных значений. Например, допустимы операторы: p = ‘&’; и cin>>*p;.

При работе с указателями часто используется операция & - получение адреса объекта. Для нее существуют следующие ограничения:

· нельзя определять адрес неименованной константы, то есть недо­пустимы выражения &3.141593 или & ‘?’ ;

· нельзя определять адрес значения, получаемого при вычислении скалярных выражений, то есть недопустимы конструк­ции: &(44*x – z) или &(a + b) !=12;

· нельзя определить адрес переменной, относящейся к классу па­мятиregister.

Таким образом, операция & применима к объектам, имеющим имя и размещенным в памяти. Ее нельзя применять к выражениям, неименованным константам, битовым полям структур и объединений, регистровым переменным и внешним объектам (файлам).

 

Операции над указателями

 

Существуют следующие операции над указателями:

· операция разыменования или доступа по адресу (*);

· преобразование типов (приведение типов);

· присваивание;

· получение (взятие) адреса (&);

· сложение и вычитание (аддитивные операции);

· инкремент или автоувеличение (++);

· декремент или автоуменьшение (--);

· операции отношений (операции сравнения).

Проиллюстрируем использование этих операций конкретными примерами.

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

#include<iostream.h>

void main()

{

unsigned long L=0x12345678L;

char *cp=(char *)&L;

int *ip=(int *)&L;

long *lp=(long *)&L;

cout <<hex; //Шестнадцатеричное представление выводимых значений

cout <<"\n Адрес L, &L="<<&L;

cout <<"\ncp = " <<(void*)cp<<

"\t*cp = 0x"<<(int)*cp;

cout <<"\nip = " <<(void *)ip<<"\t*ip=0x"<<*ip;

cout <<"\nlp = " <<(void *)lp<<"\t*lp=0x"<<*lp;

}

Результат выполнения программы:

Адрес L, &L=0x243f2256

cp = 0x243f2256 *cp= 0x78

ip = 0x243f2256 *ip= 0x5678

lp = 0x243f2256 *lp= 0x12345678

В программе используется явное приведение типов. Так как адрес &L имеет тип unsigned long *, то при инициализации указателей его значение явно преобразуется соответственно к типам char *, int *, long *. При выводе значений указателей они преобразуются к типу void *, так как требуется вывод значений, а не длин участков памяти, связанных со значениями указателей.

При выводе значения *cp использовано явное преобразование типа(int), так как при его отсутствии будет выведен не код, а соответствующий ему символ ASCII-кода. Особенность размещения чисел в памяти IBM PC заключается в том, что сначала размещается младшие байты числа, а затем старшие (рис.1).

Адреса байтов: 2259 2258 2257 2256

   
  *cp
  *ip
  *lp

Рис.1. Схема размещения в памяти IBM PC переменной L.

Примечание: на разных компьютерах могут получаться другие значения cp, ip, lp.

Пример 2: печать значений адресов и длин некоторых указателей.

#include<iostream.h>

void main()

{

char *pac,*pbc;

long *pal,*pbl;

cout <<hex;

cout <<"\n Адреса указателей:";

cout <<"\n &pac = " << &pac <<" &pbc = " <<&pbc;

cout <<"\n &pal = " << &pal <<" &pbl = " <<&pbl;

cout <<"\n Длины указателей некоторых типов:";

cout <<"\n sizeof (void *) = " <<sizeof(void *);

cout <<"\n sizeof (char *) = " <<sizeof(char *);

cout <<"\n sizeof (int *) = " <<sizeof(int *);

cout <<"\n sizeof (long *) = " <<sizeof(long *);

cout <<"\n sizeof (float *) = " <<sizeof(float *);

cout <<"\n sizeof (double *) = " <<sizeof(double *);

cout <<"\n sizeof (long double *) = " <<sizeof(long double *);

}

 

Пример 3: Приоритеты унарных операций.

#include<iostream.h>

void main()

{

int i1=10, i2=20, i3=30;

int *p=&i2;

//Значение i2

cout <<"\n *&i2 = “<< *&i2;

//Значение i2 сначала увеличенное на 1

cout <<"\n *&++i2 = “<< *&++i2;

//Значение i2

cout <<"\n *p = “<< *p;

//Значение i2, p увеличивается на 1

cout <<"\n *p++ = “<< *p++;

//Значение i1

cout <<"\n *p = “<< *p;

//Значение i1 сначала увеличенное на 1

cout <<"\n ++*p = “<<++*p;

//Значение i2, сначала уменьшается p

cout <<"\n*--p = “<<*--p;

//Значение i3, сначала уменьшается p, затем полученное значение i3

//увеличивается

cout <<"\n++*--p = “<<++*--p;

}

Выражение *p++ вычисляется в таком порядке: вначале выполняется именование (обращение по адресу), и полученное значение (21) служит значением выражения в целом. Затем выполняется операция ++ и значение указателя увеличивается на 1. Тем самым он “устанавливается” на переменную i1. (В памяти компьютера переменные i1,i2, i3 располагаются в обратном порядке).

 

Массивы и указатели

 

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

Определение одномерного массива имеет вид:

<тип элементов массива> <имя массива>[<количество элементов в массиве>]; .

Нумерация элементов в массиве начинается с нуля! В некоторых случаях допустимо описание массива без указания количества его элементов, то есть без константного выражения в квадратных скобках. Например: extern unsigned long UL[]; является описанием внешнего массива, который определен в другой части программы, где ему выделена память и (возможно) присвоены начальные значения его элементам.

При определении массива может выполняться его инициализация, то есть элементы массива получают конкретные значения. Инициализация выполняется по умолчанию, если массив статический или внешний. В этих случаях всем элементам массива компилятор автоматически присваивает нулевые значения. Явная инициализация элементов массива разрешена только при его определении и возможна двумя способами: либо с указанием раз­мера массива в квадратных скобках, либо без явного указания (без конкретного выражения) в квадратных скобках, например:

char СН[ ] = { ‘А’, ‘В’, ‘С’, ‘D’}; // Массив из 4 элементов;

int pr[6]= {10,20,30,40}; // Массивиз б элемент;

char St[ ] = “ABCD”; // Массивиз 5 элементов (включая нулевой элемент).

Следующие примеры являются ошибочными:

float A[ ] // Отсутствует размер массива;

double b[4] = {1,2,3,4,5,6,7,8} // Количество элементов

//не совпадает с размером массива

Количество элементов массива CH компилятор определяет по числу начальных значений в списке инициализации, помещенном в фигурных скобках при определении массива. В массиве pr шесть эле­ментов, но только первые четыре из них явно получают начальные значения. При отсутствии константного выражения в квадратных скобках список начальных значений в определении массива обязателен. Если размер массива явно задан, то количество элементов в списке начальных значений не должно превышать размера массива.

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

extern float E[ ]; // Правильное описание внешнего массива

Предполагается, что в месте определения массива для него выде­лена память и выполнена инициализация.

Имя массива является указателем-константой, значением которой служит адрес первого элемента массива (с индексом 0). Таким образом, доступ к первому элементу массива может быть осуществлен так:

<имя массива>[0] или *<имя массива> .

В общем случае доступ к заданному элементу массива можно осуществить двумя способами: <имя массива>[<номер элемента>] или *(<имя массива>+<номер элемента>).

Пример: вывести на экран заданную строку несколькими способами.

#include <iostream.h>

void main()

{

char x[]="Пример строки";

int i=0;

cout<<"\n Вывод первого символа строки (1 способ): "<<x[0];

cout<<"\n Вывод первого символа строки (2 способ): "<<*x;

cout<<"\n Вывод строки без цикла (1 способ): "<<x;

cout<<"\n Вывод строки без цикла (1 способ): "<<&x[0];

cout<<"\n Вывод строки с циклом (1 способ): ";

while (x[i]!='\0')

cout<<x[i++];

cout<<"\n Вывод строки с циклом (2 способ): ";

i=0;

while (*(x+i)!='\0')

cout<<*(x+i++);

cout<<"\n Вывод строки с циклом (3 способ): ";

i=0;

while (i[x]!='\0')

cout<<i++[x];

}

В заключение отметим, что в языке Си++ нет специального типа днных “строка”. Вместо этого каждая символьная строка в памяти компьютера представляется в виде одномерного массива типаchar, последн­им элементом которого является символ ‘\0’. Изображение строковой константы может использоваться по-разному. Если строка применяется для инициализации массива типа char,например, так: char array[ ] = "инициализирующая строка";, то адрес первого элемента строки становится значением указателя-константы (имени массива) array. Если строка используется для инициализации указателя типа char *: char *pointer = "инициализирующая строка" ;, то адрес первого элемента строки становится значением указателя-переменной pointer. Если использовать строку в выражении, где разрешено применять указатель, то используется адрес первого элемента строки:

char * string;

string = “строковый литерал”;

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

 

Многомерные массивы

Многомерный массив представляет собой массив массивов, то есть массив, элементами которого служат массивы. Определение многомерного массива в общем случае должно содер­жать сведения о типе, размерности и количествах элементов каждой размерности, например описание: int ARRAY[4][3][6]; определяет массив, состоящий из четырех элементов, каждый из которых - двухмерный массив с размерами 3 на 6. В памяти массив array размещается в порядке возрастания самого правого индекса, то есть самый младший адрес имеет элемент array[0][0][0], затем идет элемент array[0][0][1] и т.д.

С учетом порядка расположения в памяти элементов многомерного массива нужно размещать начальные значения его элементов в списке инициализации. Например, конструкция int ARRAY [4][3][6] = {0,1,2,3,4,5,6,7} инициализирует только первые 8 элементов этого массива: ARRAY[0][0][0]=0, ARRAY[0][0][1]=1, ARRAY[0][0][2]=2, ARRAY[0][0][3]=3, ARRAY[0][0][4]=4, ARRAY[0][0][5]=5, ARRAY[0][1][0]=6, ARRAY[0][1][1]=7.

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

Следующее определение с инициализацией трехмерного массива:

int А[4][5][6] = { { {0} },

{ {100}, {110, 111} },

{ {200), {210}, {220, 221, 222} };

так задает некоторые значения его элементов:

А[0][0][0]=0,

А[1][0][0]=100, A[1][1][0]=110, A[1][1][1]=111,

А[2][0][0]=200, A[2][1][0]=210, A[2][2][0]=220, А[2][2][1]=221, A[2][2][2]=222 .

Если многомерный массив при определении инициализируется, то его самая левая размерность может в скобках не указываться. Ко­личество элементов компилятор определяет по числу членов в ини­циализирующем списке. Например, определение: float matrix [ ][5] = { {1}, {2}, {3} }; формирует массив matrix с размерами 3 на 5, но не определяет явно начальных значений всех его элементов.

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

 

 

#include <iostream.h>

void main()

{

int b[3][2][4] = { 0, 1, 2, 3,

4, 5, 6, 7,

10, 11, 12, 13,

14, 15, 16, 17,

100, 101, 102, 103,

104, 105, 106, 107 };

//Адрес массива b[ ][ ][ ]

cout<<"\n b= "<<b;

// Адрес массива b[0][ ][ ]

cout<<"\n *b= "<<*b;

// Адрес массива b[0][0][]

cout<<"\n **b= "<<**b;

// Элемент b[0][0][0]

cout<<"\n ***b= "<<***b;

// Адрес массива b[1][ ][ ]

cout<<"\n *(b+1) = "<<*(b+1);

// Адрес массива b[2][][]

cout<<"\n *(b+2) = "<<*(b+2);

// Адрес массива b[0][1][ ]

cout<<"\n *(*b+1)= "<<*(*b+1);

//Вывод элемента b[1][1][1] двумя способами

cout<<"\n *(*(*(b+1)+1)+1)= "<<*(*(*(b+1)+1)+1);

cout<<"\n b[1][1][1]= "<<b[1][1][1];

}

В общем случае для трехмерного массива индексированный элемент b[i][j][k] соответствует выражению *(*(*(b + i) + j) + k).

Компилятор всегда реализует доступ к элементам массива с помощью указателя и операции разыменования. Если в программе использована, например, такая индексированная переменная: AR[i][j][k], принадлежащая массиву type ar[N][М][L], где N, M, L - целые положительные константы, то последовательность действий компилятора такова:

· выбирается адрес начала массива, то есть целочисленное значение указателя AR, равное (unsigned long)AR;

· добавляется смещение i * (M * L) * sizeof(type) для вычисления начального адреса i-го массива с размерами M на L, входящего в исходный трехмерный массив;

· добавляется смещение для вычисления начального адреса j-й строки (одномерный массив), включающей L элементов. Теперь смещение равно (i * (M * L) + j * L) * sizeof(type);

· добавляется смещение для получения адреса k-го элемента в строке, то есть получается адрес (unsigned long) (i * (M * l) +j * L + k) * sizeof(type);

· применяется разыменование, то есть обеспечивается доступ к со­держимому элемента по его адресу: * ((unsigned long) (i * (м * L) +j * l + k)).

 

Массивы указателей

 

Для понимания конструкций, состоящих из набора звездочек, скобок и имен типов, нужно аккуратно применять синтаксические правила, учитывающие последовательность выполнения операций. Например, определение: int *array[6]; вводит массив указателей на объекты типа int. Имя массива array, он состоит из шести элементов, тип каждого int *. Определение int (*ptr)[6]; вводит указатель ptr на массив из шести элементов, каждый из которых имеет тип int.Возможность создания массивов указателей позволяет экономить память при использовании многомерных массивов.

По определению массива, его элементы должны быть однотип­ные и одинаковые по длине. Пусть необходимо определить массив для представления списка фамилий. Если определять его как двухмерный массив типаchar, то в определении элементов массива необходимо задать предельные размеры каждого из двух индексов, например: char spisok[25][20];. Таким образом, количество фамилий в списке не более 25 и что длина каждой фамилии не превышает 19 сим­волов (букв). При определении массива одну из его предельных размерностей (значение самого левого индекса) можно не указывать. В этом случае количе­ство элементов массива определяется, например, инициализацией: char spisok[][20] = {"ИВАНОВ", "ПЕТРОВ", "СИДОРОВ"}; (рис. 2).

Массив spisokМассивы char[20]

И В А Н О В \0
П Е Т Р О В \0
С И Д О Р О В \0

Массив pointer Строковые константы

* à И В А Н О В \0  
* à П Е Т Р О В \0  
* à С И Д О Р О В \0

Рис.2. Размещение массивов в памяти ЭВМ.

Теперь в массиве spisok только 3 элемента, каждый из них длиной 20 элементов типаchar. В противоположность этому при определении и инициализации этими же символьными строками одномерного массива указателей типа char * память распределяется гораздо рациональнее:

char *pointer [ ] ={ "ИВАНОВ", "ПЕТРОВ", "СИДОРОВ" ); .

Для указателей массива pointer, в котором 3 элемента и каждый является указателем-переменной типа char *, выделяется всего 3*sizeof(char *) байтов. Кроме того, компилятор размещает в памяти три строковые константы "ИВАНОВ" (7 байт), ПЕТРОВ" (7 байт), "СИДОРОВ" (8 байт), а их адреса становятся значениями элементов pointer[0], pointer[l], pointer[2] (рис.2).

Применение указателей и их массивов позволяет рационально решать, в частности, задачи сортировки сложных объектов с неодинаковыми размерами. Например, рассмотрим задачу сортировки строк матрицы. Матрица с элементами типаdouble представлена двухмерным массивом double array [n][m], где n и m - целочисленные константы. Предположим, что нужно упорядочить строки матрицы в порядке возрастания сумм их элементов. Чтобы не переставлять сами строки исходного массива введен вспомогательный одномерный массив указателей double * par[n]. Инииализируем его элементы адресами массивов строк. В качестве значений элементов массива используем номера строк. Матрицу напечатаем три раза: до и после сортировки с помощью вспомогательного массива указателей и (после сортиров­ки) с использованием основного имени массива.

#include <iostream.h>

void main()

{

const int n=5; //Количество строк

const int m=7; //Количество столбцов

double array[n][m]; //Основной массив

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

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

array[i][j]=n-i; //Заполнение массива

double *par[n]; //Массив указателей

for (i=0;i<n;i++) //Цикл перебора строк

par[i]=(double*)array[i];

//Печать массива через указатели

cout<<"\n До перестановки элементов массива "<<

“указателей: ”;

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

{ cout<<"\n строка "<<(i+1)<<": ";

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

cout<<"\t"<<par[i][j];

}

//Упорядочение указателей на строки массива

double si,sk;

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

{ for (int j=0, si=0;j<m;j++)

si+=par[i][j];

for (int k=i+1;k<n;k++)

{ for (j=0,sk=0;j<m;j++)

sk+=par[k][j];

if (si>sk)

{ double *pa=par[i];

par[i]=par[k];

par[k]=pa;

double a=si;

si=sk;

sk=a;

}

}

}

//Печать массива через указатели

cout<<"\nПосле перестановки элементов массива:";

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

{ cout<<"\n строка "<<(i+1)<<": ";

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

cout<<"\t"<<par[i][j];

}

cout<<"\nИсходный массив остался неизменным: ";

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

{ cout<<"\nstroka "<<(i+1)<<": ";

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

cout<<"\t"<<array[i][j];

}

}

 








Не нашли, что искали? Воспользуйтесь поиском по сайту:



©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.