【发布时间】:2019-02-23 23:10:07
【问题描述】:
第一个代码块中注释的两个部分看似相同,但产生不同的结果(由于其他地方的问题)。我不明白它们有何不同。我唯一改变的是注释掉第一部分(for循环)或第二部分(赋值行)并得到不同的结果。
var
Amount: Integer;
I: Integer;
begin
Amount := 3;
// This produces undesired results (**the for loop**)
for I := 0 to Amount-1 do
CharDataBool[I] := CharToArray(CharDataText[I]);
// This works as expected (**the assignment lines**)
CharDataBool[0] := CharToArray(CharDataText[0]);
CharDataBool[1] := CharToArray(CharDataText[1]);
CharDataBool[2] := CharToArray(CharDataText[2]);
下面的代码有问题,在某种程度上是问题的根源,但我的问题是关于上面的代码。仅当 CharToArray 函数未分配 false 时才会出现上述问题,如下所示:
function CharToArray(Source: TCharDetails): TCharArray;
var
X, Y: Integer;
begin
SetLength(Result, Source.Width, 10);
for Y := 0 to 9 do
for X := 0 to Source.Width-1 do
if Source.S[Y*Source.Width+X] = 'x' then
Result[X,Y] := true
else Result[X,Y] := false; // Adding this solves the problem
end;
没有进入“留下未知的值真的很糟糕”,我只是想了解为什么问题在第一部分代码中出现(for循环)而不是(分配行)?三个赋值行与for循环有何不同?
下面可以复制到默认 VCL 项目中的 Unit1 上
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
type
TCharArray = array of array of boolean;
TCharDetails = record
S: String;
Width: Integer;
end;
var
CharDataText: array of TCharDetails;
CharDataBool: array of TCharArray;
var
Form1: TForm1;
procedure Init;
implementation
{$R *.dfm}
function CharToArray(Source: TCharDetails): TCharArray;
var
X, Y: Integer;
begin
SetLength(Result, Source.Width, 10);
for Y := 0 to 9 do
for X := 0 to Source.Width-1 do
if Source.S[Y*Source.Width+X] = 'x' then
Result[X,Y] := true;
end;
procedure Init;
var
Amount: Integer;
I: Integer;
X,Y: Integer;
S1, S2: String;
begin
Amount := 2;
SetLength(CharDataText, Amount);
SetLength(CharDataBool, Amount);
ChardataText[0].Width := 10;
ChardataText[0].S :=
// 1234567890
' ' + // 0
' x ' + // 1
' xx ' + // 2
' x ' + // 3
' x ' + // 4
' x ' + // 5
' x ' + // 6
' x ' + // 7
' x ' + // 8
' xxx ';
ChardataText[1].Width := 10;
ChardataText[1].S :=
// 1234567890
'x ' + // 0
' xxxx ' + // 1
' x x ' + // 2
' x ' + // 3
' x ' + // 4
' x ' + // 5
' x ' + // 6
' x ' + // 7
' x ' + // 8
' xxxxxx x';
for I := 0 to Amount-1 do
CharDataBool[I] := CharToArray(CharDataText[I]);
S1 := '';
for Y := 0 to 9 do
for X := 0 to 9 do
S1 := S1 + CharDataBool[1,X,Y].ToString;
CharDataBool[0] := CharToArray(CharDataText[0]);
CharDataBool[1] := CharToArray(CharDataText[1]);
S2 := '';
for Y := 0 to 9 do
for X := 0 to 9 do
S2 := S2 + CharDataBool[1,X,Y].ToString;
// S1 != S2 ??
ShowMessage(S1 + #13 + S2);
end;
initialization
Init;
end.
【问题讨论】:
-
你可以这样做:
Result[X, Y] := Source.S[Y * Source + X] = 'x';。这向您展示了为什么这或多或少等同于第一部分代码中的分配。 -
当结果未定义时,它是未定义的。这导致函数的结果未定义。我的意思是当结果应该为假时,结果可能是理想的或不理想的,这取决于地址当前持有的内容,它不是确定性的。例如,只需更改调用顺序,您可能会得到不同的 udefined 结果..
-
@Sertac:结果被 SetLength 清零,所以不是未定义的。循环前 Result 的所有值都是 false。
-
我猜在循环中,一个临时的 Result 变量被重用了。如果您将引用类型分配给某事物,并重新使用它,则内容可能会发生变化。动态数组没有写时复制,因此循环的最后一次迭代会覆盖先前迭代的结果,并且 CharDataBool 的每个元素都引用 same dynarray。这可能也是您必须将 items 设置为 false 的原因:SetLenght 还重用数组。这是不正确的,我无法调试它(没有 [MVCE]),但我想它就像我刚才描述的那样。请同时说明 Delphi 版本。
-
在函数中首先调用
SetLength(Result,0,0);。见Do I need to setLength a dynamic array on initialization?动态数组在循环中复用,必须在函数内部清除。