Указатели
Лекция 11
Несколько заданий для самопроверки
Задание 1
Задание 2
Задание 3
Указатели
Что такое адрес переменной?
Что такое адрес переменной?
Как получить адрес переменной?
Что такое указатель?
Значение NULL
Операции над указателями
Операции над указателями
Операции над указателями
Операции над указателями
Операции над указателями
Операции над указателями
Операции над указателями
Нетипизированный указатель
Указатели и const
Указатели и const
Указатели и массивы
Указатели и массивы
Указатели и массивы
Указатели и массивы
Указатели на многомерные массивы
Динамические массивы
Динамические массивы
Динамические массивы
Динамические массивы
Указатели на функции
Указатели на функции
Указатели на функции: пример
Указатели и параметры функций
Как передаются параметры через указатель?
Как изменить переменную в вызывающей функции?
Сложные описания с указателями
Сложные описания с указателями
Сложные описания с указателями
Сложные описания с указателями
Сложные описания с указателями
Вопросы?
0.96M
Категория: ПрограммированиеПрограммирование

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

1. Указатели

Алтайский государственный университет
Факультет математики и ИТ
Кафедра информатики
Барнаул 2014

2. Лекция 11

План
Лекция 11
План
Указатели
Указатели
и массивы
Динамические массивы Указатели на функции
Указатели и
параметры функций
Сложные описания
с указателями
2

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

4. Задание 1

Три задания для самопроверки
Задание 1
Что выведет программа?
#include <stdio.h>
void main() {
int i, M[3]={1};
for(i=0;i<3;i++)
printf(”%d”, M[i]);
}
100
4

5. Задание 2

5
Три задания для самопроверки
Задание 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]
6

7. Указатели

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

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

Организация курса
Что такое адрес переменной?
Память:
Адрес:
0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 0x2c4b7
Оперативная память организована как последовательность ячеек
(байт)
Каждая ячейка имеет собственный адрес
(порядковый номер)
Адрес – целое число, чаще записываемое в шестнадцатеричной
системе счисления
8

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

9
Организация курса
Что такое адрес переменной?
x
y
a
Память:
Адрес:
0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 0x2c4b7
Каждая переменная размещается в последовательных ячейках
(количество ячеек зависит от типа переменной)
Адрес переменной – адрес первой из этих ячеек

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

10
Организация курса
Как получить адрес переменной?
x
Память:
Адрес:
10
y
127
a
20031
0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 0x2c4b7
Адрес переменной можно получить с помощью операции &
Например, &x даст адрес x:
...
printf(“x=%d, &x=%p”, x, &x);
...
x=10, &x=2c4b1

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

11
Организация курса
Что такое указатель?
x
Память:
Адрес:
10
0x2c4b1 0x2c4b2
p


Указатель – переменная,
хранящая адрес
0x2c4b1
0x2c4b8 0x2c4b9 0x2c4ba 0x2c4bb
Операция разадресации * –
обратная к операции &
int x;
/*целая переменная*/
int *px; /*указатель*/
int x=10, y;
int *px;
px = &x; /*присвоить адрес*/
px = &x; /*взять адрес*/
y = *px; /*взять значение по
адресу px, y=10*/
*px = 20; /* <=> x=20 */

12. Значение NULL

Организация курса
Значение NULL
Помимо адресов, указатель может принимать
специальное значение NULL, обозначающее
недействительный адрес
int *px; /*указатель*/
px = NULL; /*присвоить NULL*/
NULL – макроконстанта
NULL чаще всего (но не всегда!) равен 0
Разадресовывать указатель со значением NULL
небезопасно!
12

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 ) ...
13

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

14
Организация курса
Операции над указателями
Указатель может складываться с целым числом N. Результат
сложения – адрес, смещенный на N компонент соответствующего
типа относительно исходного
short x=10, *px=&x; /* инициализация */
px
0x2c4b3
x
0

10
345
… 0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 …

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

15
Организация курса
Операции над указателями
Указатель может складываться с целым числом N. Результат
сложения – адрес, смещенный на N компонент соответствующего
типа относительно исходного
short x=10, *px=&x; /* инициализация */
px=px+1;
px
0x2c4b5
x
0

10
345
… 0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 …

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

16
Организация курса
Операции над указателями
Указатель может складываться с целым числом N. Результат
сложения – адрес, смещенный на N компонент соответствующего
типа относительно исходного
short x=10, *px=&x; /* инициализация */
px=px+1;
px=px-2;
px
0x2c4b1
x
0

10
345
… 0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 …

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

17
Организация курса
Операции над указателями
Указатель может складываться с целым числом N. Результат
сложения – адрес, смещенный на N компонент соответствующего
типа относительно исходного
short x=10, *px=&x; /* инициализация */
px=px+1;
px=px-2;
px++;
px
0x2c4b3
x
0

10
345
… 0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 …

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

18
Организация курса
Операции над указателями
Указатель может складываться с целым числом N. Результат
сложения – адрес, смещенный на N компонент соответствующего
типа относительно исходного
short x=10, *px=&x; /* инициализация */
px=px+1;
px=px-2;
px++; *(px+1)+=1;
px
0x2c4b3
x
0

10
346
… 0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 …

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

19
Организация курса
Операции над указателями
Можно вычислять разность однотипных указателей, которая
равна относительному смещению с учетом типа указателя
short *px=0x2c4b1, *py=0x2c4b5, d;
d = py-px; /* 2 */
px
py

0x2c4b1
0x2c4b5
d
0

10
346
… 0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 …

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

Организация курса
Нетипизированный указатель
Типизированные указатели (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; /* Ошибка! */
20

21. Указатели и const

Организация курса
Указатели и const
Два способа описания константного указателя
Неизменяемый указатель
Синтаксис: TYPE * const ptrName = &aTYPEVar;
Переменная-указатель – константа (не может изменяться)
Данные, адресуемые указателем – изменяемые
int a=42, b=42;
int* const ptr=&a;
*ptr=1; /* Ok */
ptr=&b /* Ошибка! */
21

22. Указатели и const

Организация курса
Указатели и const
Два способа описания константного указателя
Указатель на неизменяемые данные
Синтаксис: const TYPE * ptrName = &aTYPEVar;
Переменная-указатель – может изменяться
Данные, адресуемые указателем – неизменяемые
int a=42, b=42;
const int *ptr=&a;
*ptr=1; /* Ошибка! */
ptr=&b /* Оk */
22

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

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

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

24
Указатели и массивы
Указатели и массивы
Указатели и массивы очень тесно связаны в языке Си
Имя массива – константный указатель на 0-й элемент массива
short a[100];
short *ps;
<=>
ps = &a[0];
short a[100];
short *ps;
ps = a;
a[i] == *(a+i)
ps
a+1
a
*(a+1)
*a
*(a+2)
=
=
=
33
a+2
a[0]
a[1]
a[2]

36
15
… 0x2c4b1 0x2c4b2 0x2c4b3 0x2c4b4 0x2c4b5 0x2c4b6 …

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

25
Указатели и массивы
Указатели и массивы
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];
short *ps=a;
for (i=0;i<100;i++)
scanf(“%h”,&ps[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);

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

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-ов
27

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

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

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

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

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

Динамические массивы
Шаблон программы, использующей динамический массив
#include <stdio.h>
#include <stdlib.h>
void main(){
int *a;
/* Выделение 4 Мб памяти = 1Мб int-ов */
a = (int *)malloc(1024*1024*sizeof(int));
if(a == NULL) {
printf(“Ошибка выделения памяти.\n”);
return;
}
... /* Работа с элементами a[i] */
/* Освобождение памяти */
free(a);
}
30

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

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

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

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

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

33
Указатели на функции
Указатели на функции
Ук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. Указатели на функции: пример

34
Указатели на функции
Указатели на функции: пример
int
int
int
int
add(int
sub(int
mul(int
div(int
x,
x,
x,
x,
int
int
int
int
y)
y)
y)
y)
{
{
{
{
return
return
return
return
x+y;
x-y;
x*y;
x/y;
}
}
}
}
Операции пронумерованы
0 – add, 1 – sub, 2 – mul, 3 – div
int evaluate(unsigned int op, int x, int y) {
int (*eval[])(int, int) = { add, sub, mul, div };
if (op>3) {
printf(“Недопустимая операция”);
return 0;
}
Массив из указателей
на функции вида
int f(int,int)
return eval[op](x, y);
}
void main() {
printf(“%d\n”, evaluate(3, 42, 3));
}
Вызов подходящей
функции

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

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

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

36
Указатели и параметры фугкций
Как передаются параметры
через указатель?
В функцию передается не значение, а адрес переменной
Для доступа
к значению
переменной
используется
операция
разадресаци
и
#include <stdio.h>
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. Как изменить переменную в вызывающей функции?

37
Указатели и параметры фугкций
Как изменить переменную в
вызывающей функции?
a
b
5
x
y
t
10
#include <stdio.h>
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);
}
x = &a;
y = &b;
t = *x;
*X
=
*y
=
/*в х – адрес a*/
/*в y – адрес a*/
/*в t поместить значение,
хранящееся по адресу x*/
*y; /*по адресу x записать значение,
хранящееся по адресу y*/
t; /*по адресу y записать значение,
хранящееся в t*/

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

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

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

Сложные описания с указателями
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-ов
39

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

Сложные описания с указателями
double **d[8] /* хмм... */
char *(*(**foo [][8])())[] /* упс! что такое foo? */
Чтобы понять подобные сложные объявления используйте
следующие три правила:
Начинайте с имени переменной (d или foo в примерах выше)
Заканчивайте на имени типа (double или char выше)
Идите вправо пока можно, затем влево (влево необходимо идти
при обнаружении закрывающейся скобки)
Например
40

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

Сложные описания с указателями
double **d[8] /* хмм... */
char *(*(**foo [][8])())[] /* упс! что такое foo? */
Еще пример
41

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

42
Сложные описания с указателями
Сложные описания с указателями
double **d[8] /* хмм... */
char *(*(**foo [][8])())[] /* упс! что такое foo? */
Как можно упростить понимание сложных объявлений?
Ответ: использовать оператор typedef
typedef
typedef
typedef
typedef
float real;
unsigned char byte;
int *INTPTR;
void (*FUNCPTR)();
real r=0.0;
byte b=0;
INTPTR p;
FUNCPTR pf;
FUNCPTR paf[10];
/*
/*
/*
/*
/*
то же,
то же,
то же,
то же,
массив
/*
/*
/*
/*
/*
real – синоним float
byte – синоним unsigned char
INTPTR – синоним int*
FUNCPTR – указатель на функцию
с прототипом void f()
*/
*/
*/
*/
*/
что и float r=0.0
что и unsigned char b=0
что и int *p
что и void (*pf)()
из указателей на функции void (*pf)()
*/
*/
*/
*/
*/

43. Вопросы?

43
Вопросы и ответы
Вопросы?
Указатели
Указатели и массивы
Динамические массивы
Указатели на функции
Указатели и
параметры функций
Сложные описания
с указателями
Н.Копейкин Металлурги и компьютер
English     Русский Правила