Tim*_*ram 5 stack-overflow delphi
我有一个调用几个函数的过程:
procedure TForm1.Button1Click(Sender: TObject);
var
rawData: TRawData;
rawInts: TRawInts;
processedData: TProcessedData;
begin
rawData := getRawData();
rawInts := getRawInts(rawData);
processedData := getProcessedData(rawInts);
end;
Run Code Online (Sandbox Code Playgroud)
数据类型定义如下:
TRawData = array[0..131069] of Byte;
TRawInts = array[0..65534] of LongInt;
TProcessedData = array[0..65534] of Double;
Run Code Online (Sandbox Code Playgroud)
只运行程序:
rawData := getRawData();
rawInts := getRawInts(rawData);
Run Code Online (Sandbox Code Playgroud)
工作完全没问题.但是,当我尝试运行时:
getProcessedData(rawInts)
Run Code Online (Sandbox Code Playgroud)
我收到stackoverflow错误.我不明白为什么会这样.功能代码getProcessedData非常简单:
function getProcessedData(rawInts : TRawInts) : TProcessedData;
var
i: Integer;
tempData: TProcessedData;
scaleFactor: Double;
begin
scaleFactor := 0.01;
for i := 0 to 65534 do
tempData[i] := rawInts[i] * scaleFactor;
Result := tempData;
end;
Run Code Online (Sandbox Code Playgroud)
为什么会导致错误?
Rob*_*edy 13
线程的默认最大堆栈大小为1 MB.三个局部变量Button1Click共131,070 + 65,535*4 + 65,535*8 = 917,490字节.调用时getProcessedData,通过value传递参数,这意味着该函数在堆栈上生成参数的本地副本.这增加了SizeOf(TRawInts)= 262,140字节,使堆栈至少达到1,179,630字节,或大约1.1 MB.你的堆栈溢出了.
您可以TRawInts通过引用传递数组来减少堆栈使用.然后该函数将不会自己复制.Zdravko的回答建议使用var,但由于函数不需要修改传入的数组,所以应该使用const.
function getProcessedData(const rawInts: TRawInts): TProcessedData;
Run Code Online (Sandbox Code Playgroud)
天真地,我们可能期望tempData和Result变量getProcessedData占用额外的堆栈空间,但实际上,它们可能不会.首先,大型返回类型通常会导致编译器更改函数签名,因此它更像是使用var参数而不是返回值声明函数:
procedure getProcessedData(rawInts: TRawInts; var Result: TProcessedData);
Run Code Online (Sandbox Code Playgroud)
然后相应地转换呼叫:
getProcessedData(rawInts, processedData);
Run Code Online (Sandbox Code Playgroud)
因此,Result不会占用更多的堆栈空间,因为它实际上只是调用者帧中变量的别名.
此外,有时编译器会识别函数末尾的赋值,例如Result := tempData,意味着它tempData不需要任何自己的空间.相反,编译器可能会将您的函数看作是您一直直接编写的Result:
begin
scaleFactor := 0.01;
for i := 0 to 65534 do
Result[i] := rawInts[i] * scaleFactor;
end;
Run Code Online (Sandbox Code Playgroud)
但是,最好不要指望编译器进行那些节省内存的更改.相反,最好不要首先在堆栈上如此重视.为此,您可以使用动态数组.这些将把大量内存从堆栈中移出并进入堆,这是用于动态分配的内存的一部分.首先更改数组类型的定义:
type
TRawData = array of Byte;
TRawInts = array of Integer;
TProcessedData = array of Double;
Run Code Online (Sandbox Code Playgroud)
然后,在返回这些类型的函数中,使用SetLength指定每个数组的长度.例如,我们已经看到的功能可能是这样的:
function getProcessedData(const rawInts: TRawInts): TProcessedData;
var
i: Integer;
scaleFactor: Double;
begin
Assert(Length(rawInts) = 65535);
SetLength(Result, Length(rawInts));
scaleFactor := 0.01;
for i := 0 to High(rawInts) do
Result[i] := rawInts[i] * scaleFactor;
end;
Run Code Online (Sandbox Code Playgroud)