我对Pascal很新,目前正在使用指针。 我有2条记录,其中一条包含指向另一条记录类型的2条指针。
type
WaypointRef = ^Waypoint;
PathRef = ^Path;
Waypoint = record
id: integer;
Name: string;
pathRefs: array of PathRef;
end;
Path = record
distance: integer;
WaypointRefA, WaypointRefB: WaypointRef;
end;
所有航路点都保存在一个数组中。 现在,当我试图读出路径的值时,我得到了神秘的结果:
writeln(waypoints[0].pathRefs[0]^.distance);
writeln(waypoints[1].pathRefs[0]^.distance);
两者都应该打印相同的值,但它们不会。 然而,更神秘的是,即使我尝试以下内容:
writeln(waypoints[0].pathRefs[0]^.distance);
writeln(waypoints[0].pathRefs[0]^.distance);
writeln(waypoints[0].pathRefs[0]^.distance);
我得到2个不同的值。 (正确的 - 先是173 - 然后是2次。)
waypoints[0].pathRefs[0]^
总是指向同一个地址,因此我很困惑。我希望有人知道这个问题。
编辑:2似乎是默认值,因为如果我没有将任何值保存到"距离"它也会返回2。在路径创建。
EDIT2:这里是航点和路径创建的代码。我认为一定有失败。我现在可能会因设备内部的程序而混淆设计。我只是在尝试。
procedure buildWaypoint(Name: string);
procedure addWaypoint(w: Waypoint);
var
lngth: integer;
begin
lngth := Length(waypoints);
SetLength(waypoints, lngth + 1);
waypoints[lngth] := w;
end;
var
w: Waypoint;
begin
w.id := id;
id := id + 1;
w.Name := Name;
addWaypoint(w);
end;
procedure buildPath(waypointRefA, waypointRefB: WaypointRef; distance: integer);
procedure addPath(pRef: PathRef);
procedure addPathToWaypoint(pRef: PathRef; wRef: WaypointRef);
var
lngth: integer;
begin
lngth := length(wRef^.pathRefs);
SetLength(wRef^.pathRefs, lngth + 1);
wRef^.pathRefs[lngth] := pRef;
end;
begin
addPathToWaypoint(pRef, pRef^.WaypointRefA);
addPathToWaypoint(pRef, pRef^.WaypointRefB);
end;
var
p: path;
begin
p.distance := distance;
p.WaypointRefA := waypointRefA;
p.WaypointRefB := waypointRefB;
addPath(@p);
end;
答案 0 :(得分:6)
有两件事可能导致这种意外行为:
waypoints[0]
和pathRefs[0]
的数组类型属性:那么这些方法可能会产生可能导致问题的副作用。 (显然,这不是的情况)。您要添加的路径在堆栈中声明:
var
p: path; //<-- Stack variable
begin
...
addPath(@p);
end; //<-- When you leave the method the stack variable is no longer valid.
wRef^.pathRefs[??]
指向调用堆栈中较高的地址。 您需要确保指向堆上的内存。您可以使用动态内存分配例程执行此操作:New
,Dispose
,GetMem
,FreeMem
。
修改强>
Documentation关于动态内存分配例程。
如何更改代码的示例:
procedure addPathToWaypoint(pRef: PathRef; wRef: WaypointRef);
var
lngth: integer;
LpRefOnHeap: PathRef;
begin
lngth := length(wRef^.pathRefs);
SetLength(wRef^.pathRefs, lngth + 1);
New(LpRefOnHeap); //Allocate heap memory
LpRefOnHeap^ := pRef^; //Copy data pointed to by pRef to heap
wRef^.pathRefs[lngth] := LpRefOnHeap; //Hold reference to an address that won't
//become invalid when stack unwinds.
end;
注意:您必须弄清楚处理动态分配内存的位置和时间。
EDIT2 添加一个简单的控制台应用来演示此问题。
program InvalidUseOfStackVar;
{$APPTYPE CONSOLE}
type
PData = ^TData;
TData = record
Value: Integer;
end;
var
GData: PData;
procedure SetData;
var
LData: TData; //Stack variable will no longer be valid when routine exits.
begin
LData.Value := 42; //The initial value pointed to by GData
GData := @LData; //The global var will continue to point to invalid memory after exit.
end;
procedure ChangeStack;
var
//This is written to have the same stack layout as the previous routine.
LData: TData;
begin
LData.Value := 777; //This unintentionally changes data pointed to by the global var
end;
begin
SetData; //Sets GData, but GData points to an address on the call stack
Writeln(GData^.Value); //Writes 42 because that's what was on the stack at the time of the method call.
ChangeStack; //Updates the stack variable to a different value
Writeln(GData^.Value); //Now writes 777 because GData points to the same location in memory, but the
//data at that location was changed.
Writeln(GData^.Value); //Note: calling the Writeln method above also changes the stack.
//The only difference is that it is less predictable for us to determine
//how the stack will be changed.
Readln;
end.