Форум: "Начинающим";
Текущий архив: 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