![]() |
Адреса, адресные операторы
Читаю книгу по С++. Дошел до главы "Первое знакомство с указателями в С++", прочитал, и не понял для чего они нужны? Дальше в книге посмотрел листинги программ и эти операторы там активно используются.
Прошу помощи в объяснении. И во всех ли программах нужно это? |
Это делается для экономии памяти, т.к. легче хранить и передавать адрес ячейки в которой хранится число или значение, чем использовать несколько копий этого значения. В простых программах можно и без них.
Ну вот как скажем, у тебя есть пять складов по 100 кв. метров каждый, тебе ведь не будет удобно каждый раз все эти пять складов с содержимым перевозить на машине по городу для торговли? Нет. А проще дать адрес этих складов, написаный на бумажке. |
Технически, при вызове процедуры и передачи ей в качестве параметра некой переменной, допустим массива, будет передан весь массив. Если эта процедура рекурсивная, то массив будет передаваться каждый раз при вызове. Соотв потребление памяти будет расти прямо пропорционально числу вызов.
Для небольших величин затраты не существенны. Но если задействован баааальшой массив, то эффективнее, с т.з. быстродействия(меньше передаваемый объем) и экономии памяти(отсутствие необходимости дублирования), передать указатель на этот массив. Указатель говорит, что по такому адресу, в памяти хранится массив необходимых данных. В качестве другого примера, мы можем(с использованием соотв. структур) строить любые структуры. (коломбурчик :)) Это хорошо иллюстрируется графом или деревом. Практическим примером может служить создание дерева арифметического выражения и создание каким-то там обходом Польской обратной записи. (как обходить знаю, название не помню :() Можно попробовать организовать список, двунаправленный список, стек. |
Цитата:
Цитата:
Подскажите пожалуйста как это сделать, желательно с примером :) |
EvgeniyQQQ, я честно сознаюсь в том, что я не знаю С++. И предыдущий пост базируется на знаниях языка Pascal. (Да и то, надо будет в отладчик залезть, уточнить).
Так что извините, примеров не будет :) |
Цитата:
Код:
void func(int array[100]) //передаётся копия |
ganselo, спасибо за ответ. Но таким образом мы передаём массив по ссылке, т.е. саму ссылку на массив, а не копию всего массива.
Меня же интересует передача в функцию массива по значению. Что в принципе, на сколько мне известно, не возможно. Цитата:
Вот код, который подтверждает то, что в приведённом вами способе передаётся массив по ссылке, а не по значению: Код:
void func(int array[100]) //передаётся ссылка |
Medic84,
Цитата:
При выполнении программ процессор напрямую может работать только с двумя типами данных - это "локальные" и "глобальные" переменные. Для хранения локальных переменных текущей функции в стеке программы выделяется область памяти, где каждая переменная размещается на определённом растоянии (смещении) от вершины стека. Адрес вершины хранится в соответствующем регистре (ячейка памяти) процессора. То есть, допустим пользователь описал в программе переменные "A", "B" и "C" целочисленного типа. Компилятор, при создании машинного кода программы, преобразует имя переменных в записи вида [Стек+16], [Стек+20] и [Стек+24]. Соответственно команда "C = A + B" будет выглядеть, как в ячейку памяти [Стек+24] записать [Стек+16] + [Стек+20]. Глобальные переменные аналогично размещаются в определённой области памяти вне стека, и начальный адрес этой области также известен процессору. Но стек и глобальная область имеют очень небольшой размер. Как же программе использовать остальную область памяти (она же "куча")? Через эти самые "указатели". Сначала в стеке создаётся переменная типа "указатель" - её размер обычно составляет 32 бита (4 байта), реже - 64 (для работы в соответствующем режиме на 64-хразрядных процессорах). Потом, по мере выполнения функции, в этот указатель помещается адрес объекта - порядковый номер первой ячейки памяти. Адрес может быть любым - переменной из используемой области стека (текущей фукнции), переменной старших функций (из которой была вызвана текущая), глобальной переменной. Но чаще всего в указатель записывается адрес объекта, для которого особыми командами в "куче" был выделен участок памяти. Соответственно команда " *pC = A + B" означает "в ячейку памяти по адресу ячейки [Стек+24] записать результат [Стек+16] + [Стек+20]". При работе с указателями в С++ используются две команды - & (получение адреса) и * (обращение по адресу). Конечно, официально они называются иначе, вот только официальные названия звучат очень заумно и совершенно непонятно :) *pA = B . Значение переменной B помещается в ячейку памяти, адрес которой содержится в указателе pA pA = &B . Адрес ячейки памяти переменной B записывается в указатель pA (теперь, записывая что-то в *pA, мы изменим значение B) Можно даже так - *pA = *pB - значение ячейки памяти, адрес которой содержится в указателе pB, записывается в ячейку памяти, адрес которой содержится в указателе pA А вот если сделать pA = pB, то оба этих указателя станут указывать на одну и ту же переменную |
Зачем это всё вообще надо
1. Для работы с большими объёмами данных То есть, в "куче" создаётся "тяжёлый" объект (например, изображение), а в стеке программа хранит только маленький указатель на этот объект. 2. Чтобы избежать лишнего копирования больших объёмов данных То есть, можно написать функцию как void ShowPicture (TPicture Picture), а можно как void ShowPicture (TPicture *Picture). В первом случае в области стека функции будет создана копия картинки (размером в пару-тройку мегабайт ), а во втором будет передан лишь маленький указатель. Очень актуально для всех программ сложнее "Hello, World!" :) 3. Чтобы изменить объект данных в вызываемой функции Если передать в функцию копию объекта, то после выполнения функции стек сократится обратно,*и все внесённые изменения будут утеряны. А*если передать указатель на объект, то функция будет изменять именно этот объект. 4. Чтобы получить несколько результатов в одной функции (частный случай п.3) К примеру, функция SinCos (long double Angle, long double *Sinus, long double *Cosinus) будет записывать синус и косинус угла в ячейки памяти, указатели на которые программист укажет в качестве второго и третьего параметров. Пример вызова - SinCos (Alpha, &SinA, &CosA) Частным случаем указателей являются "ссылки", у которых при объявлении вместо знака * ставится & Код:
int A, B; // объявляются две переменные - при их использовании сразу производится обращение к значению по адресу ссылки (для указателей нужно ставить символ * ) - нельзя переназначить ссылку на другой объект - при объявлении ссылки нужно сразу указывать объект Пример использования ссылки Код:
int Array [100]; Обычно указатели используют для работы с объектами больших размеров (строки, массивы, объекты классов), а ссылки - для объектов поменьше (переменные простых типов, структуры). Просто потому,*что для указателей в отладчике отображаются их адреса, а для ссылок - значения по адресу. |
El Scorpio, тебе в преподаватели пора по C++:)
|
Могу ещё добавить, что в случае использования ссылок на массив элементов или объект, нет необходимости заботиться об освобождении памяти. А в случае использования указателей, за ней придётся следить :)
|
Я в шоке О_о...
El Scorpio - спасибо большое |
Medic84, вникай.
Без понимания того,*что есть указатель (маленькая цифра, говорящая процессору "информация лежит вон там"), дальше книги по С++ читать бесполезно. Особенно потому, что во многих "умных" книгах авторы используют заумные слова вроде "разыменование указателя", которые ни черта не способствуют пониманию смысла программы. И*ещё. Очень часто в "умных" книгах используются очень глупые примеры работы со строковыми значениями. Точнее говоря, в C++ вообще нет встроенного типа данных "строка" - есть только символ (char). А "строка" - это не более,*чем массив символов, заканчивающихся нулём. И не тем нулём, который символ '0', а нулевым символом (может обозначаться,*как символ '\0' - юниксоиды поймут). Посему*в книгах чаще всего используют "простые примеры" с типами данных "указатель на символ" (char *). Данный указатель содержит в себе лишь адрес первого элемента массива символов. Однако одна интересная особенность большинства сред разработки - изображать данные указатели,*как текст, на который они указывают - вводит начинающих программистов в заблуждение. Посему первое правило начинающего программиста - никогда не использовать char*. Если нужны строки - лучше использовать соответствующие классы-оболочки (сложные типы данных, скрывающие большинство действий в своих функциях) - string, AnsiString, CString и так далее. Использование объектов этих типов позволяет упростить работу со строками в 9000 раз. Я не шучу - в С++ при работе с символьными массивами программисту приходится использовать десятки функций, половина из которых легко могут привести к критическим сбоям в работе программы, и самостоятельно выполнять работу по выделению/освобождению памяти в "куче". Даже опытные кодеры предпочитают использовать char* только там, где требуется наивысшая производительность |
Время: 03:55. |
Время: 03:55.
© OSzone.net 2001-