Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Лабораторная работа № 7. Указатели и ссылки. Имя массива как указатель. Динамические массивы

Поиск

Цель и порядок работы

Цель работы – изучить работу с указателями, ссылками, получить навыки программирования с использованием динамических массивов.

 

Порядок выполнения работы:

- ознакомиться с описанием лабораторной работы;

- получить задание у преподавателя, согласно своему варианту;

- написать программу и отладить ее на ЭВМ;

- оформить отчет.

Контрольные вопросы

1. Что такое указатель?

2. Как определить указатель?

3. Как проинициализировать указатель?

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

5. Как обратиться к переменной, адресуемой указателем?

6. Как получить адрес объекта?

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

8. Что такое ссылка?

9. Как объявить ссылку?

10. Как создать переменную в динамической памяти?

11. Как удалить переменную из динамической памяти?

12. На что следует обращать особое внимание при работе с динамической памятью?

13. Какие способы обращения к элементам массива вы знаете?

14. Что такое динамический массив и для чего они применяются?

15. Как создать динамический массив?

Варианты заданий

Указатели

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

1) Вычислить площадь треугольника по стороне и высоте.

2) Вычислить площадь окружности по заданному радиусу.

3) Даны значения a и b, найти их среднее арифметическое, среднегеометрическое.

4) Вычислить y = tg(x)+5x3-4x2.

5) Вычислить площадь квадрата.

6) Вычислить высоту треугольника, зная две стороны треугольника и угол между ними.

7) Вычислить y = |x-cos(x)|.

8) Ввести сторону квадрата a. Вычислить площадь вписанной окружности.

9) Задается длина окружности. Найти площадь круга, ограниченного этой окружностью.

10) Вычислить углы треугольника, зная его стороны.

11) Вычислить площадь трапеции.

12) Вычислить y = cos|x3-x2|.

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

14) Вычислить корень квадратный от (x5-x4+|x3|).

15) Вычислить корень квадратный от (sin(x)+cos(x)).

16) Вычислить объем цилиндра, зная радиус основания и высоту.

17) Вычислить объем конуса.

18) Определить время, через которое встретятся два тела, равноускоренно движущиеся друг к другу. Известны: v1 и v2 - начальные скорости, a1 и a2 - ускорения, s - расстояние между ними.

 

Имя массива как указатель

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

1. Дан одномерный массив, состоящий из N вещественных элементов.

1.1. Ввести массив с клавиатуры.

1.2. Найти максимальный элемент.

1.3. Вычислить среднеарифметическое положительных элементов массива.

1.4. Вывести отрицательные элементы на экран в обратном порядке.

2. Дан одномерный массив, состоящий из N вещественных элементов.

2.1. Заполнить массив случайными числами.

2.2. Найти минимальный положительный элемент.

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

2.4. Вывести ненулевые элементы на экран в обратном порядке.

3. Дан одномерный массив, состоящий из N целочисленных элементов.

3.1. Ввести массив с клавиатуры.

3.2. Найти максимальный отрицательный элемент.

3.3. Вычислить сумму отрицательных элементов массива.

3.4. Вывести положительные элементы на экран.

4. Дан одномерный массив, состоящий из N целочисленных элементов.

4.1. Заполнить массив случайными числами.

4.2. Найти максимальный положительный элемент.

4.3. Вычислить сумму элементов массива.

4.4. Вывести ненулевые элементы на экран в обратном порядке.

5. Дан одномерный массив, состоящий из N вещественных элементов.

5.1. Ввести массив с клавиатуры.

5.2. Найти максимальный элемент.

5.3. Вычислить среднеарифметическое отрицательных элементов массива.

5.4. Вывести массив на экран в обратном порядке.

6. Дан одномерный массив, состоящий из N вещественных элементов.

6.1. Заполнить массив случайными числами.

6.2. Найти минимальный элемент.

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

6.4. Вывести положительные элементы на экран в обратном порядке.

7. Дан одномерный массив, состоящий из N целочисленных элементов.

7.1. Ввести массив с клавиатуры.

7.2. Найти минимальный положительный элемент.

7.3. Вычислить сумму положительных элементов массива, кратных 3.

7.4. Вывести не нулевые элементы на экран.

8. Дан одномерный массив, состоящий из N целочисленных элементов.

8.1. Заполнить массив случайными числами.

8.2. Найти максимальный положительный элемент.

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

8.4. Вывести положительные элементы на экран.

9. Дан одномерный массив, состоящий из N вещественных элементов.

9.1. Ввести массив с клавиатуры.

9.2. Найти максимальный элемент.

9.3. Вычислить сумму четных элементов массива.

9.4. Вывести отрицательные элементы на экран в обратном порядке.

10. Дан одномерный массив, состоящий из N вещественных элементов.

10.1. Заполнить массив случайными числами.

10.2. Найти минимальный отрицательный элемент.

10.3. Вычислить среднеарифметическое положительных элементов массива.

10.4. Вывести положительные элементы на экран.

11. Дан одномерный массив, состоящий из N целочисленных элементов.

11.1. Ввести массив с клавиатуры.

11.2. Найти максимальный элемент.

11.3. Вычислить среднеарифметическое элементов массива.

11.4. Вывести массив на экран в обратном порядке.

12. Дан одномерный массив, состоящий из N целочисленных элементов.

12.1. Заполнить массив случайными числами.

12.2. Найти минимальный элемент.

12.3. Вычислить сумму элементов массива.

12.4. Вывести положительные элементы на экран.

13. Дан одномерный массив, состоящий из N целочисленных элементов.

13.1. Ввести массив с клавиатуры.

13.2. Найти максимальный отрицательный элемент.

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

13.4. Вывести ненулевые элементы на экран в обратном порядке.

14. Дан одномерный массив, состоящий из N целочисленных элементов.

14.1. Заполнить массив случайными числами.

14.2. Найти максимальный элемент.

14.3. Вычислить среднеарифметическое нечетных элементов массива.

14.4. Вывести отрицательные элементы на экран.

15. Дан одномерный массив, состоящий из N вещественных элементов.

15.1. Ввести массив с клавиатуры.

15.2. Найти минимальный положительный элемент.

15.3. Вычислить сумму четных элементов массива.

15.4. Вывести массив на экран в обратном порядке.

16. Дан одномерный массив, состоящий из N вещественных элементов.

16.1. Заполнить массив случайными числами.

16.2. Найти минимальный отрицательный элемент.

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

16.4. Вывести отрицательные элементы на экран в обратном порядке.

17. Дан одномерный массив, состоящий из N целочисленных элементов.

17.1. Ввести массив с клавиатуры.

17.2. Найти максимальный отрицательный элемент.

17.3. Вычислить среднеарифметическое четных элементов массива.

17.4. Вывести ненулевые элементы на экран в обратном порядке.

18. Дан одномерный массив, состоящий из N целочисленных элементов.

18.1. Заполнить массив случайными числами.

18.2. Найти минимальный элемент.

18.3. Вычислить сумму положительных нечетных элементов массива.

18.4. Вывести положительные элементы на экран.

Содержание отчета

1. Титульный лист.

2. Наименование и цель работы.

3. Краткое теоретическое описание.

4. Задание на лабораторную работу.

5. Листинг программы.

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

 


Функции

Краткая теория

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

Описание функций

Объявление функции (прототип, заголовок, сигнатура) задает ее имя, тип возвращаемого значения и список передаваемых параметров. Формат простейшего заголовка (прототипа) функции:

 

[ класс ] тип имя ([ список_формальных_параметров ]);

 

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

 

[ класс ] тип имя ([ список_формальных_параметров ])

{

Тело функции

}

 

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

Рассмотрим составные части определения.

1. С помощью необязательного модификатора класс можно явно задать область видимости функции, используя ключевые слова extern и static:

- extern – глобальная видимость во всех модулях программы (по умолчанию);

- static – видимость только в пределах модуля, в котором определена функция.

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

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

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

 

имя ([ список_фактических_параметров ]);

 

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

Пример функции, возвращающей сумму двух целых величин:

#include <iostream>

using namespace std;

int sum(int а, int b); // объявление функции

 

void main()

{

int a = 2, b = 3, c, d;

c = sum(a, b); // вызов функции

cin >> d;

cout << sum(c, d) << endl; // вызов функции

a = 3*sum(c, d) - b; // вызов функции

cout << a << endl;

return;

}

 

int sum(int a, int b) // определение функции

{

return (a + b);

}

 

Объявление и определение функции может совпадать, но перед вызовом компилятор должен знать прототип функции, поэтому одновременное объявление и определение должно следовать перед вызовом функции.

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

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

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

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

Пример функции нахождения максимума двух чисел:

 

#include <iostream>

using namespace std;

int max(int x, int y) // одновременно объявление и определение функции

{

if (x > y)

return x;

else

return y;

}

 

void main()

{

int a = 2, b = 3, c, d;

c = max(a, b); // вызов функции

cout << c << endl;

cin >> d;

cout << max(c, d) << endl; // вызов функции

return;

}

 

Параметры функции

Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями.

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

Существует два основных способа передачи параметров в функцию: по значению и по адресу.

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

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

 

#include <iostream>

using namespace std;

void f(int a, int* b, int& c)

{

a++;

(*b)++;

c++;

}

void main()

{

int a = 1, b = 1, c = 1;

cout << "a b c" << endl;

cout << a << ' ' << b << ' ' << c << endl;

f(a, &b, c);

cout << a << ' ' << b << ' ' << c << endl;

return;

}

Результат работы программы:

 

a b c

1 1 1

1 2 2

 

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

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

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

Если требуется запретить изменение параметра внутри функции, используется модификатор const:

int f(const char *);

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

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

 

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

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

Составить программу вычисления значения функции

 

#include <iostream>

#include <iomanip>

#include <math.h>

using namespace std;

float ax(float a, float x, float eps = 0.001f) // float eps = 0.001f - параметр со значением по умолчанию

{

float xn = 1, y, y0;

unsigned int n = 1, nf = 1;

y = 1;

do

{

nf *= n;

xn *= x*log(a);

y0 = y;

y += xn/nf;

n++;

} while (abs(y - y0) > eps);

return y;

}

 

void main()

{

setlocale(LC_ALL, "Russian");

const float eps = 0.1f;

float a, x, y;

cout << "Введите a, x:" << endl;

cin >> a >> x;

//передаем значение точности вычислений при вызове

y = ax(a, x, eps);

cout <<\

"Результат вычисления с точностью 0.1 (передается явно): "\

<< setw(8) << setprecision(5) << y <<endl;

 

//значение esp опускается

//внутри функции используется значение точности вычислений по умолчанию

y = ax(a, x);

cout <<\

"Результат вычисления с точностью 0.001 (по умолчанию): "\

<< setw(8) << setprecision(5) << y <<endl;

 

//вычисляем при помощи стандартных функций

y = pow(a, x);

cout <<\

"Результат вычисления с использованием стандартных функций: "\

<< setw(8) << setprecision(5) << y <<endl;

}

 

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

 

Введите a, x:

2.5

Результат вычисления с точностью 0.1 (передается явно): 15.591

Результат вычисления с точностью 0.001 (по умолчанию): 15.625

Результат вычисления с использованием стандартных функций: 15.625

 

 

Передача массивов в функцию

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

Рассмотрим пример функции, формирующей вектор, состоящий из максимальных элементов двух исходных векторов.

 

#include <iostream>

using namespace std;

//функция формирует вектор(одномерный массив),

//состоящий из максимальных элементов исходных массивов

//массивы передаются через указатели

void max_vect(int n, int *x, int *y, int *z)

{

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

z[i] = (x[i]>y[i])?x[i]:y[i];

}

 

void main()

{

setlocale(LC_ALL, "Russian");

 

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

const int N=7;

 

int a[N]={1,4,3,-1,5,6,1};

int b[N]={7,6,-2,4,3,2,4};

int c[N];

 

//получим вектор, состоящий из максимальных элементов

//исходных массивов

max_vect(N, a, b, c);

//выведем на экран элементы массива

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

cout << "\t" << c[i];

}

 

Результат:

 

7 6 3 4 5 6 4

 

 

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

Рассмотрим пример нахождения суммы элементов двумерного массива.

 

#include <iostream>

using namespace std;

int sum(int *x, const int n, const int m)

{

int s = 0;

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

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

//здесь производится пересчет индекса массива:

//перемещение на одну строчку вниз

//эквивалентно прибавлению длинны строки

//поэтому x[i*m + j] и x[i][j] будут определять

//один и тот же элемент

s += x[i*m + j];

return s;

}

 

void main()

{

setlocale(LC_ALL, "Russian");

int a[2][2]={{1,2},{3,4}};

//имя массива a напрямую передавать нельзя из-за несоответствия типов

//поэтому используется конструкция &a[0][0]

cout << sum(&a[0][0], 2, 2);

}

 

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

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

#include <iostream>

#include <stdlib.h>

using namespace std;

int sum(int **x, const int n, const int m)

{

int s = 0;

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

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

s += x[i][j];

return s;

}

 

void main()

{

setlocale(LC_ALL, "Russian");

const int N=4, M=3;

//создаем одномерный массив указателей размером N элементов

//(то что это массив указывают квадратные скобки, а то что указателей-*)

int **a = new int* [N];

 

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

//динамически создаем одномерный массив размерностью M

//и присваиваем его адрес элементу массива указателей

a[i] = new int[M];

//заполним массив случайными числами и выведем его на экран

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

{

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

{

//путем получения остатка (%) зададим числа в диапазоне от 0 до 9

a[i][j] = rand()%10;

//здесь по индексу i выбирается указатель на одномерный массив

//а индексу j - элемент в этом массиве

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

}

cout << endl;

}

cout << sum(a, N, M);

//освобождаем выделенную память в обратном порядке

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

//сначала удаляем одномерные массивы

delete [] (a[i]);

//а затем и сам массив указателей

delete [] a;

}

Указатели на функции

Начнем с краткого обзора простых указателей на функции. В С, и С++ в частности, указатель на функцию с именем my_func_ptr, указывающий на функцию, принимающую в качестве аргументов int и char*, и возвращающую float, объявляется так:

float (*my_func_ptr)(int, char*);// Для большей удобочитаемости, я очень рекомендую использовать typedef.// Особенно можно запутаться, когда указатель на функцию является аргументом// функции.// Тогда бы объявление выглядело так:typedef float (*MyFuncPtrType)(int, char*);MyFuncPtrType my_func_ptr

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

my_func_ptr = some_func;

Для вызова функции через указатель:

(*my_func_ptr)(7, "Arbitrary string");

Наиболее распространенное применение указателей на функции в С – это использование библиотечных функций, таких как qsort, и обратных (callback) функций в Windows. Кроме того, есть еще много вариантов их применения. Реализация указателей на функции проста: это всего лишь «указатели на код», в них содержится начальный адрес участка ассемблерного кода.




Поделиться:


Последнее изменение этой страницы: 2016-12-11; просмотров: 608; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.16.50.1 (0.009 с.)