Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.09.02;
Скачать: CL | DM;

Вниз

Data Pointer и все все все...   Найти похожие ветки 

 
Салат   (2007-08-06 19:40) [0]

Мне стыдно но я совсем не умею работать с указателями (Pointers) :( Расскажите в двух словах чайнику как через pointer работать с переменными (желательно на примере, где pointer будет указывать на record если такое возможно. если уж совсем близко то на примере TServerSocket.Socket.Connections[0].Data).


 
JanMihail ©   (2007-08-06 21:58) [1]

type
 MyRecord = record
    str1: String[255];
    str2: String[255];
    i     : Integer;
end;

var
 Form1: TForm1;
 a: MyRecord;
 p: Pointer;

procedure TForm1.FormCreate(Sender: TObject);
begin
 p := @a;
end;


 
JanMihail ©   (2007-08-06 22:04) [2]

Сам толком не поинмаю, что за Pointer


 
Юрий Зотов ©   (2007-08-06 22:33) [3]

Построение стека записей:

type
 PData = ^TData; // Тип указателя на элемент стека
 TData = record // Тип элемента стека
   I: integer; // Данные элемента
   S: string;  // Еще данные элемента
   Prev: PData; // Указатель на предыдущий элемент
 end;

var
 Last: PData = nil; // Указатель на верхний элемент стека (пока пустого).

proсedure Push; // Создать новый элемент и затолкнуть его в стек.
var
 P: PData;
begin
 New(P); // Создали в динамической памяти новый элемент по адресу P.
 P^.Prev := Last; // Разместили новый элемент на вершине стека.
 Last := P // Протолкнули стек.
end;

proсedure Pop; // Вытолкнуть из стека верхний элемент и уничтожить его.
var
 P: PData;
begin
 if Last = nil then // Если стек пустой, то ничего делать не надо.
   Exit;
 P := Last; // Запомнили адрес верхнего элемента.
 Last := Last^.Prev; // Вытолкнули верхний элемент из стека.
 Dispose(P); // Освободили память, которую он занимал.
end;

procedure Clear; // Очистить стек и освободить всю занимаемую им память.
begin
 while Last <> nil do
   Pop
end;

var
 i: integer;
begin
 // Создать стек из 10 элементов
 for i := 0 to 9 do
 begin
   Push;
   Last^.I := i;
   Last^.S := "Элемент номер " + IntToStr(Last^.I);
 end;
 // Уничтожить стек
 Clear
end;


 
Салат   (2007-08-06 22:48) [4]

Спасибо, разобрался кое как :) вкривь и вкось сделал на массивах, но зато надежно, просто и работает. Для таких же как я может пригодится мой опыт:
TRecord = record
data: String[255];
end;

var
p: Pointer;
clients: array[0..2] of TRecord;

begin
p := @clients[1]; // связываем указатель с каким-то элементом массива
ShowMessage(TRecord(p^).data); // доступ к этому элементу
end;



 
MetalFan ©   (2007-08-06 22:57) [5]

хм... я раньше тоже не понимал) но однажды понял... но вот на пальцах объяснить не могу..
единственное, что в голову приходит: Pointer - это в общем случае просто 4 байта в памяти, "указывающие" на какую-либо область памяти или на nil...
к примеру:
type
 TRec = record
   Int: Integer;
   W: Word;
   Str: string;
 end;

procedure Proc;
var
 lInt: Integer;
 lStr: string;
 lRec: TRec;
 lPtr: Pointer;
begin
 lInt := 10;
 lPtr := @lInt;
 Integer(lPtr^) := 20;//изменили значение lInt
 lStr := "string";
 lPtr := @lStr;
 string(lPtr^) := "asd";// изменили значение lStr;
 lRec.Int := 100;
 lRec.W := 1;
 lRec.Str := "sadssd";
 lPtr := @lRec;
 TRec(lPtr^).Int := 1;
 TRec(lPtr^).W := 2;
 TRec(lPtr^).Str := "nnn";
end;


 
{RASkov} ©   (2007-08-06 22:57) [6]

> [4] Салат   (06.08.07 22:48)

В данном примере никакого смысла нет в указателях....
И вообще указатели лучше не использовать, если нет на то особой необходимости. См пример из [3]
type
PData = ^TData; // Тип указателя на элемент стека
TData = record // Тип элемента стека
  I: integer; // Данные элемента
  S: string;  // Еще данные элемента
  Prev: PData; // Указатель на предыдущий элемент <----------- Иначе никак, а то можно было б лучше так Prev: TData
end;


 
Юрий Зотов ©   (2007-08-06 23:07) [7]

> Салат   (06.08.07 22:48) [4]

В Вашем варианте указатель вовсе не нужен:
ShowMessage(clients[1].data);
И без всяких указателей.

Вообще, указатель - это просто-напросто переменная, которая содержит в себе адрес, только и всего. Если значение указателя равно nil, то это значит, что он пустой (то есть, не содержит в себе никакого адреса). Ничего сложного.


 
{RASkov} ©   (2007-08-06 23:07) [8]

> [5] MetalFan ©   (06.08.07 22:57)

Собственно... тоже пример по работе с указателями, но не их необходимости :)

Мои посты может быть такие, из-за того, что я панически "боюсь" их(указатели) :) И без надобности их не трогою....

Кстати вопрос по ним, если можно задам здесь в тему.... вот из [3]
Last^.S := "Элемент номер " + IntToStr(Last^.I);
Вот эти выделенные "крыши" - они непосредственно необходимы в данном месте или только для лучшего понимания, что есть что ??
Так как
Last.S := "Элемент номер " + IntToStr(Last.I);
Результат тот же....
"Крыша" - разыменование указателя, это я знаю..... Делфи наверняка, если их(крыши) не ставить, сама их подставляет, так?
Вообщем с указателями только путаница :)


 
MetalFan ©   (2007-08-06 23:24) [9]

в случае с указателями на записи крышки можно и не ставить, среда и так поймет... но лучше, имхо, для наглядности все таки ставить ^


 
Юрий Зотов ©   (2007-08-06 23:36) [10]

> {RASkov} ©   (06.08.07 23:07) [8]

Да, компилятор Deplhi сам разыменовывает указатели. Любая объектная переменная ведь тоже указатель, поэтому по правилам Паскаля надо было бы писать, строго говоря, так:
Button1^.Caption := "OK";
Но мы крышку не пишем, компилятор сам разыменовывает Button1.

Чтобы не путаться в своем же коде, я пользуюсь таким правилом: объектные переменные пусть разыменовывает компилятор, а простые (необъектные) указатели разыменовываю сам, по-старинке, явным образом. Это позволяет сразу отличать указатели от других типов переменных и правильно с ними работать (особенно, при использовании данных вида "указатель на указатель", когда требуется многократное разыменование).

> Вообщем с указателями только путаница

Никакой путаницы нет - надо только ОДИН раз понять, что это такое, а потом все будет очень просто. Указатели - штука для серьезного программиста совершенно необходимая (я бы даже сказал сильнее - кто не понимает указателей, того назвать программистом вообще нельзя). Без указателей нвозможно работать с динамической памятью напрямую (а это требуется частенько - например, при использовании многих функций WinAPI). Без понимания указателей нельзя толком понять и многие термины Delphi (длинная строка, динамический массив, объект, класс, VMT... и многое другое). Соответственно, не понимая этого, нельзя с этим и свободно работать - и именно от непонимания указателей бывает и много багов в программах, и много вопросов на форумах.


 
Anatoly Podgoretsky ©   (2007-08-06 23:59) [11]

> Юрий Зотов  (06.08.2007 23:36:10)  [10]

> Да, компилятор Deplhi сам разыменовывает указатели. Любая объектная переменная ведь
> тоже указатель, поэтому по правилам Паскаля надо было бы писать, строго говоря, так:
> Button1^.Caption := "OK";
> Но мы крышку не пишем, компилятор сам разыменовывает Button1.

Есть и исключение "Do not apply the dereference operator (^) to a dynamic-array variabl"


 
{RASkov} ©   (2007-08-07 00:05) [12]

> [10] Юрий Зотов ©   (06.08.07 23:36)

Спасибо.... есть польза.
Т.е. у типизированных указателей крышу ставить не обязательно, но желательно, а у не типизированных (Pointer) - обязательно.

 TMyRecord = record
   str1  : String[255];
   str2  : String[255];
   i     : Integer;
 end;
 PMyRecord = ^TMyRecord;

var
 P: Pointer;
 P1: PMyRecord;
.....
 TMyRecord(P^).i:=5; - обязательна ^
 P1^.i:=5; - ^ не обязательна
 P1.i:=5;



> Никакой путаницы нет - надо только ОДИН раз понять

Самое обидное, это то, что я эти указатели, то понимаю, то нифика не понимаю... т.е. то они мне легко и просто даются, то как баран на них смотрю.
Другими словами можно конечно сказать, что я их не понимаю :(
Когда этих "крышек" нет то все ок, но когда они появляются, то вот здесь и бывают провалы...

Кстати.... пример в [3] довольно легко прочитался и понялся :) А в другом месте((примере), образно) могу запутаться :(
Ладно.... еще раз спасибо и сорри за оффтоп


 
{RASkov} ©   (2007-08-07 00:26) [13]

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

Из соседней ветки:
Как все же правильнее, так:
procedure TForm1.Button4Click(Sender: TObject);
const Clr: array [0..2] of TColor = (clRed, clBlue, clYellow);
begin
 ListView1.Items[2].Data := @Clr[(Sender as TButton).Tag]; //@ - адрес локальной константы
 ListView1.Invalidate;
end;

procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
 Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
 if Item.Data<>nil then ListView1.Canvas.Brush.Color := TColor(Item.Data^); //нужно разыминование, и почему этот адрес живой
end;

или так:
procedure TForm1.Button4Click(Sender: TObject);
const Clr: array [0..2] of TColor = (clRed, clBlue, clYellow);
begin
 ListView1.Items[2].Data := Pointer(Clr[(Sender as TButton).Tag]); //приводим к Pointer"у
 ListView1.Invalidate;
end;

procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
 Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
 if Item.Data<>nil then ListView1.Canvas.Brush.Color := TColor(Item.Data); //Вроде нетипизированный указатель, а ^ не нужна
end;

или это одно и тоже? А то даю советы, а тут сам запутался :(
Оба примера рабочие.
Я так понимаю:
В первом примере- мы берем адрес локальной константы и пишем его в Data, а потом как этот локал адрес остается при разыменовании Data в другом месте? Повезло?
Во втором - ну здесь вроде более менее понятно... единственное только с крышкой не совсем понял...
Пора спать :)


 
{RASkov} ©   (2007-08-07 00:38) [14]

Второй пример разобрался...
Пишем вместо адреса - некое число (clRed, clBlue...)
И потом этот адрес(грубо - число) преобразуем к TColor и все нормально, поэтому нам "не страшно", что была локальная константа...
А вот в первом пишем адрес константы, а потом разыменовываем и преобразуем к TColor некий адрес констаны, и ... в общем непонятно...
Точно пора спать (


 
Инс ©   (2007-08-07 00:43) [15]

В первом случае мы в тэге сохраняем именно адрес. Потом, когда нужно получить значение цвета - разадресовываем. Во втором случае, мы сохраняем в тэге не адрес, а само значение, а приведение к Pointer делаем чтобы компилятор не ругался (так как свойство Data должно быть типа Pointer). Вот мы и заставляем компилятор думать, что TColor это Pointer. Раз в тэге НЕ адрес, то и разадресовывать не надо.


 
Инс ©   (2007-08-07 00:47) [16]


> а потом разыменовываем и преобразуем к TColor некий адрес
> констаны

Уже не адрес приводим, а значение. Сначала разадресация (теперь у нас вместо адреса значение, только непонятно какого типа), а потом - приводим к типу TColor. Я бы лучше написал так, понятнее:
... := PColor(Item.Data)^;


 
Юрий Зотов ©   (2007-08-07 00:53) [17]

> {RASkov} ©   (07.08.07 00:26) [13]

В первом примере указатель живой потому, что компилятор разместил локальную константу в статической памяти. Ее адрес фиксирован и жив, пока жива сама программа.

Разыменование в первом примере нужно потому, что в Item.Data мы запоминали АДРЕС константы, а получить хотим ЗНАЧЕНИЕ этой константы. Во втором же примере в Item.Data мы запоминали не АДРЕС константы, а ее ЗНАЧЕНИЕ - поэтому разыменование не нужно.

Во втором примере использовался тот факт, что длина типов TColor и Pointer одинаковая (4 байта на 32-битных платформах), поэтому, если некая область (Item.Data) хранит некое число, то это число может быть и адресом, и цветом (да и вообще чем угодно: что мы туда положили - то там и будет). А чтобы компилятор не ругался на несоответствие типов, в коде второго примера дважды использовано явное приведение типа: при запоминании цвета в Item.Data тип TColor приводится к типу Pointer, а при чтении - наоборот.


 
Инс ©   (2007-08-07 00:54) [18]


> [17] Юрий Зотов ©   (07.08.07 00:53)


Дуэтом :)


 
{RASkov} ©   (2007-08-07 00:56) [19]

Со вторым разобрался, а вот...
> В первом случае мы в тэге сохраняем именно адрес. Потом,
> когда нужно получить значение цвета - разадресовываем.

Мне все вроде понятно, будь глобальные объекты, но..... У локальных переменных, констант адреса остаються? Они(локальные пер/конст) вроде как при входе "инициализируються" при выходе освобождаются (память), .... как адрес на константу "живет" в другом методе....
Нет... я с указателями наверное крепко не подружусь :( Вроде все понятно, а с другой стороны...

> [16] Инс ©   (07.08.07 00:47)

Мне все понятно, но по отношению к глобальным вещам, а как это у локальных обстоит.... т.е. константа:
const Clr: array [0..2] of TColor = (clRed, clBlue, clYellow);
всегда в памяти присутствует?


 
{RASkov} ©   (2007-08-07 00:57) [20]

> [17] Юрий Зотов ©   (07.08.07 00:53)
> Ее адрес фиксирован и жив, пока жива сама программа.

Все встало на свои места.... Спасибо.
Все понятно теперь.... разобрался.


 
Инс ©   (2007-08-07 00:59) [21]


> всегда в памяти присутствует?

Да, конечно. Понятие "локальная" для констант подразумевает только то, что вы не сможете к ней обратиться из-за пределов подпрограммы. Но константа на то и константа, ее значение нельзя изменить, и для этого компилятор располагает ее в в области памяти, защищенной от записи, но не в стеке.


 
Инс ©   (2007-08-07 01:04) [22]

Вы сами смогли бы это проверить. Адрес константы имеет значение, близкое к $00400000 - это адрес, по которому образ exe загружен в виртуальное АП. Из этого можно сделать вывод, что константа "вшита" в exe. Стек располагается совсем по другому адресу.


 
{RASkov} ©   (2007-08-07 01:16) [23]

> [21] Инс ©   (07.08.07 00:59)

Точно,
И вот доказательства:
Так не работает:
procedure TForm1.Button4Click(Sender: TObject);
var Clr: array [0..2] of TColor;
begin
 Clr[0]:=clRed; Clr[1]:=clBlue; Clr[2]:=clYellow;
 ListView1.Items[2].Data:=@Clr[(Sender as TButton).Tag]; //пишем адрес локальной переменной
 ListView1.Invalidate;
end;

if Item.Data<>nil then ListView1.Canvas.Brush.Color := TColor(Item.Data^); //пытаемся взять данные по "паленому" адресу


А так все ок:
procedure TForm1.Button4Click(Sender: TObject);
var Clr: array [0..2] of TColor;
begin
 Clr[0]:=clRed; Clr[1]:=clBlue; Clr[2]:=clYellow;
 ListView1.Items[2].Data:=Pointer(Clr[(Sender as TButton).Tag]); //Вместо адреса на данные используем сам адрес в качестве значения цвета
 ListView1.Invalidate;
end;

if Item.Data<>nil then ListView1.Canvas.Brush.Color := TColor(Item.Data); //Адрес Pointer используем в качестве цвета, И разыменование приведет к ошибке, так как данных по этому адресу нет....



> [22] Инс ©   (07.08.07 01:04)
> Адрес константы имеет значение, близкое к $00400000 - это
> адрес, по которому образ exe загружен в виртуальное АП

Я не настолько знаком с этим, но вот .... учусь потихоньку :) Надеюсь урок усвоил правильно. Спасибо.


 
Германн ©   (2007-08-07 02:18) [24]


> {RASkov} ©   (07.08.07 01:16) [23]
>
> > [21] Инс ©   (07.08.07 00:59)
>
> Точно,
> И вот доказательства:
>

Из сего примера первый вывод, который нужно сделать - не стОит плодить лишние сущности! А переменная Clr: array [0..2] of TColor именно таковой и является. Не было б её, не было б и ошибок :)


 
Салат   (2007-08-07 07:09) [25]

Кто думает что указатели в моем случае не нужны сильно заблуждаются :) В моем примере было все очень упрощено, на деле же нужно было связать ServerSocket.Socket.Connections[].Data (который Pointer) с некими данными которые идентифицировали бы этот сокет.


 
{RASkov} ©   (2007-08-07 13:18) [26]

> [25] Салат   (07.08.07 07:09)
> Кто думает что указатели в моем случае не нужны сильно заблуждаются

И нечего здесь думать - не нужны они, тыж пример привел... [4]

> В моем примере было все очень упрощено

Ну так приведи другой пример.... наверняка они и там не нужны будут :)

К сожалению мне вот это:

> ServerSocket.Socket.Connections[].Data (который Pointer)
> с некими данными которые идентифицировали бы этот сокет.

... не о чем :(

> [24] Германн ©   (07.08.07 02:18)

Этот пример был только для доказательства, что адреса локальных констант сохраняються, а адреса переменных нет :)

А про указатели я свое мнение из [6] не поменяю.... пока, хоть и еще более ближе к ним(указателям) стал в этой ветке ;)


 
Anatoly Podgoretsky ©   (2007-08-07 13:24) [27]

> {RASkov}  (07.08.2007 13:18:26)  [26]

Менять не обязательно, а вот знания обязательно, иначе ущербность в знаниях.


 
Сергей М. ©   (2007-08-07 13:31) [28]


> Салат   (07.08.07 07:09) [25]
>
> Кто думает что указатели в моем случае не нужны сильно заблуждаются


Да что ты говоришь !?)

А мужики-то и не знают, что у тебя stNonBlocking)


 
{RASkov} ©   (2007-08-07 13:34) [29]

> [27] Anatoly Podgoretsky ©   (07.08.07 13:24)

Абсолютно согласен.... и поэтому не стесняюсь спрашивать, если что не понятно....


 
MsGuns ©   (2007-08-07 16:45) [30]

Указатели совершенно необходимы при работе со СПИСКАМИ однотипных сложных объектов (рекордов).



Страницы: 1 вся ветка

Текущий архив: 2007.09.02;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.028 c
2-1186593827
Sonia
2007-08-08 21:23
2007.09.02
Проблема с настройками клиента Oracle (а может еще чего-то)


2-1186834889
Riply
2007-08-11 16:21
2007.09.02
MFT и чтение "неправильных" записей.


2-1186512899
Цукор5
2007-08-07 22:54
2007.09.02
Кол-во символом в числе.


15-1186130415
исследователь
2007-08-03 12:40
2007.09.02
Непонятная строчка кода


8-1164745849
Oburec
2006-11-28 23:30
2007.09.02
Как отобразить графику рисуемую c OpenGL на простой форме)