Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
visual с++.doc
Скачиваний:
30
Добавлен:
18.08.2019
Размер:
10.99 Mб
Скачать

1. Присваивание указателю адреса существующего объекта:

1.1. с помощью операции получения адреса:

int a = 5; // целая переменная

int* р = &а; //в указатель записывается адрес а

int* p (&а); // то же самое другим способом

1.2. с помощью значения другого инициализированного указателя:

int* r = p;

1.3. с помощью имени массива или функции, которые трактуются как адрес:

int b[10]; // массив

int* t = b; // присваивание адреса начала массива

void f(int a ){ /* ... */ } // определение функции

void (*pf)(int); // указатель на функцию

pf = f; // присваивание адреса функции

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

char* vp = (char *)0xB8000000;

Здесь 0хВ8000000 - шестнадцатеричная константа, (char *) - операция приве­дения типа: константа преобразуется к типу «указатель на char».

3.Присваивание пустого значения:

int* suxx = NULL;

int* rulez = 0;

В первой строке используется константа NULL, определенная в некоторых за­головочных файлах С как указатель, равный нулю. Можно использо­вать просто 0, так как это значение типа int будет правильно преобразовано стандартными способами в соответствии с контекстом. Поскольку гарантиру­ется, что объектов с нулевым адресом нет, пустой указатель можно использо­вать для проверки, ссылается указатель на конкретный объект или нет.

4. Выделение участка динамической памяти и присваивание ее адреса указателю:

4.1. с помощью операции new:

int* n = new int; // 1

int* m = new int (10); // 2

int* q = new int [10]; // 3

4.2. с помощью функции malloc:

int* u = (int *)malloc(sizeof(int)); // 4

В операторе 1 операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала это­го участка в переменную n. Память под саму переменную n (размера, достаточно­го для размещения указателя) выделяется на этапе компиляции.

В операторе 2, кроме описанных выше действий, производится инициализация выделенной динамической памяти значением 10.

В операторе 3 операция new выполняет выделение памяти под 10 величин типа int (массива из 10 элементов) и записывает адрес начала этого участка в пере­менную q, которая может трактоваться как имя массива. Через имя можно обра­щаться к любому элементу массива. Если память выделить не удалось, по стандарту должно порождаться исключе­ние bad_alloc. Старые версии компиляторов могут возвращать 0.

В операторе 4 делается то же самое, что и в операторе 1, но с помощью функции выделения памяти malloc, унаследованной из библиотеки С. В функцию переда­ется один параметр - количество выделяемой памяти в байтах. Конструкция (int*) используется для приведения типа указателя, возвращаемого функцией, к требуемому типу. Если память выде­лить не удалось, функция возвращает 0.

Операцию new использовать предпочтительнее, чем функцию malloc, особенно при работе с объектами.

Освобождение памяти, выделенной с помощью операции new, должно выпол­няться с помощью delete, а памяти, выделенной функцией mallос - посредством функции free. При этом переменная-указатель сохраняется и может инициали­зироваться повторно. Приведенные выше динамические переменные уничтожа­ются следующим образом:

delete n;

delete m;

delete [] q;

free (u);

Если память выделялась с помощью new[], для освобождения памяти необходимо применять delete[]. Размерность массива при этом не указывается. Если квад­ратных скобок нет, то никакого сообщения об ошибке не выдается, но помечен как свободный будет только первый элемент массива, а остальные окажутся не­доступны для дальнейших операций. Такие ячейки памяти называются мусором.

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

int *(*р[10])();

объявляется массив из 10 указателей на функции без параметров, возвращающих указатели на int.

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

При интерпретации сложных описаний необходимо придерживаться правила «изнутри наружу»:

1) если справа от имени имеются квадратные скобки, это массив, если скобки круглые - это функция;

2) если слева есть звездочка, это указатель на проинтерпретированную ранее конструкцию;

3) если справа встречается закрывающая круглая скобка, необходимо приме­нить приведенные выше правила внутри скобок, а затем переходить наружу;

4) в последнюю очередь интерпретируется спецификатор типа.

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

int *(*р[10])();

5 4 2 1 3 // порядок интерпретации описания

Операции с указателями. С указателями можно выполнять следующие операции: разадресация, или кос­венное обращение к объекту (*), присваивание, сложение с константой, вычита­ние, инкремент (++), декремент (--), сравнение, приведение типов. При работе с указателями часто используется операция получения адреса (&).

Операция разадресации, или разыменования, предназначена для доступа к ве­личине, адрес которой хранится в указателе. Эту операцию можно использовать как для получения, так и для изменения значения величины (если она не объяв­лена как константа):

char a; // переменная типа char

char * р = new char; /* выделение памяти под указатель и под динамическую переменную типа char */

*р = 'Ю'; а = *р; /* присваивание значения обеим переменным*/

Как видно из примера, конструкцию *имя_указателя можно использовать в левой части оператора присваивания, так как она является L-значением, то есть определяет адрес области памяти. Для простоты эту конструкцию можно считать именем переменной, на которую ссылается указатель. С ней допустимы все действия, определенные для величин соответствующего типа (если указатель инициализирован). На одну и ту же область памяти может ссылаться несколько указателей различного типа. Примененная к ним сиерация разадресации даст разные результаты. Например, программа

#include <stdio.h>

int main()

{

unsigned long int A = 0Xcc77ffaa;

unsigned short int* pint = (unsigned short int*) &A;

unsigned char* pchar = (unsigned char *) &A;

printf(“ | %x | %x | %x |”, A, *pint, *pchar);

return 0;

}

на IBM PC-совместимом компьютере выведет па экран строку:

| cc77ffaa | ffaa | аа |

Значения указателей pint и pchar одинаковы, но разадресация pchar дает в резуль­тате один младший байт по этому адресу, a pint - два младших байта. В приведенном выше примере при инициализации указателей были использова­ны операции приведения типов. Синтаксис операции явного приведения типа прост: перед именем переменной в скобках указывается тип, к которому ее требу­ется преобразовать. При этом не гарантируется сохранение информации, поэто­му в общем случае явных преобразований типа следует избегать.

При смешивании в выражении указателей разных типов явное преобразование типов требуется для всех указателей, кроме void*. Указатель может неявно пре­образовываться в значение типа bool (например, в выражении условного опера­тора), при этом ненулевой указатель преобразуется в true, а нулевой в false. Присваивание без явного приведения типов допускается в двух случаях:

  1. указателям типа void*;

  2. если тип указателей справа и слева от операции присваивания один и тот же.

Таким образом, неявное преобразование выполняется только к типу void*. Зна­чение 0 неявно преобразуется к указателю на любой тип. Присваивание указате­лей на объекты указателям на функции (и наоборот) недопустимо. Запрещено и присваивать значения указателям-константам, впрочем, как и константам любо­го типа (присваивать значения указателям на константу и переменным, на кото­рые ссылается указатель-константа, допускается).

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

Инкремент перемещает указатель к следующему элементу массива, декремент - к предыдущему. Фактически значение указателя изменяется на величину sizeof (тип). Если указатель на определенный тип увеличивается или уменьшает­ся на константу, его значение изменяется на величину этой константы, умножен­ную на размер объекта данного типа, например:

short * р = new short [S3;

р++; // значение р увеличивается на 2

long * q = new long [5];

q++; // значение q увеличивается на 4

Разность двух указателей - это разность их значений, деленная на размер типа в байтах (в применении к массивам разность указателей, например, на третий и шестой элементы равна 3). Суммирование двух указателей не допускается.

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

*р++ = 10;

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

*р = 10; р++;

Выражение (*р)++, напротив, инкрементирует значение, на которое ссылается указатель.

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

Динамические массивы. Если до начала работы программы неизвестно, сколько в массиве элементов, в про­грамме следует использовать динамические массивы. Память под них выделяется с помощью операции new или функции mallос в динамической области памяти во время выполнения программы. Адрес начала массива хранится в переменной-указателе.

Обращение к элементу динамического массива осуществляется так же, как и к эле­менту обычного -­­­ например а[3]. Можно обратиться к элементу массива и другим способом - *(а+3). В этом случае мы явно задаем те же действия, что выполня­ются при обращении к элементу массива обычным образом. Рассмотрим их подробнее. В переменной-указателе а хранится адрес начала массива. Для получения адреса третьего элемента к этому адресу прибавляется смещение 3. Операция сло­жения с константой для указателей учитывает размер адресуемых элементов, то есть на самом деле индекс умножается на длину элемента массива: а + 3*sizeof(int). Затем с помощью операции * (разадресации) выполняется выборка значения из указанной области памяти.

Если динамический массив в какой-то момент работы программы перестает быть нужным и мы собираемся впоследствии использовать эту память повторно, необ­ходимо освободить ее с помощью операции delete[], например:

delete[] a;

Размерность массива при этом не указывается.

Таким образом, время жизни динамического массива, как и любой динамической переменной, - с момента выделения памяти до момента ее освобождения. Область действия зависит от места описания указателя, через который производится рабо­та с массивом. Область действия и время жизни указателей подчиняются общим правилам, рассмотренным на первом семинаре. Как вы помните, локальная пере­менная при выходе из блока, в котором она описана, «теряется». Если эта перемен­ная является указателем и в ней хранится адрес выделенной динамической памя­ти, при выходе из блока эта память перестает быть доступной, однако не помечается как свободная, поэтому не может быть использована в дальнейшем. Это называет­ся утечкой памяти и является распространенной ошибкой:

{ // пример утечки памяти

int n;

cin >> n;

int* pmas = new int[n];

}

// После выхода из блока указатель pmas недоступен

Пример 5.1. Написать программу, которая для вещественного массива из n элементов определяет сумму его элементов, расположенных правее последнего отрицательного элемента.

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

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

Листинг 5.1

#include "conio.h"

#include "math.h"

#include "windows.h"

#include “stdafx.h”

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])

{

int n;

setlocale( LC_ALL, "Russian" );

cout<<"\nВведите количество элементов: ";

int i, ineg;

float sum, *a = new float[n]; // 1

cout<< "\nВведите элементы массива: \n\n";

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

for(i = 0; i < n; i++) cout << a[i] << ‘ ‘; // 2

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

if(a[i] < 0) ineg = i; // 3

for(sum = 0., i = ineg + 1; i < n; i++)

sum += a[i]; // 4

cout<<"\nСумма: "<< sum;

delete[] a;

return 0;

}

Поскольку количество элементов заранее не задано, память под массив выделяет­ся в операторе 1 на этапе выполнения программы с помощью операции new. Выде­ляется столько памяти, сколько необходимо для хранения n элементов веществен­ного типа, и адрес начала этого участка заносится в указатель а.

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

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

Теперь перейдем к критическому анализу нашей первой попытки решения задачи. Для массивов, содержащих отрицательные элементы, эта программа работает верно, но при их отсутствии, как правило, завершается аварийно. Это связано с тем, что если в массиве нет ни одного отрицательного эле­мента, переменной ineg значение не присваивается. Поэтому в операторе for (опе­ратор 4) будет использовано значение ineg, инициализированное произвольным образом. Поэтому в программу необходимо внести проверку, есть ли в массиве хотя бы один отрица­тельный элемент. Для этого переменной ineg присваивается начальное значение, не входящее в множество допустимых индексов массива (например, -1). После цикла поиска номера отрицательного элемента выполняется проверка, сохрани­лось ли начальное значение ineg неизменным. Если это так, это означает, что усло­вие a[i]<0 в операторе 3 не выполнилось ни разу, и отрицательных элементов в массиве нет:

Листинг 5.2

#include "stdafx.h"

#include "conio.h"

#include "math.h"

#include "windows.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])

{

setlocale( LC_ALL, "Russian" );

int n;

cout<<"\nВведите количество элементов: ";

cin >> n;

int i, ineg = -1;

float sum, *a = new float[n]; // 1

cout<<"\nВведите элементы массива: \n\n";

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

for(i = 0; i < n; i++) cout << a[i] << ‘ ‘; // 2

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

if(a[i] < 0) ineg = i; // 3

if(ineg != -1)

{

for(sum = 0., i = ineg + 1; i < n; i++)

sum += a[i]; // 4

cout<<"\nСумма: "<< sum;

}

delete[] a;

getch();

return 0;

}

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

Листинг 5.3

#include "stdafx.h"

#include "conio.h"

#include "math.h"

#include "windows.h"

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])

{

int n;

setlocale( LC_ALL, "Russian" );

cout<<"\nВведите количество элементов: ";

cin >> n;

int i, ineg = -1;

float sum, *a = new float[n]; // 1

cout<<"\nВведите элементы массива: \n\n";

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

bool flag_neg = false;

float sum = 0.f;

for(i = n - 1; i >= 0; i--)

{

if(a[i] < 0)

{

flag_neg = true;

break;

}

sum += a[i];

}

if(flag_neg)

cout<<"\nСумма: "<< sum;

else

cout<<"\nОтрицательных элементов нет ";

getch();

return 0;

}

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

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

Пример 5.2. Написать программу, которая упорядочивает вещественный массив методом бы­строй сортировки.

Идея алгоритма состоит в следующем. Применим к массиву так называемую про­цедуру разделения относительно среднего элемента. Вообще-то, в качестве «сред­него» можно выбрать любой элемент массива, но для наглядности мы будем выбирать по возможности, средний по своему номеру элемент.

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

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

Для хранения границ еще не упорядоченных частей массива более всего подходит структура данных, называемая стеком. Стек является одной из наиболее часто употребляемых структур данных и функционирует по принципу: «первым пришел, последним ушел».

В приведенной ниже программе стек реализуется в виде двух массивов stackr и stackl и одной переменной sp, используемой как «указатель» на вершину стека (она хранит номер последнего заполненного элемента массива). Для этого ал­горитма количество элементов в стеке не может превышать n, поэтому размер мас­сивов задан равным именно этой величине. При занесении в стек переменная sp увеличивается на единицу, а при выборке - уменьшается.

Ниже приведена программа, реализующая этот алгоритм:

Листинг 5.4

#include “stdafx.h”

#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])

{

const int n = 20;

float *arr = new float[n], middle, temp;

int *stackl = new int[n], *stackr = new int[n];

int sp = 0, i, j, left, right;

setlocale( LC_ALL, "Russian" );

cout << “Введите элементы массива: “;

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

// Сортировка

sp = 1; stackl[1] = 0; stackr[1] = n - 1; // 1

while(sp > 0)

{ // 2

// Выборка из стека последнего запроса

left = stackl[sp]; // 3

right = stackr[sp]; // 4

sp--; // 5

while(left < right)

{ // 6

// Разделение {arr[left]..arr[right]}

i = left; j = right; // 7

middle = arr[(left + right)/2]; // 8

while(i < j)

{ // 9

while(arr[i] < middle) i++; // 10

while(middle < arr[j]) j--; // 11

if(i <= j)

{

temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

i++; j--;

}

}

if(i < right)

{ // 12

//Запись в стек запроса из правой части

sp++;

stackl[sp] = i;

stackr[sp] = right;

}

right = j; // 13

// Теперь left и right ограничивают левую часть

}

}

// Вывод результата

for(i = 0; i < n; i++) cout << arr[i] << “ “;

cout << endl;

// Удаление динамических массивов

delete[] arr;

delete[] stackl;

delete[] stackr;

getch();

return 0;

}

На каждом шаге сортируется один фрагмент массива. Левая граница фрагмента хранится в переменной left, правая - в переменной right. Сначала фрагмент уста­навливается размером с массив целиком (строка 1). В операторе 8 выбирается «сред­ний» элемент фрагмента.

Для продвижения по массиву слева направо в цикле 10 используется переменная i, справа налево - переменная j (в цикле 11). Их начальные значения устанавлива­ются в операторе 7. После того, как оба счетчика «сойдутся» где-то в средней части массива, происходит выход из цикла 9 на оператор 12, в котором заносятся в стек границы правой части фрагмента. В операторе 13 устанавливаются новые грани­цы левой части для сортировки на следующем шаге.

Если сортируемый фрагмент уже настолько мал, что сортировать его не требуется, происходит выход из цикла 6, после чего выполняется выборка из стека границ еще не отсортированного фрагмента (операторы 3,4). Если стек пуст, происходит выход из главного цикла 2. Массив отсортирован.

Ссылки. Ссылка представляет собой синоним имени, указанного при инициализации ссылки. Ссылку можно рассматривать как указатель, который всегда разымено­вывается. Формат объявления ссылки:

тип & имя;

где тип - это тип величины, на которую указывает ссылка, & - оператор ссылки, означающий, что следующее за ним имя является именем переменной ссылочного типа, например:

int kol;

int& pal = kol; //ссылка pal-альтернативное имя для kol

const char& CR = "\n"; // ссылка на константу

Запомните следующие правила.

  • переменная-ссылка должна явно инициализироваться при ее описании, кро­ме случаев, когда она является параметром функции, описана как extern или ссылается на поле данных класса.

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

  • тип ссылки должен совпадать с типом величины, на которую она ссылается.

  • не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки.

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

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

Пример 5.3. Составить программу нахождения тех элементов массива С (из n элементов), индексы которых являются степенями двойки (1, 2, 4, 8…). UML-диаграмма этого алгоритма приведена на рисунке 5.1.

Рисунок 5.1 - UML-диаграмма деятельности для примера 5.3

Листинг 5.5

#include "stdafx.h"

#include <iostream>

#include "conio.h"

#include "math.h"

#include "windows.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])

{

setlocale( LC_ALL, "Russian" );

const int N = 1000;

int n, i, j, left, right, sp = 0, m = 1;

float *C = new float[N], middle, temp;

int *stackl = new int[N], *stackr = new int[N];

cout<<"\n\nЗадание:\n";

cout<<"\nСоставить программу с использованием динамических массивов\n";

cout<<"\nдля решения задачи на переупорядочивание элементов массива.\n";

cout<<"\nВ качестве алгоритма сортировки использовать метод быстрой сортировки массива.\n";

cout<<"\n\nЗадача: составить программу нахождения тех элементов массива С,\n";

cout<<"\nиндексы которых являются степенями двойки (1, 2, 4,...).\n";

cout<<"\n\nРабота программы:\n"

A:

cout<<"\nВведите количество элементов: ";

cin >> n;

if(n <= 0 || n > N)

{

cout<<"Неверный размер массива"<< "\n";

goto A;

}

cout<<"\nВведите элементы массива: \n\n";

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

sp = 1;

stackl[1] = 0;

stackr[1] = n - 1;

while(sp > 0)

{

left = stackl[sp];

right = stackr[sp];

sp--;

while(left < right)

{

i = left;

j = right;

middle = C[(left + right)/2];

while(i < j)

{

while(C[i] < middle) i++;

while(middle < C[j]) j--;

if(i <= j)

{

temp = C[i];

C[i] = C[j];

C[j] = temp;

i++;

j--;

}

}

if(i < right)

{

sp++;

stackl[sp] = i;

stackr[sp] = right;

}

right = j;

}

}

cout<<"\nМассив после сортировки:\n\n";

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

{

cout << C[i] << "\n";

}

cout<<"\nМассив из элементов, индексы которых являются степенями двойки:\n\n"<<"\n" ;

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

{

while (m < n)

{

cout << C[m] << "\n" ;

m = m * 2;

}

}

delete[] C;

delete[] stackl;

delete[] stackr;

getch();

return 0;

}

Пример 5.4. В массиве {aj}, j = 1, 2, …10 есть положительные и отрицательные элементы. Вычислить произведение отрицательных элементов. UML-диаграмма этого алгоритма приведена на рисунке 5.2.

Рисунок 5.2 - UML-диаграмма деятельности для примера 5.4

Листинг 5.6

// Лабораторная работа №_5_1

#include "stdafx.h"

#include <iostream>

#include "conio.h"

#include "windows.h"

#include <tchar.h>

#include <math.h>

#include <time.h>

#include <iomanip>

using namespace std;

const int n = 1000;

int _tmain(int argc, _TCHAR* argv[])

{

int n;

int RANGE_MIN = 0;

int RANGE_MAX = 20;

setlocale( LC_ALL, "Russian" );

cout<< "Лабораторная работа №_5_1" << '\n';

cout<< '\n';

cout<< " В массиве {aj}, j = 1, 2, ...10 есть положительные и отрицательные элементы. " ;

cout<< '\n';

cout<< " Вычислить произведение отрицательных элементов " ;

cout<< '\n'<< '\n'<< '\n';

cout<< "\nВведите резмерность массива " ;

cin >> n;

int i = 0, s = 0, klav;

double *arr = new double[n];

double piece = 1;

cout<< '\n';

cout<< " Как будет проидведенно заполнение? " ;

cout<< '\n';

cout<<" 1 - случайным образом " ;

cout<< '\n';

cout<< " 2 - в ручную " ;

cout<< '\n';

cout<<"Для продолжения нажмите клавишу выбора ";

cin >> klav;

cout << '\n';

if (klav == 1)

{

srand( (unsigned int)time( NULL ) );

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

{

int rand100 = (((double) rand() / (double) RAND_MAX) * RANGE_MAX + RANGE_MIN);

arr[i]=rand100-10;

}

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

{

cout << arr[i] << " ";

}

}

if (klav==2)

{

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

{

cout<< "Введите " << i+1 ;

cout<< " элемент ";

cin >> arr[i];

}

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

{

cout << arr[i] << " ";

}

}

if (klav != 1 && klav !=2 )

{

cout<< "Неправильный выбор" ;

cout<< " Для выходна нажмите любую клавишу...";

goto stop;

}

cout << '\n';

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

{

if (arr[i] < 0)

{

piece = piece*arr[i];

s++;

}

}

if (s==0)

{

cout<< '\n '<<"В массиве отсутствуют отрицательные элементы";

}

else

{

cout<< '\n' <<" Произведение отрицательных элементов = " << piece;

}

delete []arr;

stop: getch ();

return 0;

}

Аппаратура и материалы. Для выполнения лабораторной работы необходим персональный компьютер со следующими характеристиками: процессор Intel Pentium-совместимый с тактовой частотой 800 МГц и выше, оперативная память - не менее 64 Мбайт, свободное дисковое пространство - не менее 500 Мбайт, устройство для чтения компакт-дисков, монитор типа Super VGA (число цветов от 256) с диагональю не менее 15. Программное обеспечение - операционная система Windows2000/XP и выше, среда разработки приложений Microsoft Visual Studio.

Указания по технике безопасности. Техника безопасности при выполнении лабораторной работы совпадает с общепринятой для пользователей персональных компьютеров, самостоятельно не производить ремонт персонального компьютера, установку и удаление программного обеспечения; в случае неисправности персонального компьютера сообщить об этом обслуживающему персоналу лаборатории (оператору, администратору); соблюдать правила техники безопасности при работе с электрооборудованием; не касаться электрических розеток металлическими предметами; рабочее место пользователя персонального компьютера должно содержаться в чистоте; не разрешается возле персонального компьютера принимать пищу, напитки.

Методика и порядок выполнения работы. Перед выполнением лабораторной работы каждый студент получает индивидуальное задание в соответствии с индивидуальным заданием лабораторной работы №4. Защита лабораторной работы происходит только после его выполнения (индивидуального задания). При защите лабораторной работы студент отвечает на контрольные вопросы, приведенные в конце, и поясняет выполненное индивидуальное задание. Ход защиты лабораторной работы контролируется преподавателем. Порядок выполнения работы:

  1. Проработать примеры, приведенные в лабораторной работе.

2. Составить программу с использованием динамических массивов для решения задачи. Номер варианта определяется по формуле , где - номер студента по списку преподавателя.

Индивидуальное задание №1. Вариант:

    1. Сформировать массив, содержащий 7 элементов, задав элементы с клавиатуры. Определить количество элементов, кратных 3 и индексы последнего такого элемента.

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

    3. Сформировать с помощью датчика случайных чисел массив, элементы которого находятся в промежутке от -60 до 60. Подсчитать в нем количество положительных элементов.

    4. В заданном одномерном массиве все отрицательные элементы заменить нулями и подсчитать их количество.

    5. В массивах U[7], D[7], V[7] содержатся значения утренней, дневной и вечерней температуры соответственно за каждый день недели. Подсчитать среднее значение дневной температуры за каждый день.

    6. В массивах А[n], G[n], F[n] содержатся оценки учащихся по алгебре, геометрии и физике соответственно. Определить, по какому предмету лучше успеваемость.

    7. Сформировать с помощью датчика случайных чисел массив A[n], элементы которого находятся в промежутке от -60 до 60. Создать массив B[n], каждый элемент которого вычисляется по формуле:

    1. В заданном одномерном массиве найти максимальный элемент и поменять его местами с последним элементом массива.

    2. Задан массив из N случайных чисел, принадлежащих промежутку [-50, 100]. Найти сумму тех элементов массива, которые больше 15, но меньше 45, а также вычислить количество этих элементов.

    3. В массивах U[7], D[7], V[7] содержатся значения утренней, дневной и вечерней температуры соответственно за каждый день недели. Сформировать массив S[7], в котором будут содержаться значения среднедневной температуры. Определить среднее значение температуры за неделю.

    4. В массивах А[n], G[n], F[n] содержатся оценки учащихся по алгебре, геометрии и физике соответственно. Определить среднюю оценку по алгебре и количество учащихся, не имеющих ни одной «двойки».

    5. Из массива целых чисел составить три других, в первый из которых записать числа, кратные 5, во второй - числа, кратные 7, а в третий - остальные числа.

    6. Дана последовательность из 100 различных чисел. Составить программу нахождения суммы чисел этой последовательности, расположенных между максимальным и минимальным числами (в сумму включить и оба эти числа)

    7. Задан одномерный числовой массив. Вычислить сумму произведений всех пар соседних чисел

    8. Определить в одномерном числовом массиве число соседств из двух чисел разного знака.

    9. Проверить, имеется ли в данном одномерном числовом массиве хотя бы одна пара чисел, совпадающих по величине.

    10. Проверить, имеется ли в одномерном числовом массиве хотя бы одна пара соседних чисел, являющихся противоположными.

    11. Дан числовой массив a1, a2, …an. Вывести на печать массив b1, b2,… bn, в котором bi= a1, b2= a1+a2, b3= a1+a2+a3 и т.д.

    12. В заданном массиве найти максимальный элемент. Элементы, стоящие после максимального элемента заменить нулями.

    13. Из чисел a1, a2, …. , an выбрать те, которые меньше заданного числа с , и образовать из них новый массив, сохранив порядок следования элементов.

    14. В массиве {aj}, j = 1, 2, …10 есть хотя бы один отрицательный элемент. Вычислить произведение элементов массива до первого отрицательного.

    15. В массиве {aj}, j = 1, 2, …8 есть хотя бы один нуль. Вычислить произведение массива до первого нуля

    16. В массиве {aj}, j = 1, 2, …8 есть хотя бы один отрицательный элемент. Вычислить сумму элементов массива до первого отрицательного элемента.

    17. В массиве {aj}, j = 1, 2, …8 есть хотя бы один ноль. Вычислить сумму элементов массива до первого нуля.

    18. В массиве {aj}, j = 1, 2, …10 есть положительные и отрицательные элементы. Вычислить произведение положительных элементов.

    19. В массиве {aj}, j = 1, 2, …10 подсчитать количество элементов, больших 3

    20. Найти сумму первых чисел последовательности a1, a2, …., an, произведение которых не превосходит заданного числа M.

28. Из чисел a1, a2, ..., an выбрать те, которые больше по модулю заданного числа c, и образовать из них новый массив, сохранив порядок следования элементов.

3. Составить программу с использованием динамических массивов для решения задачи на переупорядочивание элементов массива. В качестве алгоритма сортировки использовать метод быстрой сортировки массива. Номер варианта определяется по формуле , где - номер студента по списку преподавателя.

Индивидуальное задание №2. Вариант:

    1. Массив содержит 2n чисел. Из суммы первых n его элементов вычесть сумму последних n элементов.

    2. Даны два массива разных размеров. Определить, какие элементы первого массива и сколько раз встречаются во втором массиве.

    3. Даны два целочисленных массива одинакового размера. Получить третий массив того же размера, каждый элемент которого равен большему из соответствующих элементов данных массивов.

    4. Задан массив С, содержащий m чисел. Составить программу формирования массивов А и В, включая в массив А четные по номеру элементы массива С в порядке их следования, а в массив В - нечетные.

    5. Заданы два массива А и В, содержащие по n чисел. Составить программу формирования массива С, включая в него сначала все элементы массива А, затем все элементы массива В.

    6. Числовой массив a1, a2, …an упорядочен по возрастанию. Известно, что число х принадлежит отрезку числовой оси, вмещающему заданный массив. Определить номер k, для которого ak-1< x <= ak.

    7. Заменить отрицательные элементы в числовом массиве из n чисел (n>10) их квадратами, оставив остальные без изменения.

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

    9. В массиве из 2n чисел найти сумму квадратов элементов с четными индексами и сумму кубов элементов с нечетными индексами.

    10. Транспонировать массив, т.е. по а1, а2,..., аn сформировать аn, аn-1, ..., a1.

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

    12. Заменить отрицательные числа в массиве их квадратами, оставив остальные без изменения.

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

    14. В массиве из 2n чисел найти сумму квадратов элементов с четными индексами и сумму кубов элементов с нечетными индексами.

    15. Из чисел а1, а2,..., аn выбрать те, которые больше по модулю заданного числа с, и образовать из них новый массив, сохранив порядок следования элементов.

    16. Из массива целых чисел составить три других, в первый из которых записать числа, кратные 5, во второй - числа, кратные 7, а в третий - остальные числа.

    17. Задан массив из 100 целых случайных чисел, принадлежащих промежутку [0, 100]. Найти сумму тех элементов массива, которые больше 15, но меньше 45, а также вычислить количество этих элементов.

    18. В линейном массиве заменить все элементы на число m (m - индекс максимального элемента).

    19. Дан массив, состоящий как из положительных, так и отрицательных чисел. Нужно сначала записать положительные числа, а затем отрицательные в том же порядке, как они были расположены в исходном массиве. Если есть нули, записать их в последнюю очередь.

    20. Найти сумму элементов данного массива. Разделить каждый элемент исходного массива на полученное значение.

    21. Вычислить сумму и разность массивов одного размера.

    22. Найти среднее арифметическое значение элементов заданного массива. Преобразовать исходный массив, вычитая из каждого элемента среднее значение.

    23. Даны два массива одинакового размера. Рассматривая их как арифметические векторы, найти длины этих векторов и их скалярное произведение.

    24. Заданы два массива разных размеров. Объединить их в один массив, включив второй массив между k-ым и (k + 1)-ым элементами первого (k задано).

    25. Вычесть из положительных элементов данного массива элемент с номером k1 а к отрицательным элементам прибавить элемент с номером k2. Нулевые элементы заменить 1. Номера k1 и k2 вводятся с клавиатуры.

    26. К четным элементам целочисленного массива прибавить данное число а, а из элементов с четными номерами вычесть данное число b.

    27. Дан первый член геометрической прогрессии и ее знаменатель. Сформировать одномерный массив, элементами которого служат первые n членов этой прогрессии.

    28. Вставить одно и то же число, введенное с клавиатуры, перед каждым отрицательным элементом заданного целочисленного массива.

29. Дан массив четного размера. Поменять местами его половины следующим образом: первый элемент - с последним, второй - с предпоследним элементом и т.д.

Содержание отчета и его форма. Отчет по лабораторной работе должен состоять из:

1. Названия лабораторной работы.

2. Цели и содержания лабораторной работы.

3. Ответов на контрольные вопросы лабораторной работы.

4. Формулировки индивидуальных заданий и порядка их выполнения.

Отчет о выполнении лабораторной работы в письменном виде сдается преподавателю.

Вопросы для защиты работы

  1. Что является указателем в языке C++?

  2. Какие виды указателей существуют в языке C++?

  3. Каким образом осуществляется инициализация указателя в языке C++?

  4. Какие существуют способы инициализации указателя?

  5. Время жизни динамических переменных.

  6. С помощью какой языковой конструкции выделяется память под динамический массив в языке C++?

  7. Какие операции можно выполнять с указателями?

  8. Что является ссылкой в языке C++?

  9. Формат объявления ссылки.

  10. К чему приводит операция над ссылкой?

Пример выполнения лабораторной работы №5:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]