Указатели и массивы. Указатели и параметры функций. Сложные описания с указателями. (Лекция 11)

Содержание

Слайд 2

План Лекция 11 План Указатели Указатели и массивы Динамические массивы Указатели

План

Лекция 11

План
Указатели
Указатели и массивы
Динамические массивы Указатели на функции
Указатели и

параметры функций
Сложные описания с указателями
Слайд 3

Несколько заданий для самопроверки

Несколько заданий для самопроверки

Слайд 4

Три задания для самопроверки Задание 1 Что выведет программа? #include void

Три задания для самопроверки

Задание 1

Что выведет программа?

#include
void main() {
   int

i, M[3]={1};
   for(i=0;i<3;i++)
      printf(”%d”, M[i]);
}

100

Слайд 5

Три задания для самопроверки Задание 2 Какие из следующих описаний массивов

Три задания для самопроверки

Задание 2

Какие из следующих описаний массивов являются корректными?

int

A[3]={1,3,5};
int B[3]={11,22};
int C[3]={0,1,2,3};
int D[1..3];
int M[3];
int N[]={10,20,30,40};
int P[3,5];
int Q[2][2]={{0},{0}};

Да

Да

Нет

Нет

Да

Да

Нет

Да

Слайд 6

Три задания для самопроверки Задание 3 В каком порядке в памяти

Три задания для самопроверки

Задание 3

В каком порядке в памяти располагаются элементы

описанного следующим образом массива?

int M[3][2];


a) A[1][1] A[1][2] A[2][1] A[2][2] A[3][1] A[3][2]
b) A[0][0] A[1][0] A[2][0] A[0][1] A[1][1] A[2][1]
c) A[0][0] A[0][1] A[1][0] A[1][1] A[2][0] A[2][1]
d) A[0][0] A[0][1] A[1][0] A[0][2] A[2][0] A[2][3]

Так

Слайд 7

Указатели Адреса переменных Что такое указатель? Значение NULL Операции над указателями Нетипизированные указатели Указатели и const

Указатели

Адреса переменных
Что такое указатель?
Значение NULL
Операции над указателями
Нетипизированные указатели
Указатели и const

Слайд 8

Организация курса Что такое адрес переменной? Оперативная память организована как последовательность

Организация курса

Что такое адрес переменной?

Оперативная память организована как последовательность ячеек (байт)
Каждая

ячейка имеет собственный адрес (порядковый номер)
Адрес – целое число, чаще записываемое в шестнадцатеричной системе счисления

0x2c4b1

Память:

Адрес:

0x2c4b2

0x2c4b3

0x2c4b4

0x2c4b5

0x2c4b6

0x2c4b7

Слайд 9

Организация курса Что такое адрес переменной? Каждая переменная размещается в последовательных

Организация курса

Что такое адрес переменной?

Каждая переменная размещается в последовательных ячейках (количество

ячеек зависит от типа переменной)
Адрес переменной – адрес первой из этих ячеек


Память:

Адрес:

x

y

a

0x2c4b1

0x2c4b2

0x2c4b3

0x2c4b4

0x2c4b5

0x2c4b6

0x2c4b7

Слайд 10

Организация курса Как получить адрес переменной? Адрес переменной можно получить с

Организация курса

Как получить адрес переменной?

Адрес переменной можно получить с помощью операции

&
Например, &x даст адрес x:

10

127

20031

Память:

Адрес:

x

y

a

0x2c4b1

0x2c4b2

0x2c4b3

0x2c4b4

0x2c4b5

0x2c4b6

0x2c4b7

...
printf(“x=%d, &x=%p”, x, &x); ...

x=10, &x=2c4b1

Слайд 11

Организация курса Что такое указатель? Указатель – переменная, хранящая адрес 10

Организация курса

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

Указатель – переменная, хранящая адрес

10


Память:

Адрес:


x

p

0x2c4b1

Операция разадресации *

– обратная к операции &

0x2c4b1

0x2c4b2

0x2c4b8

0x2c4b9

0x2c4ba

0x2c4bb

int x; /*целая переменная*/ int *px; /*указатель*/ px = &x; /*присвоить адрес*/

int x=10, y; int *px; px = &x; /*взять адрес*/ y = *px; /*взять значение по адресу px, y=10*/
*px = 20; /* <=> x=20 */

Слайд 12

Организация курса Значение NULL Помимо адресов, указатель может принимать специальное значение

Организация курса

Значение NULL

Помимо адресов, указатель может принимать специальное значение NULL, обозначающее

недействительный адрес
NULL – макроконстанта
NULL чаще всего (но не всегда!) равен 0
Разадресовывать указатель со значением NULL небезопасно!

int *px; /*указатель*/
px = NULL; /*присвоить NULL*/

Слайд 13

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

Организация курса

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

Указатель может быть инициализирован
Указателю можно присваивать значение
Указатель можно

сравнивать: < > <= >= == != (т.е. вычислять отношения адресов)

int y, *px=NULL, *py=&y, *pz=py; /* инициализация */

int x=10, y=20, *px, *py;
px=&x;
py=px;

int x=10, y=20, *px=&x, *py=&y;
if( px == py ) ...

Слайд 14

Организация курса Операции над указателями Указатель может складываться с целым числом

Организация курса

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

Указатель может складываться с целым числом N. Результат

сложения – адрес, смещенный на N компонент соответствующего типа относительно исходного

short x=10, *px=&x; /* инициализация */

Слайд 15

Организация курса Операции над указателями Указатель может складываться с целым числом

Организация курса

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

Указатель может складываться с целым числом N. Результат

сложения – адрес, смещенный на N компонент соответствующего типа относительно исходного

short x=10, *px=&x; /* инициализация */
px=px+1;

Слайд 16

Организация курса Операции над указателями Указатель может складываться с целым числом

Организация курса

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

Указатель может складываться с целым числом N. Результат

сложения – адрес, смещенный на N компонент соответствующего типа относительно исходного

short x=10, *px=&x; /* инициализация */
px=px+1;
px=px-2;

0


10

345


x

0x2c4b1

0x2c4b2

0x2c4b3

0x2c4b4

0x2c4b5

0x2c4b6

px

0x2c4b1


Слайд 17

Организация курса Операции над указателями Указатель может складываться с целым числом

Организация курса

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

Указатель может складываться с целым числом N. Результат

сложения – адрес, смещенный на N компонент соответствующего типа относительно исходного

short x=10, *px=&x; /* инициализация */
px=px+1;
px=px-2;
px++;

0


10

345


x

0x2c4b1

0x2c4b2

0x2c4b3

0x2c4b4

0x2c4b5

0x2c4b6

px

0x2c4b3


Слайд 18

Организация курса Операции над указателями Указатель может складываться с целым числом

Организация курса

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

Указатель может складываться с целым числом N. Результат

сложения – адрес, смещенный на N компонент соответствующего типа относительно исходного

short x=10, *px=&x; /* инициализация */
px=px+1;
px=px-2;
px++; *(px+1)+=1;

0


10

346


x

0x2c4b1

0x2c4b2

0x2c4b3

0x2c4b4

0x2c4b5

0x2c4b6

px

0x2c4b3


Слайд 19

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

Организация курса

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

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

смещению с учетом типа указателя

short *px=0x2c4b1, *py=0x2c4b5, d;
d = py-px; /* 2 */

Слайд 20

Организация курса Нетипизированный указатель Типизированные указатели (int *, char *, double

Организация курса

Нетипизированный указатель

Типизированные указатели (int *, char *, double *, …)

неявно задают длину фрагмента памяти (4,1,8, …), начинающегося с адреса, хранимого указателем
Длина важна при разадресации и адресной арифметике
Однако иногда приходится использовать указатели, не подразумевая длины адресуемого фрагмента памяти – void *
Разадресация указателя void * невозможна!
Указатель void * совместим по типу со всеми типизированными указателями

int i=10, *pi=&i;
double d=3.14, *pd=&d;
void *p;
p=pi; /* Ok */
p=pd; /* Ok */
*p=*p+1; /* Ошибка! */

Слайд 21

Организация курса Указатели и const Два способа описания константного указателя Неизменяемый

Организация курса

Указатели и const

Два способа описания константного указателя
Неизменяемый указатель
Синтаксис: TYPE *

const ptrName = &aTYPEVar;
Переменная-указатель – константа (не может изменяться)
Данные, адресуемые указателем – изменяемые

int a=42, b=42;
int* const ptr=&a;
*ptr=1; /* Ok */
ptr=&b /* Ошибка! */

Слайд 22

Организация курса Указатели и const Два способа описания константного указателя Указатель

Организация курса

Указатели и const

Два способа описания константного указателя
Указатель на неизменяемые данные
Синтаксис:

const TYPE * ptrName = &aTYPEVar;
Переменная-указатель – может изменяться
Данные, адресуемые указателем – неизменяемые

int a=42, b=42;
const int *ptr=&a;
*ptr=1; /* Ошибка! */
ptr=&b /* Оk */

Слайд 23

Указатели и массивы Указатели и массивы Массивы как параметры функций Указатели на многомерные массивы

Указатели и массивы

Указатели и массивы
Массивы как параметры функций
Указатели на многомерные массивы

Слайд 24

Указатели и массивы Указатели и массивы Указатели и массивы очень тесно

Указатели и массивы

Указатели и массивы

Указатели и массивы очень тесно связаны в

языке Си
Имя массива – константный указатель на 0-й элемент массива
a[i] == *(a+i)

short a[100];
short *ps;
ps = &a[0];

short a[100];
short *ps;
ps = a;

<=>

Слайд 25

Указатели и массивы Указатели и массивы a[i] == *(a+i) == *(i+a)

Указатели и массивы

Указатели и массивы

a[i] == *(a+i) == *(i+a) == i[a]
a[2]

== *(a+2) == *(2+a) == 2[a]

short a[100];
for (i=0;i<100;i++)
scanf(“%h”,&a[i]);

short a[100];
for (i=0;i<100;i++)
scanf(“%h”,a+i);

<=>

short a[100];
short *ps;
for (ps=a;ps==a+100;ps++)
scanf(“%h”,ps);

<=>

short a[100];
short *ps=a;
for (i=0;i<100;i++)
scanf(“%h”,&ps[i]);

<=>

Слайд 26

Указатели и массивы Указатели и массивы Синонимичные выражения Передача массива в

Указатели и массивы

Указатели и массивы

Синонимичные выражения
Передача массива в функцию как параметра


int a[10];
f(a); /* или */
f(&a[0]);

void f(int *array){
...
}
/*или*/
void f(int array[]){
...
}

Слайд 27

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

Указатели и массивы

Указатели на многомерные массивы

Для вычисления адреса элемента двумерного массива

компилятору нужно «знать» количество столбцов в матрице (т.е. мало знать начальный адрес масива)
Пусть нужно передать в функцию массив int array[3][15], чтобы ее вызов выглядел так: f(array)
Возможны следующие идентичные варианты описания функции f:
f(int x[3][15]) { … }
f(int x[][15]) { … }
f(int (*x)[15]) { … }
Важно: в последнем случае нельзя опустить скобки!
f(int *x[15]) { … } – передается массив из 15 указателей на int, а не указатель на массив из 15 int-ов
Слайд 28

Динамические массивы Динамические массивы Выделение памяти, malloc() Освобождение памяти, free()

Динамические массивы

Динамические массивы
Выделение памяти, malloc()
Освобождение памяти, free()

Слайд 29

Динамические массивы Динамические массивы Часто размер массива заранее не известен, а

Динамические массивы

Динамические массивы

Часто размер массива заранее не известен, а известен лишь

в момент исполнения
Требуется динамически распределять массив в памяти
Стандартная функция malloc() позволяет запросить память у ОС
Для использования malloc() нужно подключать stdlib.h
Полный прототип функции: void *malloc(size_t size);
malloc() возвращает нетипизированный указатель на начальный байт выделенного массива. Для дальнейшего использования обычно указатель преобразуют в типизированный

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

Требуемое количество байт

Слайд 30

Динамические массивы Динамические массивы Шаблон программы, использующей динамический массив #include #include

Динамические массивы

Динамические массивы

Шаблон программы, использующей динамический массив

#include
#include
void main(){
int

*a;
/* Выделение 4 Мб памяти = 1Мб int-ов */
a = (int *)malloc(1024*1024*sizeof(int));
if(a == NULL) {
printf(“Ошибка выделения памяти.\n”);
return;
}
... /* Работа с элементами a[i] */
/* Освобождение памяти */
free(a);
}
Слайд 31

Динамические массивы Динамические массивы Выделенную память необходимо освобождать Стандартная функция free()

Динамические массивы

Динамические массивы

Выделенную память необходимо освобождать
Стандартная функция free() позволяет освободить память

, выделенную malloc()
Блоки памяти освобождаются целиком, т.е. невозможно частичное освобождение (см. также функцию realloc())
Полный прототип функции: void free(void *ptr);

Адрес начала освобождаемого фрагмента памяти

Слайд 32

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

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

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

Слайд 33

Указатели на функции Указатели на функции Укaзатель на функцию содержит адрес

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

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

Укaзатель на функцию содержит адрес тела функции
Как

описывается указатель на функцию?

Тип возвращаемого значения функции

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

Тип параметра функции

Тип параметра функции

float myfun(int a, float b) {
return a+b;
}
...
float (*fptr)(int,float);
fptr = myfun;
...
x=fptr(42,3.14f);
...

Указатель на функцию, воспринимающую параметры типов int и float и возвращающую float

Вызов функции по указателю

Слайд 34

Указатели на функции Указатели на функции: пример int add(int x, int

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

Указатели на функции: пример

int add(int x, int y) {

return x+y; }
int sub(int x, int y) { return x-y; }
int mul(int x, int y) { return x*y; }
int div(int x, int y) { return x/y; }
int evaluate(unsigned int op, int x, int y) {
int (*eval[])(int, int) = { add, sub, mul, div };
if (op>3) {
printf(“Недопустимая операция”);
return 0;
}
return eval[op](x, y);
}
void main() {
printf(“%d\n”, evaluate(3, 42, 3));
}

Операции пронумерованы 0 – add, 1 – sub, 2 – mul, 3 – div

Массив из указателей на функции вида int f(int,int)

Вызов подходящей функции

Слайд 35

Указатели и параметры функций Указатели в параметрах функций

Указатели и параметры функций

Указатели в параметрах функций

Слайд 36

Указатели и параметры фугкций Как передаются параметры через указатель? В функцию

Указатели и параметры фугкций

Как передаются параметры через указатель?

В функцию передается не

значение, а адрес переменной

#include
void swap(int *x, int *y) {
int t;
t = *x; *x = *y; *y = t;
}
void main() {
int a=5, b=10;
swap(&a, &b);
printf(“a=%d, b=%d\n”, a, b);
}

Для доступа к значению переменной используется операция разадресации

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

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

Слайд 37

Указатели и параметры фугкций Как изменить переменную в вызывающей функции? a

Указатели и параметры фугкций

Как изменить переменную в вызывающей функции?

a

b

x

5

10

#include
void swap(int

*x, int *y) {
int t;
t = *x; *x = *y; *y = t;
}
void main() {
int a=5, b=10;
swap(&a, &b);
printf(“a=%d, b=%d\n”, a, b);
}

y

t

x = &a; /*в х – адрес a*/ y = &b; /*в y – адрес a*/ t = *x; /*в t поместить значение, хранящееся по адресу x*/ *X = *y; /*по адресу x записать значение, хранящееся по адресу y*/ *y = t; /*по адресу y записать значение, хранящееся в t*/

Слайд 38

Сложные описания с указателями Сложные объявления Правила чтения сложных объявлений Класс памяти typedef

Сложные описания с указателями

Сложные объявления
Правила чтения сложных объявлений
Класс памяти typedef

Слайд 39

Сложные описания с указателями Сложные описания с указателями char **argv argv:

Сложные описания с указателями

Сложные описания с указателями

char **argv argv: указатель на указатель

на char
int (*x)[13] x: указатель на массив из 13 int-ов
int *x[13] x: массив из 13 указателей на int
void *comp() comp: функция, возвращающая указатель на void
void (*comp)() comp: указатель на функцию, возвращающую void
char (*(*x())())[5] x: функция, возвращающая указатель на массив из 5 указателей на функцию, возвращающую char
char (*(*x[3])())[5] x: массив из 3 указателей на функцию, возвращающую указатель на массив из 5 char-ов
Слайд 40

Сложные описания с указателями Сложные описания с указателями Чтобы понять подобные

Сложные описания с указателями

Сложные описания с указателями

Чтобы понять подобные сложные объявления

используйте следующие три правила:
Начинайте с имени переменной (d или foo в примерах выше)
Заканчивайте на имени типа (double или char выше)
Идите вправо пока можно, затем влево (влево необходимо идти при обнаружении закрывающейся скобки)
Например

double **d[8] /* хмм... */
char *(*(**foo [][8])())[] /* упс! что такое foo? */

Слайд 41

Сложные описания с указателями Сложные описания с указателями Еще пример double

Сложные описания с указателями

Сложные описания с указателями

Еще пример

double **d[8] /* хмм...

*/
char *(*(**foo [][8])())[] /* упс! что такое foo? */
Слайд 42

Сложные описания с указателями Сложные описания с указателями Как можно упростить

Сложные описания с указателями

Сложные описания с указателями

Как можно упростить понимание сложных

объявлений?
Ответ: использовать оператор typedef

double **d[8] /* хмм... */
char *(*(**foo [][8])())[] /* упс! что такое foo? */

typedef float real; /* real – синоним float */
typedef unsigned char byte; /* byte – синоним unsigned char */
typedef int *INTPTR; /* INTPTR – синоним int* */
typedef void (*FUNCPTR)(); /* FUNCPTR – указатель на функцию */ /* с прототипом void f() */
real r=0.0; /* то же, что и float r=0.0 */
byte b=0; /* то же, что и unsigned char b=0 */
INTPTR p; /* то же, что и int *p */
FUNCPTR pf; /* то же, что и void (*pf)() */
FUNCPTR paf[10]; /* массив из указателей на функции void (*pf)() */