Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   Адреса, адресные операторы (http://forum.oszone.net/showthread.php?t=173661)

Medic84 20-04-2010 21:04 1397209

Адреса, адресные операторы
 
Читаю книгу по С++. Дошел до главы "Первое знакомство с указателями в С++", прочитал, и не понял для чего они нужны? Дальше в книге посмотрел листинги программ и эти операторы там активно используются.

Прошу помощи в объяснении. И во всех ли программах нужно это?

Drongo 20-04-2010 21:51 1397241

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

Ну вот как скажем, у тебя есть пять складов по 100 кв. метров каждый, тебе ведь не будет удобно каждый раз все эти пять складов с содержимым перевозить на машине по городу для торговли? Нет. А проще дать адрес этих складов, написаный на бумажке.

lxa85 20-04-2010 22:34 1397274

Технически, при вызове процедуры и передачи ей в качестве параметра некой переменной, допустим массива, будет передан весь массив. Если эта процедура рекурсивная, то массив будет передаваться каждый раз при вызове. Соотв потребление памяти будет расти прямо пропорционально числу вызов.
Для небольших величин затраты не существенны. Но если задействован баааальшой массив, то эффективнее, с т.з. быстродействия(меньше передаваемый объем) и экономии памяти(отсутствие необходимости дублирования), передать указатель на этот массив.
Указатель говорит, что по такому адресу, в памяти хранится массив необходимых данных.

В качестве другого примера, мы можем(с использованием соотв. структур) строить любые структуры. (коломбурчик :)) Это хорошо иллюстрируется графом или деревом.
Практическим примером может служить создание дерева арифметического выражения и создание каким-то там обходом Польской обратной записи. (как обходить знаю, название не помню :()
Можно попробовать организовать список, двунаправленный список, стек.

EvgeniyQQQ 21-04-2010 10:20 1397490

Цитата:

Цитата lxa85
Технически, при вызове процедуры и передачи ей в качестве параметра некой переменной »

Правильнее писать функции. Так как в C++, на сколько я знаю :) , процедур нет.

Цитата:

Цитата lxa85
передачи ей в качестве параметра некой переменной, допустим массива, будет передан весь массив »

Несколько лет занимаюсь программированием, а вот как передать в функцию весь массив не знаю. Знаю как можно передать через указатель. Знаю как по средствам передачи ссылки. Например, в Java методу всегда передаётся ссылка на массив, это точно. То, что в C++ не так, для меня открытие :) Прям аж стыдно стало :(

Подскажите пожалуйста как это сделать, желательно с примером :)

lxa85 21-04-2010 10:46 1397505

EvgeniyQQQ, я честно сознаюсь в том, что я не знаю С++. И предыдущий пост базируется на знаниях языка Pascal. (Да и то, надо будет в отладчик залезть, уточнить).
Так что извините, примеров не будет :)

ganselo 21-04-2010 13:36 1397644

Цитата:

Цитата EvgeniyQQQ
а вот как передать в функцию весь массив не знаю. »

Если правильно понял, то так
Код:

void func(int array[100]) //передаётся копия
{
}

void func2(int array[100][100])
{
}

int main()
{
 int ar1[100], ar2[100][100];
func(ar1);
func2(ar2);
return 0;
}


EvgeniyQQQ 21-04-2010 15:02 1397713

ganselo, спасибо за ответ. Но таким образом мы передаём массив по ссылке, т.е. саму ссылку на массив, а не копию всего массива.
Меня же интересует передача в функцию массива по значению. Что в принципе, на сколько мне известно, не возможно.
Цитата:

Цитата EvgeniyQQQ
а вот как передать в функцию весь массив не знаю »

Это была скорее скрытая ирония, а не вопрос. Прошу прощения за это :sorry:

Вот код, который подтверждает то, что в приведённом вами способе передаётся массив по ссылке, а не по значению:
Код:

void func(int array[100]) //передаётся ссылка
{
        array[0] = 5;
}

int main()
{
        int ar1[100];
       
        ar1[0] = 17;

        printf("ar1[0] = %d\n", ar1[0]);
       
        func(ar1);

        printf("ar1[0] = %d\n", ar1[0]);

        return 0;
}


El Scorpio 22-04-2010 06:55 1398214

Medic84,
Цитата:

Цитата 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, то оба этих указателя станут указывать на одну и ту же переменную

El Scorpio 22-04-2010 07:47 1398231

Зачем это всё вообще надо
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 *pA = &A; // Указатель на переменную А
int &rA = A; // ссылка на переменную А

rA = B; // Значение B записывается в ячейку памяти по адресу, который содержится в ссылке rA
*pA = B; // Значение B записывается в ячейку памяти по адресу, который содержится в указателе pA

Ссылки от указателей отличаются тем, что
- при их использовании сразу производится обращение к значению по адресу ссылки (для указателей нужно ставить символ * )
- нельзя переназначить ссылку на другой объект
- при объявлении ссылки нужно сразу указывать объект

Пример использования ссылки
Код:

int Array [100];
for (i = 0; i < 100; i++)
{
  int &Item = Array [i];
  // а дальше идут 10 строк кода, где используется ссылка Item
}

Кроме того, если ту же функцию SinCos записать с параметрами (long double Angle, long double &Sinus, long double &Cosinus), то её вызов будет выглядеть так SinCos (Alpha, SinA, CosA)

Обычно указатели используют для работы с объектами больших размеров (строки, массивы, объекты классов), а ссылки - для объектов поменьше (переменные простых типов, структуры). Просто потому,*что для указателей в отладчике отображаются их адреса, а для ссылок - значения по адресу.

Delirium 22-04-2010 07:54 1398232

El Scorpio, тебе в преподаватели пора по C++:)

EvgeniyQQQ 22-04-2010 10:46 1398306

Могу ещё добавить, что в случае использования ссылок на массив элементов или объект, нет необходимости заботиться об освобождении памяти. А в случае использования указателей, за ней придётся следить :)

Medic84 22-04-2010 13:53 1398454

Я в шоке О_о...
El Scorpio - спасибо большое

El Scorpio 23-04-2010 06:07 1398991

Medic84, вникай.
Без понимания того,*что есть указатель (маленькая цифра, говорящая процессору "информация лежит вон там"), дальше книги по С++ читать бесполезно.
Особенно потому, что во многих "умных" книгах авторы используют заумные слова вроде "разыменование указателя", которые ни черта не способствуют пониманию смысла программы.

И*ещё. Очень часто в "умных" книгах используются очень глупые примеры работы со строковыми значениями.
Точнее говоря, в C++ вообще нет встроенного типа данных "строка" - есть только символ (char). А "строка" - это не более,*чем массив символов, заканчивающихся нулём. И не тем нулём, который символ '0', а нулевым символом (может обозначаться,*как символ '\0' - юниксоиды поймут).
Посему*в книгах чаще всего используют "простые примеры" с типами данных "указатель на символ" (char *). Данный указатель содержит в себе лишь адрес первого элемента массива символов. Однако одна интересная особенность большинства сред разработки - изображать данные указатели,*как текст, на который они указывают - вводит начинающих программистов в заблуждение.

Посему первое правило начинающего программиста - никогда не использовать char*.
Если нужны строки - лучше использовать соответствующие классы-оболочки (сложные типы данных, скрывающие большинство действий в своих функциях) - string, AnsiString, CString и так далее. Использование объектов этих типов позволяет упростить работу со строками в 9000 раз.
Я не шучу - в С++ при работе с символьными массивами программисту приходится использовать десятки функций, половина из которых легко могут привести к критическим сбоям в работе программы, и самостоятельно выполнять работу по выделению/освобождению памяти в "куче". Даже опытные кодеры предпочитают использовать char* только там, где требуется наивысшая производительность


Время: 03:55.

Время: 03:55.
© OSzone.net 2001-