Разделы презентаций


Тема Указатели и массивы Лекция 03.10.11г. 1

Содержание

Лекция 03.10.11г.Адресация памятиБудем считать, что оперативная память компьютера представляет собой линейную последовательность пронумерованных ячеек памяти – байтов и каждая ячейка памяти имеет адрес.Будем также считать, что компьютер имеет 32-разрядную архитектуру и

Слайды и текст этой презентации

Слайд 1Тема
Указатели и массивы
Лекция 03.10.11г.

Тема Указатели и массивыЛекция 03.10.11г.

Слайд 2Лекция 03.10.11г.
Адресация памяти
Будем считать, что оперативная память компьютера представляет собой

линейную последовательность пронумерованных ячеек памяти – байтов и каждая ячейка

памяти имеет адрес.
Будем также считать, что компьютер имеет 32-разрядную архитектуру и адреса записываются как 32-битные коды.
Наименьший адрес: 0x00000000, наибольший – 0xFFFFFFFF.
Адресуемый объем памяти – 232 байт = 4 Гбайт
Лекция 03.10.11г.Адресация памятиБудем считать, что оперативная память компьютера представляет собой линейную последовательность пронумерованных ячеек памяти – байтов

Слайд 3Лекция 03.10.11г.
Адреса объектов программы
Каждый объект программы занимает определенный участок оперативной

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

участка, который и считается адресом данного объекта.
В языке С имеется специальная унарная операция & для получения адреса объекта d в оперативной памяти.
Функция printf имеет специальный формат %p для вывода адреса.

#include
#include
int x;
short y;
char a[256], b[512];
int fact(int n) {
return n ? n * fact(n-1) : 1;
}
main() {
printf("addr(x) = %p\n", &x);
printf("addr(y) = %p\n", &y);
printf("addr(a) = %p\n", &a);
printf("addr(b) = %p\n", &b);
printf("addr(fact) = %p\n", &fact);
system("PAUSE");
return 0;
}

Лекция 03.10.11г.Адреса объектов программыКаждый объект программы занимает определенный участок оперативной памяти компьютера и, следовательно, может быть охарактеризован

Слайд 4Лекция 03.10.11г.
Размещение в памяти объектов программы
0x1DC–0x0DC=0x100=256
0x1EC–0x1DC=0x010=16
0x3EC–0x1EC=0x200=512

Лекция 03.10.11г.Размещение в памяти объектов программы0x1DC–0x0DC=0x100=2560x1EC–0x1DC=0x010=160x3EC–0x1EC=0x200=512

Слайд 5Лекция 03.10.11г.
Замечание
Некоторые объекты программы не требуют применения к ним операции

взятия адреса &, т.к. они сами и являются адресами.
К их

числу относятся функции и массивы, т.е. значением имени функции и значением имени массива является адрес.
Доказательство этого:

#include
#include
int x;
short y;
char a[256], b[512];
int fact(int n) {
return n ? n * fact(n-1) : 1;
}
main() {
printf("addr(x) = %p\n", &x);
printf("addr(y) = %p\n", &y);
printf("addr(a) = %p\n", a);
printf("addr(b) = %p\n", b);
printf("addr(fact) = %p\n", fact);
system("PAUSE");
return 0;
}

a означает то же, что и &a
b означает то же, что и &b
fact означает то же, что и &fact

Лекция 03.10.11г.ЗамечаниеНекоторые объекты программы не требуют применения к ним операции взятия адреса &, т.к. они сами и

Слайд 6Лекция 03.10.11г.
О размерах участков памяти, выделяемых объектам
Результаты работы программы позволяют

предположить, что память объектам программы выделяется блоками, кратными 16 байт.
Проведем

эксперимент - вместо y определим массив из 8 элементов, а размер массива b увеличим на 1:

#include
#include
int x;
short y[8];
char a[256], b[512+1];
int fact(int n) {
return n ? n * fact(n-1) : 1;
}
main() {
printf("addr(x) = %p\n", &x);
printf("addr(y) = %p\n", y);
printf("addr(a) = %p\n", a);
printf("addr(b) = %p\n", b);
printf("addr(fact) = %p\n", fact);
system("PAUSE");
return 0;
}

Лекция 03.10.11г.О размерах участков памяти, выделяемых объектамРезультаты работы программы позволяют предположить, что память объектам программы выделяется блоками,

Слайд 7Лекция 03.10.11г.
Иллюстрация
Было:
Стало:
0x3FC–0x1EC=0x210=528
528 байт
8 * 2байта
513 * 1байт

Лекция 03.10.11г.ИллюстрацияБыло:Стало:0x3FC–0x1EC=0x210=528528 байт8 * 2байта513 * 1байт

Слайд 8Лекция 03.10.11г.
Адресная арифметика
Можно предположить, что адреса являются обычными числами (?)

и могут участвовать в арифметических операциях. Проверим это:
#include
#include
int

x;
short y[8];
char a[256], b[512+1];
int fact(int n) {
return n ? n * fact(n-1) : 1;
}
main() {
printf("addr(x) = %p\n", &x);
printf("addr(x)+2 = %p\n", &x+2);
printf("addr(y) = %p\n", y);
printf("addr(y)-128 = %p\n", y-128);
printf("addr(a) = %p\n", a);
printf("addr(a)+2 = %p\n", a+2);
printf("addr(b) = %p\n", b);
printf("addr(b)+528 = %p\n", b+528);
printf("addr(fact) = %p\n", fact);
printf("addr(fact)+2 = %p\n", fact+2);
system("PAUSE");
return 0;
}
Лекция 03.10.11г.Адресная арифметикаМожно предположить, что адреса являются обычными числами (?) и могут участвовать в арифметических операциях. Проверим

Слайд 9Лекция 03.10.11г.
Анализ результатов
int x: при увеличении адреса на 2 результат

увеличился на 0x404-0x3FC=8 , т.е. увеличение в 4 раза больше

(а размер переменной типа int как раз и = 4 байтам)

short y[8]: при уменьшении адреса на 128 результат уменьшился на 0x51DC-0x50DC=0x100=256 , т.е. уменьшение в 2 раза больше (а размер переменной типа short как раз и = 2 байтам)

char a[256]: при увеличении адреса на 2 результат увеличился на 0xDE-0xDC=2 , что и требовалось, потому что размер переменной типа char = 1 байту
char b[513]: увеличение адреса на 528 в точности дало адрес переменной x.

int fact(int n): при увеличении адреса на 2 результат увеличился также на 2, что позволяет предположить, что «тип адреса» функции такой же, что и тип адреса переменной char .

Лекция 03.10.11г.Анализ результатовint x: при увеличении адреса на 2 результат увеличился на 0x404-0x3FC=8 , т.е. увеличение в

Слайд 10Лекция 03.10.11г.
Выводы (правила адресной арифметики)
К адресу можно прибавлять или из

адреса можно вычитать целые значения. При этом результат операции зависит

от типа переменной, адрес которой участвует в операции. Если для данного типа размер переменной равен n байт и к адресу прибавляется i, то величина адреса изменится на i*n.
Никакие другие операции к адресам неприменимы, т.е. адреса нельзя умножать, делить, складывать между собой и пр.
Исключением является операция вычитания адресов однотипных объектов, результатом которой является целое число, равное количеству объектов данного типа, которые могут разместиться на участке памяти между адресами-операндами.
Лекция 03.10.11г.Выводы (правила адресной арифметики)К адресу можно прибавлять или из адреса можно вычитать целые значения. При этом

Слайд 11Лекция 03.10.11г.
Иллюстрация к вычитанию адресов
#include
#include
int x;
short y[8];
char a[256],

b[512+1];
int fact(int n) {
return n ? n * fact(n-1)

: 1;
}
main() {
printf("addr(b)-addr(a) = %d\n", b-a);
printf("addr(a)-addr(b) = %d\n", a-b);
printf("addr(y)-addr(a) = %d\n", (char*)y-a);
printf("addr(y)-addr(a) = %d\n", y-(short*)a);
printf("addr(x)-addr(a) = %d\n", (void*)&x-(void*)a);
printf("addr(x)-addr(a) = %d\n", (double*)&x-(double*)a);
system("PAUSE");
return 0;
}
Лекция 03.10.11г.Иллюстрация к вычитанию адресов#include #include int x;short y[8];char a[256], b[512+1];int fact(int n) { return n ?

Слайд 12Лекция 03.10.11г.
Иллюстрация к вычитанию адресов
256байт
272байта
800байт

Лекция 03.10.11г.Иллюстрация к вычитанию адресов256байт272байта800байт

Слайд 13Лекция 03.10.11г.
Указатели
Адреса объектов программы можно сохранять в переменных специального вида

– указателях.
Указатели образуют не один конкретный тип данных (хотя все

указатели имеют один размер – 4 байта); типов указателей существует столько, сколько существует типов данных, т.е. для любого типа данных Т существует тип указателя Т*.
Примеры типов указателей: char*, int*, short*, long*, float*, double*, void*, …
Указатели типа void* называются нетипизированными указателями. При выполнении арифметических операций над такими указателями считается, что объекты, на которые они указывают, имеют размер 1 байт.


main() {
int *px = &x;
short *py = y;
char *pa = a, *pb = b;
void *pf = fact;
printf("px = %p\n", px);
printf("py = %p\n", py);
printf("pa = %p\n", pa);
printf("pb = %p\n", pb);
printf("pf = %p\n", pf);
system("PAUSE");
return 0;
}

Лекция 03.10.11г.УказателиАдреса объектов программы можно сохранять в переменных специального вида – указателях.Указатели образуют не один конкретный тип

Слайд 14Лекция 03.10.11г.
Доступ по указателю
В языке С существует одноместная операция *

, применяемая к значениям, являющимся адресами (указателями). Результатом этой операции

является объект программы, расположенный по данному адресу. Эта операция называется операцией ссылки по указателю, или операцией разыменования.
Операции & и * являются взаимно-дополняющими и, если они расположены подряд, взаимно компенсируют друг друга:
Пусть дано определение: 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=%d\n”, a, b, c); //a=7, b=8, c=9
Лекция 03.10.11г.Доступ по указателюВ языке С существует одноместная операция * , применяемая к значениям, являющимся адресами (указателями).

Слайд 15Лекция 03.10.11г.
Указатели и аргументы функций
В теории программирования известны два способа

передачи аргументов в функцию:
передача по значению
передача имени (ссылке)
В обоих случаях

аргументы функции являются её локальными переменными и «не видны» извне. Различие способов состоит в том, что при вызове функции её аргумент-локальная переменная получает :
в первом случае - копию значения фактического аргумента
во втором случае - ссылку на фактический аргумент (т.е. его адрес) и, следовательно, может им распоряжаться
Каждый из этих способов имеет свои преимущества и недостатки и программист должен обоснованно подходить к выбору наиболее подходящего для конкретной ситуации способа.
Наиболее известны два случая, когда необходимо использовать передачу по имени:
функция должна изменить значение передаваемого ей аргумента
размер аргумента велик и передача его по значению приведет к большим затратам памяти
В языке С «штатно» реализован первый способ, однако и второй способ легко реализуется, если в качестве аргумента передавать адрес (указатель).
Лекция 03.10.11г.Указатели и аргументы функцийВ теории программирования известны два способа передачи аргументов в функцию:передача по значениюпередача имени

Слайд 16Лекция 03.10.11г.
Указатели и аргументы функций
Рассмотрим еще раз функцию swap (обмен

значениями)
#include
#include
void swap(int *a, int *b) {
int c

= *a;
*a = *b; *b = c;
}
int main() {
int x = 5, y = 7;
printf("x = %d, y = %d\n",x,y);
swap(&x, &y);
printf("x = %d, y = %d\n",x,y);
system("PAUSE");
return 0;
}

5

7

&x

&y

x :

y :

a :

b :

swap

main

Лекция 03.10.11г.Указатели и аргументы функцийРассмотрим еще раз функцию swap (обмен значениями)#include #include void swap(int *a, int *b)

Слайд 17Лекция 03.10.11г.
Указатели и аргументы функций
Второй пример – передача массива в

функцию по ссылке
#include
#include
int sum(int m[], int n) {

int i, s;
for(i=0, s=0; i return s;
}
#define N 100
int main() {
int z[N], i;
for(i=0; i z[i] = 2*i+1;
printf("summa = %d\n", sum(z, N));
system("PAUSE");
return 0;
}

z

50

z :

m :

n :

sum

main

400 байт

Лекция 03.10.11г.Указатели и аргументы функцийВторой пример – передача массива в функцию по ссылке#include #include int sum(int m[],

Слайд 18Лекция 03.10.11г.
Указатели и массивы
Массив - это одна из наиболее простых

структур данных, представляющих собой линейную последовательность фиксированного количества однотипных элементов,

расположенных в памяти друг за другом непрерывно и допускающих прямой доступ к любому элементу по его номеру (индексу).
Массив характеризуется именем, типом элементов и их количеством.
Пример определения массива: int x[50]

x[0]

0

1

2

3


49

x[1]

x[2]

x[3]



x[49]


x:

addr

В языке С существует очень тесная связь между массивами и указателями, которая характеризуется простым утверждением:

Имя массива – это константный указатель на его начало

Из этого утверждения следуют важные соотношения (здесь Т – имя типа):

x[i] эквивалентно *(x+i)
T x[] эквивалентно T *x

Лекция 03.10.11г.Указатели и массивыМассив - это одна из наиболее простых структур данных, представляющих собой линейную последовательность фиксированного

Слайд 19Лекция 03.10.11г.
Определение массивов
При работе с массивами имеется одно затруднение –

размер массива (количество элементов) должен быть известен в момент выделения

памяти массиву компилятором. Проблем не возникает в следующих случаях:
размер массива задается константным выражением
размер массива задается выражением, в которое входят константы и внешние по отношению к блоку, в котором производится определение массива, переменные (и эти переменные получили значения!)
размер массива не задается, а определяется списком инициализации


#define N 100
int n1 = 10;
int main() {
int n = 20;
int a[N], b[30], c[n1];
int g[] = {1, 2, 3, 4};
{
int d[n];
}
}

Лекция 03.10.11г.Определение массивовПри работе с массивами имеется одно затруднение – размер массива (количество элементов) должен быть известен

Слайд 20Лекция 03.10.11г.
Динамические массивы
В тех случаях, когда размер массива невозможно знать

заранее (например, эта величина вводится человеком), можно использовать динамические массивы,

память для которых выделяется динамически системной функцией
void *calloc(size_t n, size_t r)
где n – количество элементов массива, r – размер элемента в байтах.
Функция возвращает нетипизированный указатель на начало массива.

#include
#include
int sum(int m[], int n) {
int i, s;
for(i=0, s=0; i return s;
}
int main() {
int i, n, *z;
printf("input n : "); scanf("%d", &n);
z = (int *)calloc(n, sizeof(*z));
for(i=0; i printf("summa = %d\n", sum(z, n));
system("PAUSE");
return 0;
}

Лекция 03.10.11г.Динамические массивыВ тех случаях, когда размер массива невозможно знать заранее (например, эта величина вводится человеком), можно

Слайд 21Лекция 03.10.11г.
Версия только с указателями
В этой версии программы используются только

указатели.
#include
#include
int sum(int *m, int n) {
int i,

s;
for(i=0, s=0; i return s;
}
int main() {
int i, n;
int *z, *y;
printf("input n : "); scanf("%d", &n);
y = z = (int *)calloc(n, sizeof(*z));
for(i=0; i printf("summa = %d\n", sum(y, n));
system("PAUSE");
return 0;
}
Лекция 03.10.11г.Версия только с указателямиВ этой версии программы используются только указатели.#include #include int sum(int *m, int n)

Слайд 22Лекция 03.10.11г.
Освобождение памяти
В программировании встречается неприятная ситуация, которая называется «утечка

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

функцией calloc. Для устранения этой ситуации рекомендуется освобождать неиспользуемую память с помощью функции
void free(void *p)
где p – нетипизированный указатель, получивший значение в результате вызова calloc.


int sum(int m[], int n) {
int i, s;
for(i=0, s=0; i return s;
}
int main() {
int i, n, *z;
void *p;
printf("input n : "); scanf("%d", &n);
z = (int *)(p = calloc(n, sizeof(*z)));
for(i=0; i printf("summa = %d\n", sum(z, n));
free(p);
system("PAUSE");
return 0;
}

Лекция 03.10.11г.Освобождение памятиВ программировании встречается неприятная ситуация, которая называется «утечка памяти». Эта ситуация может возникнуть при использовании

Обратная связь

Если не удалось найти и скачать доклад-презентацию, Вы можете заказать его на нашем сайте. Мы постараемся найти нужный Вам материал и отправим по электронной почте. Не стесняйтесь обращаться к нам, если у вас возникли вопросы или пожелания:

Email: Нажмите что бы посмотреть 

Что такое TheSlide.ru?

Это сайт презентации, докладов, проектов в PowerPoint. Здесь удобно  хранить и делиться своими презентациями с другими пользователями.


Для правообладателей

Яндекс.Метрика