05 указатели

Содержание

Слайд 2

Указатель

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

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

Язык Си. Тема 5

Слайд 3

Объявление указателя

Объявление указателя на целое и целого числа:
Объявление двух указателей типа float:

Язык

Объявление указателя Объявление указателя на целое и целого числа: Объявление двух указателей
Си. Тема 5

int *countPtr = NULL, count = 0;

float *xPtr = 0, *yPtr = 0;

Хорошо!
NULL – специальный макрос для обнуления указателей.
Можно также воспользоваться числом 0.

float *xPtr, *yPtr;
int *countPtr;

Плохо!
Объявлять неинициализированный указатель

Слайд 4

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

Взятия адреса или адресации & - унарная операция, которая возвращает адрес

Операция адресации Взятия адреса или адресации & - унарная операция, которая возвращает
своего операнда
Операнд операции адресации должен быть L-величиной (т.е. чем-то таким, чему можно присвоить значение так же, как переменной)
Операция адресации не может быть применена к константам, к выражениям, не дающим результат, на который можно сослаться

Язык Си. Тема 5

int y = 5;
int *yPtr = 0;
yPtr = &y;

Слайд 5

Операция разыменования

Разыменования или косвенной адресации * - возвращает значение объекта, на который

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

Язык Си. Тема 5

printf("%d\n", *yPtr); //5

Слайд 6

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

int a = 7;
int *aPtr = &a;
printf("Address a is: %x\n",

Операции с указателями int a = 7; int *aPtr = &a; printf("Address
&a);
printf("Value aPtr is: %x\n", aPtr);
printf("Value a is: %d\n", a);
printf("Value *aPtr is:%d\n", *aPtr);
printf("&*aPtr is %x\n", &*aPtr);
printf("*&aPtr is %x\n", *&aPtr);

Язык Си. Тема 5

Результат:
0012FF7C
0012FF7C
7
7
0012FF7C
0012FF7C

Слайд 7

Указатель на указатель

Позволяет хранить адрес переменной, хранящей адрес
При объявлении нужно использовать две

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

Язык Си. Тема 5

int y = 5;
int *p = 0;
p = &y;
int **pp = 0;
pp = &p;

Слайд 8

Указатель на указатель

int a = 5;
int * p = &a;
int ** pp

Указатель на указатель int a = 5; int * p = &a;
= &p;
printf("%d\n", a);
printf("%d\n", *p);
printf("%d\n", **pp);
printf("%x\n", &a);
printf("%x\n", p);
printf("%x\n", *pp);
printf("%x\n", &p);
printf("%x\n", pp);
printf("%x\n", &pp);

Язык Си. Тема 5

Результат:
5
5
5
0012FBA0
0012FBA0
0012FBA0
0012FB94
0012FB94
0012FB88

Слайд 9

Взаимосвязь указателей и массивов

Имя массива – это адрес первого элемента массива
Имя массива

Взаимосвязь указателей и массивов Имя массива – это адрес первого элемента массива
– это постоянный указатель
Можно объявить указатель на первый элемент массива и использовать его вместо имени массива
Указатель на первый элемент массива и имя массива могут использоваться практически эквивалентно

Язык Си. Тема 5

Слайд 10

Взаимосвязь указателей и массивов

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

Взаимосвязь указателей и массивов for (int i = 0; i printf("%d\n", b[i]);
{
printf("%d\n", b[i]);
}

Язык Си. Тема 5

#define SIZE 5
int b[5] = {1, 2, 3, 4, 5};
int* bPtr = 0;
bPtr = b;
bPtr = &b[0];

Эти строчки эквивалентны

for (int i = 0; i < SIZE; i++) {
printf("%d\n", bPtr[i]);
}

После инициализации указателя работать с массивом можно через его имя, а можно через указатель

Слайд 11

Арифметика указателей

Возможные действия:
<указатель> = <указатель> + <целое число>
<указатель> = <указатель> – <целое

Арифметика указателей Возможные действия: = + = – = ++ = –-
число>
<указатель> = <указатель> ++
<указатель> = <указатель> –-
<целое число> = <указатель> - <указатель>

Язык Си. Тема 5

Арифметические действия с указателями имеют смысл, только если указатель ссылается на массив

Слайд 12

Арифметика указателей

#define ROW 5
double arr[ROW] = { 1.1, 2.2, 3.3, 4.4, 5.5

Арифметика указателей #define ROW 5 double arr[ROW] = { 1.1, 2.2, 3.3,
};
double *p = arr;

Язык Си. Тема 5

Слайд 13

Арифметика указателей

#define SIZE 5
int v[SIZE] = { 1, 2, 3, 4, 5

Арифметика указателей #define SIZE 5 int v[SIZE] = { 1, 2, 3,
};
int *vPtr = &v[0];
printf("%x\n", vPtr++);
vPtr +=2;
printf("%x\n", vPtr);
printf("%d\n", vPtr - v);

Язык Си. Тема 5

1 2 3 4 5

3000

Результат:
3000
3012
3

Слайд 14

Взаимосвязь указателей и массивов

Язык Си. Тема 5

bPtr +=3; //OK
b += 3; //error
bPtr

Взаимосвязь указателей и массивов Язык Си. Тема 5 bPtr +=3; //OK b
++; //OK
b ++; //error

int b[5] = {1, 2, 3, 4, 5};
int* bPtr = 0;
bPtr = b;
bPtr = &b[0];

Имя массива – это постоянный указатель, значит, оно не является L-величиной:

Слайд 15

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

Для доступа к элементу массива или для сдвига

Операция индексации и запись указатель-смещение Для доступа к элементу массива или для
указателя по массиву можно использовать два варианта обращения:
Через операцию индексации:
Через запись указатель-смещение
Эти варианты эквивалентны

Язык Си. Тема 5

printf("%d\n", b[3] );
bPtr[3] = 5;

printf("%d\n", *(bPtr+3) );
*(b+3) = 5;

Слайд 16

Массивы указателей

Это массивы, элементами которых являются указатели
Используются при работе с динамическими объектами
Указатели

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

Язык Си. Тема 5

Слайд 17

Массивы указателей

char *suit[4] = { "весна",
"лето",
"осень",
"зима" };

Язык

Массивы указателей char *suit[4] = { "весна", "лето", "осень", "зима" }; Язык
Си. Тема 5

A1 A2 A3 A4 A5 A6

D1 D2 D3 D4 D5

Слайд 18

Массивы указателей

char* c[] = {"ENTER",
"NEP",
"POINT",
"FIRST"};
char **cp[]

Массивы указателей char* c[] = {"ENTER", "NEP", "POINT", "FIRST"}; char **cp[] =
= {c+3, c+2, c+1, c};
char *** cpp = cp;
printf("%s", * * ++ cpp);
printf("%s ", * -- * ++ cpp + 3);
printf("%s", *cpp[ -2 ] + 3);
printf("%s\n", cpp[ -1 ][ -1 ] + 1);

Массив указателей

Массив указателей на указатели

Указатель на указатель на указатель

Что будет напечатано?

Язык Си. Тема 5

Слайд 19

Массивы указателей

cpp

A1 A2 A3 A4 A5 A6

B1 B2 B3 B4

Массивы указателей cpp A1 A2 A3 A4 A5 A6 B1 B2 B3
C1 C2 C3 C4 C5 C6

D1 D2 D3 D4 D5 D6

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

Язык Си. Тема 5

Слайд 20

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

Язык Си. Тема 5

Это указатели, которые ссылаются на целый массив,

Указатели на массивы Язык Си. Тема 5 Это указатели, которые ссылаются на
а не на отдельный элемент
Используются при передаче многомерных массивов в функции
При арифметике указателей смещаются на размер всего массива, на который ссылаются
Указатели на массивы также можно рассматривать как двумерные массивы. У таких массивов может быть неизвестное число строк, но число столбцов фиксировано и не меняется

Слайд 21

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

#define ROW 2
#define COLUMN 3
int b[ROW][COLUMN] = { 1,2,3,4,5,6 };
int(*pb)[COLUMN]

Указатели на массивы #define ROW 2 #define COLUMN 3 int b[ROW][COLUMN] =
= 0;
pb = b;
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COLUMN; j++) {
printf("%d\t", pb[i][j] );
}
printf("\n");
}

Язык Си. Тема 5

pb ссылается на первый элемент массива b.
Теперь через pb можно работать с массивом b

Работа как с обычным двумерным массивом

pb хранит адрес первого элемента массива b.

Первый элемент массива b – это массив из трех элементов {1,2,3}

Слайд 22

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

#define ROW 2
#define COLUMN 3
int b[ROW][COLUMN] = { 1,2,3,4,5,6 };
int(*pb)[COLUMN]

Указатели на массивы #define ROW 2 #define COLUMN 3 int b[ROW][COLUMN] =
= 0;
pb = b;
printf("%x\n", pb);
printf("%x\n", b);
printf("%x\n", b[0]);

Язык Си. Тема 5

Результат:
0028F914
0028F914
0028F914

pb имеет тип int(*)[3]
b имеет тип int[2][3]
b[0] имеет тип int * const
При этом адрес, на который они ссылаются - одинаковый

Слайд 23

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

#define ROW 2
#define COLUMN 3
int b[ROW][COLUMN] = { 1,2,3,4,5,6 };
int(*pb)[COLUMN]

Указатели на массивы #define ROW 2 #define COLUMN 3 int b[ROW][COLUMN] =
= 0;
pb = b;
printf("%x\n", (pb + 1) );
printf("%x\n", (b + 1) );
printf("%x\n", (b[0] + 1) );

Язык Си. Тема 5

Результат:
0028F920
0028F920
0028F918

Смещение указателей дает разные результаты:
Указатели pb и b хранят адрес массива и сдвигаются на sizeof(int[COLUMN])
Указатель b[0] хранит адрес одного целого числа и сдвигается на sizeof (int)

Слайд 24

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

#define ROW 2
#define COLUMN 3
int b[ROW][COLUMN] = { 1,2,3,4,5,6 };
int(*pb)[COLUMN]

Указатели на массивы #define ROW 2 #define COLUMN 3 int b[ROW][COLUMN] =
= 0;
pb = b;
printf("%x\n", pb++);
printf("%x\n", b++);
printf("%x\n", b[0]++);

Язык Си. Тема 5

Результат:
0028F914
ошибка
ошибка

Указатели b и b[0] являются постоянными указателями на первый элемент массива, поэтому к ним нельзя применять операцию инкремента / декремента

Слайд 25

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

Их размер может меняться в процессе работы программы
Память под них выделяется

Динамические массивы Их размер может меняться в процессе работы программы Память под
и освобождается только по запросу пользователя (программиста)
Место выделяется в специальной памяти – динамической
Динамические массивы работают медленнее обычных (статических)

Язык Си. Тема 5

Слайд 26

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

Выделение блока памяти размера size
Выделение блока для

Функции для работы с динамической памятью Выделение блока памяти размера size Выделение
хранения n-элементов по size байт
Перераспределение блока памяти
Освобождение памяти

Язык Си. Тема 5

void * malloc (size_t size);

void * calloc(size_t n, size_t size);

void * realloc(void* ptr, size_t size);

void free(void *ptr);

Слайд 27

Выделение памяти под динамические массивы

char * MyArr = 0;
int n = 0;
puts("Enter

Выделение памяти под динамические массивы char * MyArr = 0; int n
a number");
scanf("%d", &n);
//выделение памяти под массив символьного типа
MyArr =(char *) calloc(n, sizeof(char) );

//освобождение памяти из-под массива
free(MyArr);

Язык Си. Тема 5

Слайд 28

Выделение памяти под двумерный массив

int ** MyArr = 0, n, m;
puts("Enter two

Выделение памяти под двумерный массив int ** MyArr = 0, n, m;
numbers");
scanf("%d%d", &n, &m);
//выделение памяти под двумерный массив
//сначала под массив указателей
MyArr = (int **) calloc(n, sizeof(int *) ); //потом под каждый из подмассивов
for (int i=0; i{
MyArr[i] = (int *) calloc(m, sizeof(int) );
}

Язык Си. Тема 5

Слайд 29

Освобождение памяти из-под двумерного массива

//сначала из-под каждого подмассива
for (int i=0; i free(MyArr[i]);
//потом

Освобождение памяти из-под двумерного массива //сначала из-под каждого подмассива for (int i=0;
из-под массива указателей
free(MyArr);

Язык Си. Тема 5