Лекция-08-09.ppt
- Количество слайдов: 22
Тема Указатели и массивы Лекция 03. 10. 11 г. 1
Адресация памяти Ø Будем считать, что оперативная память компьютера представляет собой линейную последовательность пронумерованных ячеек памяти – байтов и каждая ячейка памяти имеет адрес. Ø Будем также считать, что компьютер имеет 32 разрядную архитектуру и адреса записываются как 32 -битные коды. Ø Наименьший адрес: 0 x 0000, наибольший – 0 x. FFFF. Ø Адресуемый объем памяти – 232 байт = 4 Гбайт 0 x 00000001 0 x 00000002 ……………. . 4 Гбайт 0 x. FFFFFFFE 0 x. FFFF Лекция 03. 10. 11 г. 2
Адреса объектов программы Ø Каждый объект программы занимает определенный участок оперативной памяти компьютера и, следовательно, может быть охарактеризован адресом начала этого участка, который и считается адресом данного объекта. Ø В языке С имеется специальная унарная операция & для получения адреса объекта d в оперативной памяти. Ø Функция printf имеет специальный формат %p для вывода адреса. #include
Размещение в памяти объектов программы fact 0 x 00401300 ? ? байт a 0 x 004050 DC y 0 x 004051 DC b 0 x 004051 EC 0 x 004053 EC 0 x 1 DC– 0 x 0 DC=0 x 100=256 16 байт 0 x 1 EC– 0 x 1 DC=0 x 010=16 512 байт x 256 байт 0 x 3 EC– 0 x 1 EC=0 x 200=512 16 байт Лекция 03. 10. 11 г. 4
Замечание Некоторые объекты программы не требуют применения к ним операции взятия адреса &, т. к. они сами и являются адресами. К их числу относятся функции и массивы, т. е. значением имени функции и значением имени массива является адрес. Доказательство этого: #include
О размерах участков памяти, выделяемых объектам Результаты работы программы позволяют предположить, что память объектам программы выделяется блоками, кратными 16 байт. Проведем эксперимент - вместо y определим массив из 8 элементов, а размер массива b увеличим на 1: #include
Иллюстрация Было: fact Стало: fact 0 x 00401300 ? ? байт a 0 x 004050 DC y 0 x 004051 DC b 0 x 004051 EC ? ? байт 16 байт 0 x 004050 DC y 0 x 004051 DC b 256 байт a 0 x 004051 EC 0 x 004053 EC 16 байт x 0 x 004053 FC 16 байт 8 * 2 байта 528 байт 512 байт x 256 байт 513 * 1 байт 16 байт 0 x 3 FC– 0 x 1 EC=0 x 210=528 Лекция 03. 10. 11 г. 7
Адресная арифметика Можно предположить, что адреса являются обычными числами (? ) и могут участвовать в арифметических операциях. Проверим это: #include
Анализ результатов Ø int x: при увеличении адреса на 2 результат увеличился на 0 x 404 -0 x 3 FC=8 , т. е. увеличение в 4 раза больше (а размер переменной типа int как раз и = 4 байтам) Ø short y[8]: при уменьшении адреса на 128 результат уменьшился на 0 x 51 DC-0 x 50 DC=0 x 100=256 , т. е. уменьшение в 2 раза больше (а размер переменной типа short как раз и = 2 байтам) Ø char a[256]: при увеличении адреса на 2 результат увеличился на 0 x. DE-0 x. DC=2 , что и требовалось, потому что размер переменной типа char = 1 байту Ø char b[513]: увеличение адреса на 528 в точности дало адрес переменной x. Ø int fact(int n): при увеличении адреса на 2 результат увеличился также на 2, что позволяет предположить, что «тип адреса» функции такой же, что и тип адреса переменной char. Лекция 03. 10. 11 г. 9
Выводы (правила адресной арифметики) 1. К адресу можно прибавлять или из адреса можно вычитать целые значения. При этом результат операции зависит от типа переменной, адрес которой участвует в операции. Если для данного типа размер переменной равен n байт и к адресу прибавляется i, то величина адреса изменится на i*n. 2. Никакие другие операции к адресам неприменимы, т. е. адреса нельзя умножать, делить, складывать между собой и пр. 3. Исключением является операция вычитания адресов однотипных объектов, результатом которой является целое число, равное количеству объектов данного типа, которые могут разместиться на участке памяти между адресами-операндами. Лекция 03. 10. 11 г. 10
Иллюстрация к вычитанию адресов #include
Иллюстрация к вычитанию адресов int fact(int) 0 x 00401300 ? ? байт char a[256] 0 x 004050 DC short y[8] 0 x 004051 DC char b[513] 256 байт 256 байт 272 байта 16 байт 800 байт 0 x 004051 EC 528 байт int x 0 x 004053 FC 16 байт Лекция 03. 10. 11 г. 12
Указатели Адреса объектов программы можно сохранять в переменных специального вида – указателях. Указатели образуют не один конкретный тип данных (хотя все указатели имеют один размер – 4 байта); типов указателей существует столько, сколько существует типов данных, т. е. для любого типа данных Т существует тип указателя Т*. Примеры типов указателей: char*, int*, short*, long*, float*, double*, void*, … Указатели типа void* называются нетипизированными указателями. При выполнении арифметических операций над такими указателями считается, что объекты, на которые они указывают, имеют размер 1 байт. … main() { int *px = &x; short *py = y; char *pa = a, *pb = void *pf = fact; printf("px = %pn", printf("py = %pn", printf("pa = %pn", printf("pb = %pn", printf("pf = %pn", system("PAUSE"); return 0; } b; px); py); pa); pb); pf); Лекция 03. 10. 11 г. 13
Доступ по указателю Ø В языке С существует одноместная операция * , применяемая к значениям, являющимся адресами (указателями). Результатом этой операции является объект программы, расположенный по данному адресу. Эта операция называется операцией ссылки по указателю, или операцией разыменования. Ø Операции & и * являются взаимно-дополняющими и, если они расположены подряд, взаимно компенсируют друга: Ø Пусть дано определение: int x; Тогда &x – адрес x; *&x – сама переменная x; &*&x – адрес x и т. д. Ø Переменная типа указатель может в разное время принимать различные значения. Следовательно, через одну и туже переменную-указатель можно обращаться к разным переменным (в разное время): int a=2, b=3, c=4; int* p=&a; *p=7; p=&b; *p=8; p=&c; *p=9; printf(“a=%d, b=%d, c=%dn”, a, b, c); //a=7, b=8, c=9 Лекция 03. 10. 11 г. 14
Указатели и аргументы функций В теории программирования известны два способа передачи аргументов в функцию: § передача по значению § передача имени (ссылке) В обоих случаях аргументы функции являются её локальными переменными и «не видны» извне. Различие способов состоит в том, что при вызове функции её аргументлокальная переменная получает : § в первом случае - копию значения фактического аргумента § во втором случае - ссылку на фактический аргумент (т. е. его адрес) и, следовательно, может им распоряжаться Каждый из этих способов имеет свои преимущества и недостатки и программист должен обоснованно подходить к выбору наиболее подходящего для конкретной ситуации способа. Наиболее известны два случая, когда необходимо использовать передачу по имени: § функция должна изменить значение передаваемого ей аргумента § размер аргумента велик и передача его по значению приведет к большим затратам памяти В языке С «штатно» реализован первый способ, однако и второй способ легко реализуется, если в качестве аргумента передавать адрес (указатель). Лекция 03. 10. 11 г. 15
Указатели и аргументы функций Рассмотрим еще раз функцию swap (обмен значениями) #include
Указатели и аргументы функций Второй пример – передача массива в функцию по ссылке main #include
Указатели и массивы Массив - это одна из наиболее простых структур данных, представляющих собой линейную последовательность фиксированного количества однотипных элементов, расположенных в памяти друг за другом непрерывно и допускающих прямой доступ к любому элементу по его номеру (индексу). Массив характеризуется именем, типом элементов и их количеством. Пример определения массива: int x[50] x: addr x[0] x[1] x[2] x[3] … … 0 1 2 3 … … x[49] 49 В языке С существует очень тесная связь между массивами и указателями, которая характеризуется простым утверждением: Имя массива – это константный указатель на его начало Из этого утверждения следуют важные соотношения (здесь Т – имя типа): x[i] эквивалентно *(x+i) T x[] эквивалентно T *x Лекция 03. 10. 11 г. 18
Определение массивов При работе с массивами имеется одно затруднение – размер массива (количество элементов) должен быть известен в момент выделения памяти массиву компилятором. Проблем не возникает в следующих случаях: Ø размер массива задается константным выражением Ø размер массива задается выражением, в которое входят константы и внешние по отношению к блоку, в котором производится определение массива, переменные (и эти переменные получили значения!) Ø размер массива не задается, а определяется списком инициализации … #define N 100 int n 1 = 10; int main() { int n = 20; int a[N], b[30], c[n 1]; int g[] = {1, 2, 3, 4}; { int d[n]; } } Лекция 03. 10. 11 г. 19
Динамические массивы В тех случаях, когда размер массива невозможно знать заранее (например, эта величина вводится человеком), можно использовать динамические массивы, память для которых выделяется динамически системной функцией void *calloc(size_t n, size_t r) где n – количество элементов массива, r – размер элемента в байтах. Функция возвращает нетипизированный указатель на начало массива. #include
Версия только с указателями В этой версии программы используются только указатели. #include
Освобождение памяти В программировании встречается неприятная ситуация, которая называется «утечка памяти» . Эта ситуация может возникнуть при использовании динамического выделения памяти функцией calloc. Для устранения этой ситуации рекомендуется освобождать неиспользуемую память с помощью функции void free(void *p) где p – нетипизированный указатель, получивший значение в результате вызова calloc. … int sum(int m[], int n) { int i, s; for(i=0, s=0; i


