Переменные-указатели и операции над указателями. Лекция 6 по алгоритмизации и программированию

Содержание

Слайд 2

1) Что будет выведено на экран? 2) Что надо исправить для

1) Что будет выведено на экран? 2) Что надо исправить для

получения правильного результата для всех целых чисел?
Слайд 3

Указатели Указатели – это переменные, предназначены для хранения адресов памяти.

Указатели
Указатели – это переменные, предназначены для хранения адресов памяти.

Слайд 4

Объявление указателя: тип* имя; Память под переменные-указатели выделяется только тогда, когда

Объявление указателя: тип* имя;
Память под переменные-указатели выделяется только тогда, когда им

присваивается какое-либо значение.
Примеры
int* i;
double *f, *ff;
char* c;
int** a; // указатель на указатель
const int* pci;
Слайд 5

Указатель можно сразу проинициализировать: Примеры: int i;//целая переменная; const int ci;//целая

Указатель можно сразу проинициализировать:
Примеры:
int i;//целая переменная;
const int ci;//целая константа
//указатель на целую

переменную
int* pi=&i;
//указатель на целую константу
const int* pci=&ci;
//указатель-константа на переменную целого типа
int* const cpi=&i;
//указатель-константа на целую константу
const int* const cpc=&ci;
Слайд 6

Способы инициализации указателя с помощью операции получения адреса int a=5; int*

Способы инициализации указателя

с помощью операции получения адреса
int a=5;
int* p=&a;// или

int p(&a);
с помощью проинициализированного указателя
int* r=p;
адрес присваивается в явном виде
char* cp=(char*)0х В800 0000;
где 0х В800 0000 – шестнадцатеричная константа,
(char*) – операция приведения типа.
присваивание пустого значения:
int* N=NULL;
int* R=0;
Слайд 7

Пример: int A; // выделяется память int* B; // память не

Пример:
int A; // выделяется память
int* B; // память не выделяется
...
A=10;
B=&A; //

&A – взятие адреса. Выделяется память под В и в нее записывается адрес области память, выделенной под А
Слайд 8

Слайд 9

Обращаться к содержимому области памяти можно через переменные-указатели, для этого используется

Обращаться к содержимому области памяти можно через переменные-указатели, для этого используется

операция разыменования. Для этого ставится «*» перед именем переменной-указателем:
Слайд 10

int **D; // значением этой переменной является значение переменной типа указатель D=&B;

int **D; // значением этой переменной является значение переменной типа указатель
D=&B;

Слайд 11

Действия над указателями Описание: int *p1, *p2, i;

Действия над указателями

Описание: int *p1, *p2, i;

Слайд 12

Слайд 13

Слайд 14

Передача параметров по значению вычисляются значения выражений, стоящие на месте фактических

Передача параметров по значению

вычисляются значения выражений, стоящие на месте фактических

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

//функция возвращает площадь треугольника, заданного длинами сторон а,b,c double square (double

//функция возвращает площадь треугольника, заданного длинами сторон а,b,c
double square (double

a, double b, double c)
{
double s, p=(a+b+c)/2;
return s=sqrt(p*(p-a)*(p-b)*(p-c));
}

//вызов функции
double s1=square(2.5,2,1);

a

c

s

p

b

Стек функции square

s1

Стек функции main

Слайд 16

//вызов функции double a=2.5,b=2,c=1; double s1=square (a, b, c); 2.5 1

//вызов функции
double a=2.5,b=2,c=1;
double s1=square (a, b, c);

2.5

1

s

p

b

Стек функции square

s1

Стек функции main

a

2

2.5

2

1

Таким

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

a

b

c

a

b

c

Слайд 17

Пример. Найти наибольший общий делитель (НОД) для значений x, y, x+y.

Пример. Найти наибольший общий делитель (НОД) для значений x, y, x+y.


#include
using namespace std;
int evklid(int m,int n) //данные передаются по значению
{
while (m!=n)
if (m>n) m=m-n;
else n=n-m;
return (m);
}
int main ()
{
int x,y,nod;
cin>>x>>y;
nod=evklid(evklid(x,y),x+y);
cout<<"NOD="<system ("pause");
return 0;
}
Слайд 18

void Change (int a,int b) //передача по значению { int r=a;

void Change (int a,int b) //передача по значению
{
int r=a;
a=b;
b=r;
}
//вызов функции
int x=1,y=5;
Change(x,y);
cout<<”x=”<

y=”<

1

5

а

b

r

1

5

x

y

Слайд 19

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

Передача параметров по адресу

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

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

void Change (int* a, int* b) //передача по адресу { int

void Change (int* a, int* b) //передача по адресу
{
int r=*a;
*a=*b;
*b=r;
}
//вызов функции
int x=1,y=5;
Change(&x,&y);
cout<<”x=”<

y=”<

&x

&y

а

b

r

1

5

x

y

Слайд 21

void Change (int& a, int& b) //передача по адресу (ссылке) {

void Change (int& a, int& b) //передача по адресу (ссылке)
{
int r=a;
a=b;
b=r;
}
//вызов функции
int

x=1,y=5;
cout<<”x=”<Change(x,y);
cout<<”x=”<

&x

&y

а

b

r

1

5

x

y

Слайд 22

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

Локальные переменные

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

для них выделяется в стеке, поэтому после окончания работы функции они удаляются из памяти.
Нельзя возвращать указатель на локальную переменную, т. к. память, выделенная такой переменной, будет освобождаться.
int* f()
{
int a;

return &a;// ОШИБКА!
}
Слайд 23

Глобальные переменные Глобальные переменные – это переменные, описанные вне функций. Они

Глобальные переменные

Глобальные переменные – это переменные, описанные вне функций. Они видны

во всех функциях, где нет локальных переменных с такими именами.
Слайд 24

Пример. Написать программу, запрашивающую N целых чисел и выводящих в текстовый

Пример. Написать программу, запрашивающую N целых чисел и выводящих в текстовый

файл все цифры этих чисел через запятую в обратном порядке.
Слайд 25

Подставляемые (inline) функции Спецификатор inline определяет для функции так называемое внутреннее

Подставляемые (inline) функции

Спецификатор inline определяет для функции так называемое внутреннее

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

/* функция возвращает расстояние от точки с координатами (x1,y1) (по умолчанию

/* функция возвращает расстояние от точки с координатами (x1,y1) (по умолчанию

центр координат) до точки с координатами (x2,y2)*/
inline float Line(float x1,float y1,
float x2=0,float y2=0)
{
return sqrt(pow(x1-x2)+pow(y1-y2,2));
}
Слайд 27

Рекурсия Рекурсией называется ситуация, когда какой-то алгоритм вызывает себя прямо (прямая

Рекурсия

Рекурсией называется ситуация, когда какой-то алгоритм вызывает себя прямо (прямая

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

Рекурсия — это способ определения множества объектов через само это множество

Рекурсия — это способ определения множества объектов через само это множество

на основе заданных простых базовых случаев.
Слайд 29

Задачи Вычислить факториал (n!), используя рекурсию. Вычислить степень, используя рекурсию. Вычислить n-ое число Фиббоначи

Задачи

Вычислить факториал (n!), используя рекурсию.
Вычислить степень, используя рекурсию.
Вычислить n-ое число Фиббоначи

Слайд 30

Задача 1. Вычислить факториал (n!), используя рекурсию. Исходные данные: n Результат:

Задача 1. Вычислить факториал (n!), используя рекурсию.
Исходные данные: n
Результат: n!
Рассмотрим эту

задачу на примере вычисления факториала для n=5. Более простой задачей является вычисление факториала для n=4. Тогда вычисление факториала для n=5 можно записать следующим образом:
5!=4!*5.
Аналогично:
4!=3!*4;
3!=2!*3;
2!=1!*2 ;
1!=0!*1
Тривиальная (простая) задача:
0!=1.
Можно построить следующую математическую модель:
Слайд 31

#include int fact(int n) { if (n==0)return 1; //тривиальная задача return

#include
int fact(int n)
{
if (n==0)return 1; //тривиальная задача
return (n*fact(n-1));
}
void main()
{
cout<<"n?";
int k;
cin>>k;

//вводим число для вычисления факториала
//вычисление и вывод результата
cout<}
Слайд 32

Слайд 33

Задача 2. Вычислить степень, используя рекурсию. Исходные данные: x,n Результат: xn Математическая модель:

Задача 2. Вычислить степень, используя рекурсию.
Исходные данные: x,n
Результат: xn
Математическая модель:

Слайд 34

#include int pow( int x,int n) { if(n==0)return 1;//тривиальная задача return(x*pow(x,n-1));

#include
int pow( int x,int n)
{
if(n==0)return 1;//тривиальная задача
return(x*pow(x,n-1));
}
void main()
{
int x,k;
cout<<"n?";
cin>>x;

//вводим число
cin>>k; //вводим степень
//вычисление и вывод результата
cout<}
Слайд 35

Вычисление суммы цифр числа int sumDig ( int n ) {

Вычисление суммы цифр числа

int sumDig ( int n )
{
int

sum;
sum = n %10;
if ( n >= 10 )
sum += sumDig ( n / 10 );
return sum;
}

рекурсивный вызов

sumDig( 1234 )

4 + sumDig( 123 )

4 + 3 + sumDig( 12 )

4 + 3 + 2 + sumDig( 1 )

4 + 3 + 2 + 1

последняя цифра

Слайд 36

Алгоритм Евклида Алгоритм Евклида. Чтобы найти НОД двух натуральных чисел, нужно

Алгоритм Евклида

Алгоритм Евклида. Чтобы найти НОД двух натуральных чисел, нужно вычитать

из большего числа меньшее до тех пор, пока меньшее не станет равно нулю. Тогда второе число и есть НОД исходных чисел.

int NOD ( int a, int b )
{
if ( a == 0 || b == 0 )
if ( a > b )
return NOD( a - b, b );
else return NOD( a, b – a );
}

return a + b;

рекурсивные вызовы

условие окончания рекурсии

Слайд 37

Как работает рекурсия? int Fact ( int N ) { int

Как работает рекурсия?

int Fact ( int N )
{
int F;

cout << "-> N=" << N << endl;
if ( N == 1 )
F = 1;
else F = N * Fact(N - 1);
cout << "<- N=" << N << endl;
return F;
}

-> N = 3
-> N = 2
-> N = 1
<- N = 1
<- N = 2
<- N = 3

Факториал:

Слайд 38

Стек Стек – область памяти, в которой хранятся локальные переменный и

Стек

Стек – область памяти, в которой хранятся локальные переменный и адреса

возврата.

Fact(3)

Fact(2)

Fact(1)

значение параметра

адрес возврата

локальная переменная

Слайд 39

Рекурсия – «за» и «против» с каждым новым вызовом расходуется память

Рекурсия – «за» и «против»

с каждым новым вызовом расходуется память в

стеке (возможно переполнение стека)
затраты на выполнение служебных операций при рекурсивном вызове

программа становится более короткой и понятной

возможно переполнение стека
замедление работы

int Fact ( int N )
{
int F;
F = 1;
for(i = 2;i <= N;i++)
F = F * i;
return F;
}

итерационный алгоритм

Слайд 40