КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Операции над указателями (косвенная адресация)
Связь указателей и массивов. Идентификатор массива указывает адрес памяти, начиная с которого он расположен, т.е. адрес его первого элемента. Работа с массивами тесно связана с применением указателей. Пусть объявлен массив a из 5 целочисленных элементов:
int a[5];
a
4000 4004 4008 4012 4016 Здесь приведено символическое изображение оперативной памяти, выделенной компилятором для объявленного целочисленного массива а[5]. Адрес массива выбирается компилятором в зависимости от размера доступной памяти, наличия других переменных и массивов и др. Для конкретности, здесь положено значение адреса, равное 4000. В реальной программе вместо 4000 может быть другое значение, но относительное положение элементов массива всегда остается постоянным. В языке С идентификаторы массивов считаются константными указателями (т.е. в данном примере а "имеет значение" 4000). Такую константу можно присвоить переменной типа указатель, но нельзя подвергать преобразованиям, например:
int a[5], *q; q = a; // Правильно - присваивание константы переменной a = q; // Ошибка: в левой части - указатель-константа
Именно потому, что имена массивов считаются константными указателями, в языке Си нельзя непосредственно присваивать массивы друг другу (хотя структуры, включающие массивы как поля, целиком присваивать друг другу можно!) Однако операция sizeof для массивов все же дает размер массива, а не указателя:
int n = sizeof(a) / sizeof(*a); // n=5, т.к. sizeof(a)=20, sizeof(int)=4
int m = sizeof(q) / sizeof(*q); // m=1, т.к. sizeof(int*)=4, sizeof(int)=4
Элементы одного массива хранятся в памяти подряд, поэтому адрес каждого последующего элемента больше адреса предыдущего на размер одного элемента, т.е на sizeof(тип) байт, где тип - тип элемента массива. Поэтому, зная адрес одного элемента, легко вычислить адрес другого.
В языке С программист имеет возможность воспользоваться этим с помощью арифметических операций с указателями, т.е. прибавлением к ним (или вычитанием из них) целой величины: p+i p-i p+=i p-=i p++ p-- где: p - указатель, i - целочисленное выражение.
Допускается также вычитание указателей: p1-p2 где p1, p2 - указатели. Результатом вычитания является целое число.
Чтобы программист не был вынужден при этом каждый раз учитывать размер элемента, в языке Си принято правило: все арифметические операции с указателями выполняются в единицах памяти того типа объекта, на который ссылается этот указатель. Иными словами, операция p++ означает реальное увеличение p не на единицу, а на sizeof(*p); при этом p как раз будет указывать на следующий элемент массива. Аналогично, выражение p+i означает в действительности p+ i*sizeof(*p), т.е. смещение на i элементов. Заметим, что из-за этого указатели на объекты разных типов, первоначально равные, могут стать неравными при прибавлении к ним одной и той же величины:
int a[5], *q=a; // i указывает на a[0] double *d=(double*)q; // Теперь d=q (не считая разницы в типах) q++; d++; // теперь d>q, т.к. хранимый в d адрес // увеличился на 8, а хранимый в q - на 4 q++; // а теперь снова d=q, и равно &a[2]
Однако на практике подобная адресация одного и того же участка памяти указателями разных типов редко имеет смысл. В силу сказанного выше, адрес i-го элемента массива A всегда можно записывать и как &A[i], и как A+i. Итак, для массивов запись A[i] и *(A+i) эквивалентна. Для удобства операций с указателями, в языке С введено такое же правило записи и для них: p[i] равносильно *(p+i) где p - указатель, i - целочисленное выражение. Иными словами, для обращения к i-му (считая от места, куда указывает p) элементу массива вместо записи *(p+i) можно писать короче: p[i]. Соответственно, и для указателей, и для массивов запись *p эквивалентна p[0]
Пример: int a[5], *q=a; // Инициализация q: q указывает на a[0] //(Здесь * перед q означает объявление его типа(указатель), а не разадресацию) q++; *q=40; // Означает a[1]=40; q[3]=70; // Означает a[4]=70; q[-1]=22; // Означает a[0]=22;
Операции с указателями бывают особенно полезны для массивов char. (Напомним, что в любом месте, где допустима строка как массив char, допустим также указатель на char). С их помощью можно, например, обратиться к середине строки:
char s[]="Hello, world!"; cout<<s+7; // Будет выведен текст: world!
Дата добавления: 2014-01-05; Просмотров: 420; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |