Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2006.08.20;
Скачать: [xml.tar.bz2];

Вниз

Указатели   Найти похожие ветки 

 
начинающий1   (2006-07-26 03:15) [0]

Объясните. Бъюсь три часа.
Есть код:


 PGoods = ^TGoods;

 TGoods = record
   IDNumb:integer;
   Name,Developer,MadeIn,Info:string;
   Cash:real;
   Bitmap:TBitmap;
 end;


...


function GetGoods(IDNumb:integer):PGoods;
var
 Goods:TGoods;
 Res:PGoods;
begin
 with datamodule1.IBQuery1 do
   begin
     close;
     sql.Clear;
     sql.Add("select * from goods where IDNUMB="+inttostr(IDNumb));
     open;
     Goods.IDNumb:=fieldbyName("IDnumb").AsInteger;
     Goods.Name:=fieldbyName("Name").AsString;
     Goods.Developer:=fieldbyName("developer").AsString;
     Goods.MadeIn:=fieldbyName("madein").AsString;
     Goods.Cash:=fieldbyName("cash").Asfloat;
     Goods.Info:=fieldbyName("info").AsString;
   end;
 Res:=@Goods;
 result:=res;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
 GoodsPointer:PGoods;
 Goods:TGoods;
begin
 GoodsPointer:=GetGoods(strtoint(form1.Edit8.Text));
 Goods:=GoodsPointer^;
end;


Из функции GetGoods, как видно, получаем указатель на запись. Затем просто разыменовываем его. Так вот на этой строке

Goods:=GoodsPointer^;

выдает AV.
Говорит, что пишу в адрес в который писать не должен.
Функция GetGoods, читает из базы, вычисляет указатель. Передавая его в точку вызова передает именно его. Т.е. ошибка как я понимаю где-то не там. Блин, все же по детски.
Пробовал передавать не указатель, а саму запись. Передавать-то передает именно то, что нужно, но начинаешь с ней работать и там оказывается какая-то лабуда.
помогите.


 
Джо ©   (2006-07-26 03:27) [1]

Вот существенные исправления Вашего примера:

 PGoods = ^TGoods;

 TGoods = record
   IDNumb:integer;
   Name,Developer,MadeIn,Info: ShortString;
   Cash:real;
   Bitmap:TBitmap;
 end;

function GetGoods(IDNumb:integer):PGoods;
begin
 GetMem (Result,SizeOf(TGoods));
 // тут, для простоты, я просто присваиваю
 // фиксированные значения
 Result^.IDNumb := 1;
 Result^.Name := "2";
 Result^.Developer := "3";
 Result^.MadeIn := "4";
 Result^.Cash := 5;
 Result^.Info := "6";
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 GoodsPointer:PGoods;
 Goods:TGoods;
begin
 GoodsPointer := GetGoods(1);
 Goods := GoodsPointer^;
 ShowMessage (Goods.Name);
 FreeMem (GoodsPointer);
end;


Вам нужно почитать книгу или справку о работе с динамической памятью.
Безотносительно к последнему замечанию: зачем тут вообще все эти указатели и выделение/освобождение памяти, когда можно просто возвращать TGoods?!


 
Джо ©   (2006-07-26 03:35) [2]

Да, забыл, что нужно было объяснение. AV возникало от того, что локальная переменная Goods располагается на стеке, после выхода из функции возвращать на нее ссылку бессмысленно.


 
Джо ©   (2006-07-26 03:36) [3]

Про работу с БД: странная она :)


 
Указатели   (2006-07-26 05:08) [4]


> Джо ©


спасибо за внимание и столь подробное обяснение.
Однако, если позволите:
Как же так? Указатель такая же переменная как и целочисленная(кстати), почему я не могу вернуть как значение функции эту переменную и присвоить это значение в другую переменную такого же типа? а затем разыменовать этот указатель в готовую локальную переменную.
Я ценю ваши советы, поверьте, Вы один из тех кто постоянно и без насмешек помогает на этом форуме и не только мне, но мне кажется Вы не обратили внимание на заведенные локальные переменные, в которые присваиваются значения функции. И как мне кажется стек здесь уже не при делах.


 
stef ©   (2006-07-26 05:12) [5]

Что же касается
> function GetGoods(IDNumb:integer):PGoods;
> begin
>  GetMem (Result,SizeOf(TGoods));
>  // тут, для простоты, я просто присваиваю
>  // фиксированные значения
>  Result^.IDNumb := 1;
>  Result^.Name := "2";
>  Result^.Developer := "3";
>  Result^.MadeIn := "4";
>  Result^.Cash := 5;
>  Result^.Info := "6";
> end;

Неужели надо выделять память под результат функции?


 
stef ©   (2006-07-26 05:14) [6]

И уж совсем обнаглев, я добавлю - чем же странная работа с БД?


 
GrayFace ©   (2006-07-26 05:59) [7]

Указатели   (26.07.06 5:08) [4]
а затем разыменовать этот указатель в готовую локальную переменную.

Слова-то какие страшные. :) Я вот тоже не знаю, зачем - это у тебя надо спросить.
Res:=@Goods; - указатель указывает на переменную в стеке.
Goods:=GoodsPointer^; - копируешь память по адресу указателя в локальную переменную.
Что, собственно, непонятно?
А, вообще, AV возникло из-за наличия строк в структуре. Иначе бы просто в Goods была абракадабра. (если до сюда все понятно, могу разъяснить)

stef ©   (26.07.06 5:12) [5]
Неужели надо выделять память под результат функции?

Результат - указатель. Под него выделять память не нужно. А GetMem выделяет память и присваивет перемнной ссылку на нее.


 
Stakan ©   (2006-07-26 11:11) [8]

Я бы сделал следующим образом:
 PGoods = ^TGoods;

 TGoods = record
   IDNumb:integer;
   Name,Developer,MadeIn,Info: String; //Длинная строка
   Cash:real;
   Bitmap:TBitmap;
 end;

...

Procedure FillGoods(IDNumb:integer; Goods: PGoods);
begin
with datamodule1.IBQuery1 do
  begin
    close;
    sql.Clear;
    sql.Add("select * from goods where IDNUMB = :IDNUMB";
    ParamByName("IDNUMB").AsInteger := IDNumb;
    open;
    Goods.IDNumb :=fieldbyName("IDnumb").AsInteger;
    Goods.Name:=fieldbyName("Name").AsString;
    Goods.Developer:=fieldbyName("developer").AsString;
    Goods.MadeIn:=fieldbyName("madein").AsString;
    Goods.Cash:=fieldbyName("cash").Asfloat;
    Goods.Info:=fieldbyName("info").AsString;
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
 GoodsPointer: PGoods;
begin
 New(GoodsPointer); //Память выделять и освобождать желательно в одном блоке
 try
   FillGoods(strtoint(form1.Edit8.Text), GoodsPointer);
   ShowMessage(GoodsPointer^.Developer);
   // делаем с GoodsPointer ещё что нибудь
 finally
   Dispose(GoodsPointer);
 end;
end;


 
Плохиш ©   (2006-07-26 11:20) [9]


> Указатели   (26.07.06 05:08) [4]
>
> > Джо ©
>
>
> спасибо за внимание и столь подробное обяснение.
> Однако, если позволите:
> Как же так? Указатель такая же переменная как и целочисленная(кстати),
>  почему я не могу вернуть как значение функции эту переменную
> и присвоить это значение в другую переменную такого же типа?
>  а затем разыменовать этот указатель в готовую локальную
> переменную.

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


 
Плохиш ©   (2006-07-26 11:22) [10]


> Stakan ©   (26.07.06 11:11) [8]

Крышечки забыл ;-)


 
Stakan ©   (2006-07-26 11:29) [11]

Плохиш ©   (26.07.06 11:22) [10]
Goods^.IDNumb :=fieldbyName("IDnumb").AsInteger;
Goods.Name:=fieldbyName("Name").AsString;
Goods.Developer:=fieldbyName("developer").AsString;
Goods.MadeIn:=fieldbyName("madein").AsString;
Goods.Cash:=fieldbyName("cash").Asfloat;
Goods.Info:=fieldbyName("info").AsString;

Сдесь чтоли? И так работает нормально.


 
Джо ©   (2006-07-26 21:19) [12]

> [4] Указатели   (26.07.06 05:08)
> Как же так? Указатель такая же переменная как и целочисленная(кстати)
> , почему я не могу вернуть как значение функции эту переменную
> и присвоить это значение в другую переменную такого же типа?
> а затем разыменовать этот указатель в готовую локальную
> переменную.

Как я и написал, локальные переменные располагаются на стеке. После выхода из процедуры, стек (не строго говоря) "очищается". Поэтому указатель на ту область памяти, находившуюся на стеке уже "недействителен", там, вероятнее всего, будут находится уже другие данные либо доступ к этой области памяти вообще может быть запрещен. Отсюда и AV.
Нужно выделить память в области, называемой "куча" (heap) и возвращать указатель на нее. Собственно, GetMem этим и занимается.


 
Джо ©   (2006-07-26 21:20) [13]

Мой встречный вопрос по поводу разумности возврата указателя вместо возврата обычной записи (TGoods) по-прежнему в силе :)


 
Джо ©   (2006-07-26 21:22) [14]

> [6] stef ©   (26.07.06 05:14)
> И уж совсем обнаглев, я добавлю - чем же странная работа
> с БД?

Ну, порядок действий странный. Закрыть, потом открыть. После выхода из процедуры набор остается открытым до следующего вызова. Зачем? Почему не сделать по-человечески, т.е:
1. открыть; 2. получить данные; 3. закрыть. И все в пределах одной процедуры, желательно с использованием try/finally.


 
Kolan ©   (2006-07-26 21:25) [15]


> Крышечки забыл ;-)
>

Компилятор сам догадается... имхо


 
Юрий Зотов ©   (2006-07-26 22:32) [16]

> начинающий1   (26.07.06 03:15)

С понятием "указатель" связано 2 (а не одна) области памяти. Первая - это память, в которой размещена сама переменная типа "указатель", ее размер всегда 4 байта. Вторая - это та область памяти, не которую эта переменная указывает, ее размер может быть любым.

Ваша функция GetGoods содержит 2 локальные переменные - Goods:TGoods и Res:PGoods, причем Res содержит адрес Goods. Как и все локальные переменные, обе они размещены в стеке, существуют только во время работы функции, а после выхода из нее существовать перестают.

Таким образом, пока функция работает, переменная Res (кстати, она лишняя, можно использовать сразу Result) содержит верный адрес переменной Goods. Этот же адрес Вы возвращаете, как результат функции. Но ведь при выходе из нее переменная Goods исчезнет - и куда же после этого будет указывать адрес, который выдала функция?

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

Но если, как показано в [1] внутри функции вместо объявления переменной Goods Вы вызовете GetMem, то после выхода из функции эта область памяти автоматически не освободится - таким образом, ее адрес, который вернула функция, окажется верным и за пределами этой функции тоже.

Естественно, если где-то выделена память, то после того, как содержимое этой памяти становится ненужным, ее надо освободить. Хорошим стилем считается (и неспроста!) выделение и освобождение памяти в пределах одного и того же программного блока, причем с использованием try-finally:

выделяем_память
try
 что-то_с_ней_делаем
finally
 освобождаем_память
end;

Естественно, функция, которая выделяет область памяти сама, внутри себя, а наружу выдает ее адрес, сама же освободить эту область не имеет права, иначе снова возникнет ошибка. Поэтому использование такий функций - плохой стиль, его надо стараться избегать. Вот о чем было сказано в [8].


 
GrayFace ©   (2006-07-27 19:59) [17]

Stakan ©   (26.07.06 11:11) [8]
Тогда уж
Procedure FillGoods(IDNumb:integer; var Goods: TGoods);

procedure TForm1.Button3Click(Sender: TObject);
var
  Goods: TGoods;
begin
  FillGoods(strtoint(form1.Edit8.Text), Goods);
  ShowMessage(GoodsPointer^.Developer);
  // делаем с GoodsPointer ещё что нибудь
end;


Плохиш ©   (26.07.06 11:20) [9]
После выхода из Вашей функции Ваш указатель указывает на область памяти, которая больше не распределена для использования Вашей праграммы, поэтому при обращении к этой памяти, Вы получаете по рукам.

Не совсем. Память стека никуда не девается, она заполняется другими данными. Но в структуре есть строки - они начинают указывать на несуществующую память, при попытки их копирования (точнее увеличения их счетчика ссылок) и вылетает AV.


 
GrayFace ©   (2006-07-27 20:14) [18]

ОписАлся: ShowMessage(GoodsPointer^.Developer); -> ShowMessage(Goods.Developer);



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

Форум: "Начинающим";
Текущий архив: 2006.08.20;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.51 MB
Время: 0.038 c
2-1154507984
Dysan
2006-08-02 12:39
2006.08.20
посоветуйте альтернативный менеджер памяти!


2-1154208812
SerJaNT
2006-07-30 01:33
2006.08.20
Рамзер канвы


2-1154585931
vladimirg88
2006-08-03 10:18
2006.08.20
картинка вглубь экрана


1-1152266221
kyn66
2006-07-07 13:57
2006.08.20
Не удаляется иконка из трея


2-1154588840
Neket
2006-08-03 11:07
2006.08.20
UDP





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский