Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Делфи _ Работа с динамическим массивом

Автор: Tan 1.11.2011 17:52

Доброго времени суток ув. форумчане! Обращаюсь к вам со своей проблемой, для начала опишу текущую картину.

Имеется следующий класс :

type
point = class
id : integer;
X : real;
Y : real;
Z : real;
function Point (x : real; y : real; id_get : integer) : integer;
end;

Описание функции этого класса :
function point.Point (x : real; y : real; id_get : integer) : integer;
var id : Integer;
begin
id := - 1;
X := x;
Y := y;
if ( id_get <> -1 ) then id_get := id;
end;

Типы данных :
    init_points : array of integer;
xPos, yPos, zPos : integer;
tab : array of point;

Так же есть функция которая заполняет переменные xPos, yPos, zPos значениями и вызывает функцию добавления точки.
procedure TForm1.GiveValues();
var i : integer;
begin
for i:= Low(init_points) to High(init_points)
do
if ( ( i mod 3 ) = 0 ) then
begin
xPos := init_points[i];
yPos := init_points[i + 1];
zPos := init_points[i + 2] - 150;
AddPoint();
end;
end;


И вот сама ключевая функция AddPoint() :
procedure TForm1.AddPoint();
var deletion : boolean;
dx, dy, dist : real;
i, y : integer;
p : point;
new_point : point;
begin
deletion := FALSE;
if ( ( zPos > 0 ) or ( xPos < 0 ) ) then Exit;
if ( Length(tab) > 0 ) then
begin
for i := Low(tab) to High(tab) do
begin
p := tab[i];
dx := p.X - xPos;
dy := p.Y - yPos;
dist := sqrt (dx * dx + dy * dy);
if (dist < 4) then
begin
deletion := TRUE;
for y := i to High(tab) - 1 do tab[y] := tab[y + 1]; //смещаем на элемент вниз
SetLength(tab, High(tab) - 1); //уменьшаем размер так как убрали 1 элемент
end;
end;
end;
if ( deletion = FALSE ) then
begin
new_point.Point(xPos, yPos, -1);
new_point.Z := zPos + 150; // !!!! Вот тут программа вылетает !!!!
SetLength(tab, High(tab) + 1);
tab[Length(tab)] := new_point; // добавляем в конец новый элемент
end;
end;

Как я отметил в коде, программа вылетает на строке new_point.Z := zPos + 150; с ошибкой EAccessViolation. Мне кажется это связано с неверной работой с динамическими массивами, но более детально я не могу понять суть этой проблемы. Буду рад помощи, заранее спасибо.

Автор: IUnknown 1.11.2011 18:35

Для начала: Point у тебя - класс, и его перед использованием надо что? Правильно, создать. У тебя в коде создания экземпляра класса нет. Как-то вот так:

    if ( deletion = FALSE ) then
begin
new_point := Point.Create; // Да, да. Point.Point - обычная функция, а нужен конструктор

new_point.Point(xPos, yPos, -1);
new_point.Z := zPos + 150; // !!!! Вот тут программа вылетает !!!!
SetLength(tab, High(tab) + 1);
tab[Length(tab)] := new_point; // добавляем в конец новый элемент
end;
Ну, или сделать Point.Point конструктором (не понимаю я этой С++-ной привычки называть конструктор точно так же, как и сам класс. Я больше привык к Create-у) и вызывать соответствующим образом (как принято в Дельфи, а не так, как в Турбо Паскале)

Автор: Tan 1.11.2011 20:07

Спасибо за ответ Владимир, идея понятна, есть несколько вытекающих вопросов :
1 - Destroy надо делать каждый раз после того как я уже использовал это значение?
2 - Нет идей почему теперь может писать invalid pointer operation ссылаясь на end в конце функции GiveValues?

Автор: IUnknown 1.11.2011 20:25

Цитата
Destroy надо делать каждый раз после того как я уже использовал это значение?
Destroy надо делать тогда, когда значение больше не нужно. Чтоб не было утечек. То есть, при уменьшении размера Tab. Кстати, замени там цикл
Цитата
for i := Low(tab) to High(tab) do
на While - негоже внутри цикла For уменьшать динамический массив, это тоже приведет к проблемам, при использовании For значение High(tab) на каждой итерации не пересчитывается, конечное значение вычисляется при входе в цикл, а внутри цикла оно может измениться - будет ошибка...

Цитата
Нет идей почему теперь может писать invalid pointer operation ссылаясь на end в конце функции GiveValues?
Выход за пределы массива - как минимум. Ты ж идешь до High(init_points), так откуда у тебя будет init_points[i + 2]? Вот тебе и вылет программы.

Автор: Tan 1.11.2011 22:14

Я переписал указанные места (destroy пока не делал, потому что основная задача сначала скомпилить то, что есть). Теперь компилятор указывает ту же ошибку только в строке dx := p.X - xPos; Вероятно где-то что-то опять я проворонил.

procedure TForm1.AddPoint();
var deletion : boolean;
dx, dy, dist : real;
i, y : integer;
p : point;
new_point : point;
begin
deletion := FALSE;
if ( ( zPos > 0 ) or ( xPos < 0 ) ) then Exit;
if ( Length(tab) > 0 ) then
begin
i := 1;
while ( i <> Length(tab) ) do
begin
ShowMessage(inttostr(Length(tab)));
p := Point.Create; //добавил и тут конструктор
p := tab[i];
dx := p.X - xPos;
dy := p.Y - yPos;
dist := sqrt (dx * dx + dy * dy);
if (dist < 4) then
begin
deletion := TRUE;
for y := i to High(tab) - 1 do tab[y] := tab[y + 1];
SetLength(tab, High(tab) - 1);
end;
i := i + 1;
end;
end;
if ( deletion = FALSE ) then
begin
new_point := Point.Create;
new_point.Point(xPos, yPos, -1);
new_point.Z := zPos + 150;
SetLength(tab, Length(tab) + 1);
tab[Length(tab)] := new_point;
end;
end;

procedure TForm1.GiveValues();
var i : integer;
begin
i := 0;
while ( i < Length(init_points) )
do
begin
xPos := init_points[i];
yPos := init_points[i + 1];
zPos := init_points[i + 2] - 150;
AddPoint();
i := i + 3;
end;
end;

Автор: IUnknown 1.11.2011 23:34

Цитата
Я переписал указанные места
Не вижу:

Цитата
  i := 0;
while ( i < Length(init_points) ) // <--- Ну, будет i = Length(init_points) - 1
do
begin
xPos := init_points[i];
yPos := init_points[i + 1];
zPos := init_points[i + 2] - 150; // Тут - попытка обратиться к "запредельному элементу"
AddPoint();
i := i + 3;
end;


Как у тебя изначально инициализируется массив init_points? Какого он размера на момент входа в GiveValues?

Автор: Tan 2.11.2011 0:06

Cпасибо за ответ, Владимир. Массив считывает из файла значения, которых 256. Делаю я это так:

procedure TForm1.DataFromFile ();    //pick ups data from file
var num, size, i : Integer;
begin
AssignFile(Points_file, 'Points.txt');
Reset(Points_file);
size := 256;
i := 0;
SetLength(init_points, size);
SetLength(tab, 0);
while not Eof(Points_file) do
begin
Read(Points_file, num);
init_points[i] := num;
i := i + 1;
end;
CloseFile(Points_file);
end;


Автор: IUnknown 2.11.2011 0:20

Стоп, стоп... Сразу не посмотрел вот сюда:

Цитата
     i := 1;
while ( i <> Length(tab) ) do
begin
Это ни разу не аналог
for i := Low(tab) to High(tab) do
. Массивы индексируются с 0, а не с 1. Надо делать:

     i := 0;
while ( i < Length(tab) ) do
begin


Еще одно (причина - та же, индексация с 0) :
Цитата
             SetLength(tab, Length(tab) + 1); 
tab[Length(tab)] := new_point;
Это некорректный код. Вылетит запросто. Ибо элемента tab[Length(tab)] в динамическом массиве быть не может. Элементы, которые существуют - от tab[0] до tab[Length(tab) - 1], или от tab[low(tab)] до tab[high(tab)]. Но никак не до Length ...

Пересмотри еще раз всю программу, с учетом того, что ты теперь знаешь о начале отсчета с 0, а не с 1...

Автор: Tan 2.11.2011 4:44

Спасибо Владимир, я смог скомпилировать код - основная моя проблема была в том, что я по массиву не с 0 шёл.
У меня остался последний вопрос по моему коду - в моём первом посте описана функция Point. В этой функции каждрому экземпляру класса point должно быть присвоено значение X и Y, которое передаётся через параметр этой функции через x и y. Но когда в моём случае создаётся экзсемпляр new_point, и после вызова функции new_point.Point(параметры) значения new_point.X и new_point.Y равны 0 (по крайней мере я так заметил) - я сделал вывод что каким-то образом эти значения не передаются вне функции. Если я не ошибаюсь, то в паскале для таких случае перед параметром функции требовалось написать var, мне очень интересно что для этого надо сделать на Delphi.

Автор: IUnknown 2.11.2011 4:54

Так что чему в функции присваивается? Одинаковые имена полей класса и параметров - зло. Хотя бы перед именем поля поставь Self, чтобы было понятнее, что происходит...

Цитата
Но когда в моём случае создаётся экзсемпляр new_point, и после вызова функции new_point.Point(параметры) значения new_point.X и new_point.Y равны 0 (по крайней мере я так заметил) - я сделал вывод что каким-то образом эти значения не передаются вне функции.
Бррр... Теперь я не понял. А что должно вернуться? Где инициализация полей класса? Если добавишь Var (да, в Дельфи точно так же, как и в Паскале) - что должно вернуться из функции? Где вообще возврат результата? Функция не возвращает ничего. Компилятор должен выбрасывать предупреждение...


Автор: TarasBer 2.11.2011 13:22

> не понимаю я этой С++-ной привычки называть конструктор точно так же, как и сам класс

Она, кстати, не всегда выполняется.
Вообще это неудобно.

> Кстати, замени там цикл на while

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

Автор: Tan 2.11.2011 16:47

Я хочу чтобы внутри функции Point формировались значения для new_point.X и new_point.Y. Для этого я делаю там присваивание X := x, Y := y, но значения не присваиваются и равны 0 (внутри функции они верные), вне функции new_point.X и new_point.Y - 0.

Автор: IUnknown 2.11.2011 18:38

Я ж написал:

Цитата
Хотя бы перед именем поля поставь Self

Т.е., вот так:
function point.Point (x : real; y : real; Var id_get : integer) : integer;
begin
{Self.}id := - 1;
self.X := x;
self.Y := y;
if ( id_get <> -1 ) then id_get := {Self.}id;

Point := { чему равен результат функции??? }
end;
(Паскаль, как и все языки его группы, не делает разницы между X и x, это для него одна и та же переменная. И то, что ты делаешь - это присвоение параметру самого себя. Смысла - никакого, поле класса вообще не затрагивается).

Если перед id_get не поставишь Var, то изменение ее значения внутри Point.Point наружу не выйдет. Только смысла в этом параметре вообще ноль: "если id_get не (-1), то присвоить его значению id, которое в самом начале принудительно ставится в (-1). Если же оно и так (-1) - то ничего не делать" Вдумайся, что тут написано. Это именно то, что ты запрограммировал.

Цитата
Не надо заменять его на while, потому что там останется тот же дурацкий алгоритм за квадрат.
Там останется не "тот же дурацкий", а корректно работающий (в отличие от For-а, я написал почему) алгоритм за квадрат...

Автор: TarasBer 2.11.2011 19:07

> а корректно работающий (в отличие от For-а, я написал почему) алгоритм за квадрат...

Хорошая отмаза.

Автор: Tan 2.11.2011 19:14

Спасибо огромное за помощь, ввиду некоторых жизненных обстоятельств мне пришлось вернуться к Delphi - из-за того, что давно там не работал - оттуда и все вытекающие ошибки, на которые вы указали.