ФГОБУ ВПО "СибГУТИ" Кафедра вычислительных систем УКАЗАТЕЛИ Преподаватель: Доцент Кафедры ВС, к.т.н. Поляков Артем Юрьевич © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» ФГОБУ ВПО "СибГУТИ" Кафедра вычислительных систем ЯЗЫКИ ПРОГРАММИРОВАНИЯ / ПРОГРАММИРОВАНИЕ
Переменная Компьютерная память хранит большой объем разнообразной информации. Для доступа к конкретной ее части необходим адрес. 2 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x xFA Переменная в программе – объект, имеющий имя, содержимое которого может меняться. Память программы A
Переменная для компилятора Для компилятора и компоновщика переменная – это блок памяти, хранящий значение переменной, обратиться к которому можно по имени переменной. Размер блока определяется типом переменной. Например размер типа int на современных ПК – 4 байта. При определении переменной компилятору предоставляется информация о типе и имени переменной и, возможно, о ее значении: int a = 154; 3 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x0 154 AaAa a
Выполнение программы int a = 154; 4 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x0 154 AaAa a компилятором будет сгенерирована инструкция процессору, помещающая значение 15 по адресу A a, ассоциированному с символом a. a = 15; 15 При выполнении инструкции:
Переменная как объект языка программирования int a = 154; 5 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x0 154 AaAa a Таким образом, с каждой переменной в высокоуровневом языке программирования связано два числа: адрес переменной (lvalue) значение переменной (rvalue)
Контекст использования переменной (rvalue – справа от знака присваивания) int a = 154, b; 6 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» b = a;
Контекст использования переменной (lvalue – слева от знака присваивания) 7 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» a = 15;... 0x0 154 AaAa 15 Если переменная расположена слева от знака присваивания она рассматривается в смысле "адрес переменной", т.е.: в ячейку памяти, начинающуюся с адреса A a будет записано sizeof(a) байт, которые будут преобразованы к типу int и определяются выражением справа от знака присваивания.
Комбинации контекста и значения, связанного с переменной 8 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x0 154 AaAa 15 int a = 5, b = 10; a = b; ab rvalueнельзяконтекст lvalueконтекст ? Не имеет смысла, как и: 7 = 5 или 7 = b Не имеет смысла, как и: 7 = 5 или 7 = b
Указатели (2) 9 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Указатели используются в языках высокого уровня для изменения умолчаний, связанных с контекстом использования переменных. Основной их функцией является возможность хранения адреса ячейки памяти.
Указатели (2) 10 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Для описания указателя на языке Си перед его именем необходимо указать знак '*': int *i; Размер, необходимый для хранения указателя определяется аппаратурной платформой. На современных ПК: 4 байта (x86), 8 байт (x86_64). Фактический размер адреса не важен для программиста, т.к. компилятор берет на себя обработку этих деталей.
Указатели (3) 11 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Рассмотрим следующий пример: int *ptr; имя переменной-указателя (в дальнейшем просто указатель) – ptr; звездочка информирует компилятор что определяется указатель, т.е. выделяется ячейка памяти размером с адрес (одинаков для всех типов данных). тип int говорит о том что указатель будет хранить адрес целочисленного типа данных Говорят: "объявлен указатель на целое".
Операция взятия адреса 12 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» В языке Си существует операция взятия адреса, которая обозначается через амперсанд ("&"). Оператор "&" позволяет получить адрес (lvalue) переменной, если она располагается справа от знака присваивания, несмотря на то, что. по умолчанию переменная будет рассматриваться как lvalue: int a = 10, b = 15, x;... = &a – b + 8 В указанном примере переменная a рассматривается как адрес!
Настройка указателей 13 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Операция взятия адреса используется для "настройки" указателя на реальную область памяти, например: int k = 10, *ptr;... 0x0 154 AkAk k ??? ptr ptr = &k; A ptr... 0x0 154 AkAk k AkAk ptr A ptr
Нулевой указатель 14 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Переменная-указатель может не указывать ни на какую реальную ячейку памяти: 1)при определении без инициализации; 2)в связи с алгоритмом выполнения программы. Для того, чтобы явно задать значение указателя, не указывающего никуда, используется специальная константа NULL, которая в большинстве архитектур (но не во всех) равна 0.
Контекст использования указателей 15 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Для переменных-указателей в языке Си по умолчанию действуют те же правила определения используемого значения по контексту, что и для обычных переменных. Например: int k = 10, *ptr = &k, b; b = ptr + 5;... 0x0 AkAk k 10A k +5AkAk ptr A ptr AbAb b
Контекст использования указателей (2) 16 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int k = 10, *ptr = &k, b = 8; ptr = b + 5;... 0x0 AkAk k ptr A ptr AbAb b
Операция разыменования 17 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Для изменения стандартного распознавания переменной-указателя в выражениях используется операция разыменования, которая обозначается через "звездочку" ("*"): int k = 10, *ptr = &k, b; b = k + 5; b = *(ptr) + 5;... 0x0 AkAk k 1015AkAk ptr A ptr AbAb b
Операция разыменования (2) 18 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int k = 10, *ptr = &k, b = 8; *ptr = b + 5;... 0x0 AkAk k 138AkAk ptr A ptr AbAb b
Двойной указатель 19 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» Переменная-указатель, в свою очередь, имеет адрес. Что позволяет создавать ячейки памяти, предназначенные для хранения адресов ячеек с адресами, или двойных указателей. Аналогичное рассуждение можно в свою очередь применить к двойным указателям, что позволит перейти к тройным переменным-указателям.... 0x0 Ak1Ak1 k1 1010Ak1Ak1 ptr1 A ptr1 Ak2Ak2 k2 20Ak2Ak2 ptr2 A ptr2 A ptr1 A
Двойной указатель 20 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» В общем существует следующее правило: Количество знаков "*", используемых при определении переменной-указателя, определяет количество знаков "*", которое требуется для доступа к конечной ячейке, содержащей значение, а не адрес. int j, *ptr=&j, **dptr=&ptr, ***tptr=&dptr; *ptr ~ j, *dptr ~ ptr, **dptr ~ j *tptr ~ dptr, **tptr ~ ptr, ***tptr ~ j... 0x0 Ak1Ak1 j 1010 AjAj ptr A ptr A dptr dptr 20 A dptr tptr A tptr
Двойной указатель (пример) 21 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int k1 = 10, k2 = 20; int *ptr1 = &k1, *ptr2 = &k2; int **dptr = &ptr1; **dptr = 80; // в какую ячейку запись? dptr = &ptr2; // в какую ячейку запись? **dptr = 100; // в какую ячейку запись? *dptr = ptr1; // в какую ячейку запись? **dptr = 120; // в какую ячейку запись?... 0x0 Ak1Ak1 k Ak1Ak1 ptr1 A ptr1 Ak2Ak2 k2 20 Ak2Ak2 ptr2 A ptr2 A ptr1 dptr A dptr
Для чего задается тип указателя? 22 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int i = 10, *iptr = &i; char c = '0', *cptr = &c; short s = 10, *sptr = &s;... 0x0 AiAi i 10 iptr A iptr AiAi AcAc c 0x30 cptr A cptr AcAc AsAs s 0x30 sptr A sptr AsAs Адрес – это порядковый номер байта Адрес одинаков для ячеек любого размера! =>=>
1. Копирование и разрядность данных 23 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» short s = 0xFFCC; char *cptr = (char*)&s; *сptr = '1'; // изменен только 1-й байт!... 0x0 AsAs s 0x31 cptr A cptr AsAs 0xFF Для выполнения операции присваивания требуется информация о количестве байт, которые должны быть перезаписаны
Допустимые операции 24 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int *ptr1, *ptr2, j, **dptr; Допустимые операции с указателями: 1Операция присваивания: ptr1 = &j; 2Операция взятия адреса: dptr = &ptr1; 3Операция разыменования: j = *ptr2; 4Сложение с целым: ptr2 = ptr1 + j; 5Разность указателей: j = ptr1 – ptr2; 6Операция индексации: ptr1[j] = 10; Адресная арифметика
2. Адресная арифметика (сложение с целым) 25 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x0 AmAm ptr A m + 4 AmAm int m[2], *ptr = m; *ptr = 15; *(ptr + 1) = 20; m Имя массива – УКАЗАТЕЛЬ-КОНСТАНТА на его первый элемент
2. Адресная арифметика (разность указателей) 26 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int m[3], *ptr1 = &m[0], *ptr2 = &m[2]; int i = ptr2 – ptr3; // i == x0 AmAm ptr1 A m + 4 AmAm A m + 8 ptr2 Am+8Am+8 m
Связь массивов и указателей 27 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x0 AmAm ptr A m + 4 AmAm int m[3], *ptr = m, i = 2; m[i] ~ ptr[i] ~ *(ptr + i) A m + 8 m ~ A m Имя массива является указателем-константой на его первый элемент. К N-мерному указателю может применяться N операций индексации. Например:
Указатель на многомерный массив 28 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ»... 0x0 AmAm ptr AmAm int m[3][3]; Пусть дан массив: int **ptr1 = m, (*ptr2)[3] = m; Особенность многомерного массива – его расположение в одномерной памяти программы Это приводит к тому, что при объявлении указателя на данный массив требуется сообщить компилятору обо всех размерностях, кроме старшей
Применение двух операций индексации (неверный вариант) 29 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int m[3][3], **ptr = m; m[i] ~ ptr[i] ~ *(ptr + i) ptr[i][j] ~ *(*(ptr + i) + j) ptr[1][2] - ? x5 ptr AmAm AmAm ptr[1] ptr[1][2] ?
Применение двух операций индексации (правильный вариант) 30 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int m[3][3], (*ptr)[3] = m; m[i] ~ ptr[i] ~ (ptr + i*3) ptr[i][j] ~ *((ptr + i*3) + j) ptr[1][2] - ? x5 ptr AmAm AmAm ptr[1] ptr[1][2] ?
Альтернативный способ организации двумерного массива 31 © Кафедра вычислительных систем ГОУ ВПО «СибГУТИ» int m[3][3], *ptr[3] = {m[0],m[1],m[2]}; ptr[i] == m[i] ~ *(ptr + i) ptr[i][j] ~ *(*(ptr + i) + j) 5... A prt ptr~(int**) AmAm AmAm A m +3A m +6 ptr[1]~(int*) A m +3 ptr[1][2]~(int)
32 © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» СПАСИБО ЗА ВНИМАНИЕ!