示例代码:
unit Main;
interface
uses
Winapi.Windows, System.SysUtils, Vcl.Forms;
type
TSomeRec = record
SomeData: Integer;
SomePtr: Pointer;
procedure Reset;
class operator Implicit(const SomeData: Integer): TSomeRec;
end;
TMainForm = class(TForm)
procedure FormCreate(Sender: TObject);
private
FSomeRec: TSomeRec;
end;
var
MainForm: TMainForm;
GSomeRec: TSomeRec;
implementation
{$R *.dfm}
function SomeFunc(Value: Integer): TSomeRec;
begin
OutputDebugString(PWideChar(Result.SomeData.ToString + ' : ' + Integer(Result.SomePtr).ToString));
Result.SomeData := Value;
end;
{ TSomeRec }
procedure TSomeRec.Reset;
begin
SomeData := 5;
SomePtr := nil;
end;
class operator TSomeRec.Implicit(const SomeData: Integer): TSomeRec;
begin
OutputDebugString(PWideChar(Result.SomeData.ToString + ' : ' + Integer(Result.SomePtr).ToString));
Result.SomeData := SomeData;
end;
{ TMainForm }
procedure TMainForm.FormCreate(Sender: TObject);
var
LSomeRec: TSomeRec;
begin
LSomeRec.Reset;
GSomeRec.Reset;
FSomeRec.Reset;
LSomeRec := 1;
GSomeRec := 1;
FSomeRec := 1;
LSomeRec.Reset;
GSomeRec.Reset;
FSomeRec.Reset;
LSomeRec := SomeFunc(1);
GSomeRec := SomeFunc(1);
FSomeRec := SomeFunc(1);
end;
end.
此代码提供此调试输出:
Debug Output: 5 : 0 Process DPITest.exe (1764)
Debug Output: 172555996 : 1638080 Process DPITest.exe (1764)
Debug Output: 1 : 1638080 Process DPITest.exe (1764)
Debug Output: 5 : 0 Process DPITest.exe (1764)
Debug Output: 1 : 1638080 Process DPITest.exe (1764)
Debug Output: 1 : 1638080 Process DPITest.exe (1764)
似乎不同变量的编译器会创建不同的代码:
这是正常的吗?如果是正常的,请给我链接到规范(文档),
PS
一个小小的补充......
Here写道:
对于静态数组,记录和设置结果,如果值占用一个 字节在AL中返回;如果该值占用两个字节 在AX中返回;如果值占用四个字节,则返回 EAX。否则,结果将在另一个var参数中返回 在声明的参数
之后传递给函数
但事实上,这条规则并不令人满意。如果它保持调试器输出将如下:
Debug Output: 5 : 0 Process DPITest.exe (1764)
Debug Output: 5 : 0 Process DPITest.exe (1764)
Debug Output: 5 : 0 Process DPITest.exe (1764)
Debug Output: 5 : 0 Process DPITest.exe (1764)
Debug Output: 5 : 0 Process DPITest.exe (1764)
Debug Output: 5 : 0 Process DPITest.exe (1764)
答案 0 :(得分:5)
最重要的一点是,两个函数都无法完全初始化返回值。一个function return value is not initialized,所以你不应假设它在进入时的价值。
您在观察Delphi ABI将大型返回值实现为隐藏var
参数时是正确的。所以
function SomeFunc(Value: Integer): TSomeRec;
转化为
procedure SomeFunc(Value: Integer; var Result: TSomeRec);
但是,这并不意味着您可以对Result
的初始状态做出任何假设。当你写:
Foo := SomeValue(42);
您希望将其转化为:
SomeValue(42, Foo);
如果Foo
是一个局部变量,那么确实会发生这种情况。否则,虽然使用了隐藏的临时变量。代码转换为:
var
Temp: TSomeRec;
....
SomeValue(42, Temp);
Foo := Temp;
原因是编译器无法保证非局部变量有效。访问非本地可能会导致访问冲突。因此编译器的实现者决定使用临时本地,这样如果确实发生了访问冲突,那么它将在调用站点而不是被调用者中引发。
可能还有其他原因。
一个非常相关的问题,可能是重复的,可以在这里找到:Is it necessary to assign a default value to a variant returned from a Delphi function?这个问题和这个问题之间的一个关键区别是,那里考虑的类型是托管的,所以总是默认初始化,即使对于本地变量(隐藏或其他)。
但实际情况是,这完全是实施细节的问题。您需要了解function return values are not initialized,并且每个函数都必须初始化其返回值。