【问题标题】:Why does Format reject procedure address arguments starting with XE4为什么格式拒绝以 XE4 开头的过程地址参数
【发布时间】:2014-09-12 20:01:30
【问题描述】:

考虑这个程序:

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

procedure Foo;
begin
end;

type
  TProcedure = procedure;

const
  FooConst: TProcedure = Foo;

var
  FooVar: TProcedure = Foo;
  P: Pointer;

{$TYPEDADDRESS ON}

begin
  P := @Foo;
  Writeln(Format('%p', [P]));
  Writeln(Format('%p', [@FooConst]));
  Writeln(Format('%p', [@FooVar]));
  Writeln(Format('%p', [@Foo]));
  Readln;
end.

此程序在 XE3 上编译和运行,并产生以下输出:

00419FB8 00419FB8 00419FB8 00419FB8

在 XE4 及更高版本上,程序无法编译,这两行都显示错误消息:

Writeln(Format('%p', [@FooConst]));
Writeln(Format('%p', [@FooVar]));
[dcc32 错误] E2250 没有可以调用的“格式”的重载版本 带着这些论点

在 XE4、XE5 和 XE6 上,当 $TYPEDADDRESS 关闭时程序编译。在 XE7 上,无论$TYPEDADDRESS 的设置如何,程序都无法编译。

这是编译器错误吗?还是我使用了不正确的语法来获取过程的地址?

【问题讨论】:

  • Addr(FooConst)Addr(FooVar)在 XE6 中工作。 System.Addr 不受 $T 指令的影响。现在手头没有 XE7。
  • @LURD Addr(...) 在 XE7 中也可以正确编译和运行。这很奇怪,并且指向编译器错误。谢谢。
  • FWIW, Pointer(@FooConst) 在 XE6 和 XE7 中同样有效。
  • @LURD 是的,我也观察到了

标签: delphi


【解决方案1】:

我认为这是一个编译器错误并已提交 QC 报告:QC#127814

作为一种变通方法,您可以使用以下任一方法:

  1. 使用addr() 而不是@ 运算符。
  2. @FooVar@FooConst 转换为Pointer,例如Pointer(@FooVar)

【讨论】:

  • FWIW 我真的不喜欢在有其他答案时回答然后接受我自己的答案,但我担心我不同意@aleroot 的分析,我真的相信我上面写的是准确的。
  • 应该注意,这两种变通方法都隐藏了拥有{$TYPEDADDRESS ON} 的目的,但这是在修复错误之前最好的方法。
  • @LURD @ 在作用于过程和过程变量时很奇怪,因为 oit 旨在产生无类型指针
  • 来自The @ Operator, If X is a variable, @X returns the address of X. (Special rules apply when X is a procedural variable; see "Procedural Types in Statements and Expressions" in Data Types, Variables, and Constants.) The type of @X is Pointer if the default {$T} compiler directive is in effect. In the {$T+} state, @X is of type ^T, where T is the type of X (this distinction is important for assignment compatibility, see Assignment-compatibility).
  • @LURD 是的,这里引用的其他文档很重要:docwiki.embarcadero.com/RADStudio/XE7/en/Procedural_Types
【解决方案2】:

我认为新编译器 XE7 的行为更符合规范,在这种情况下需要显示错误,因为 {$TYPEDADDRESS ON} 强制 @ 运算符返回类型化指针和 Format 函数取而代之的是一个无类型的通用指针作为输入。

由于{$TYPEDADDRESS ON} 的目的是鼓励谨慎使用指针,在编译时捕获不安全的指针分配,如果函数需要一个通用的无类型指针是正确的(在这种情况下是有意义的,因为函数的目的是打印它的地址——所以不需要有类型指针来检索它的地址),编译器会在一个类型指针被传递的情况下捕获一个错误,行为与规格。

我认为在这种情况下(根据文档)正确的方法是:

  Writeln(Format('%p', [Addr(FooConst)]));
  Writeln(Format('%p', [Addr(FooVar)]));

因为Addr 函数总是返回一个无类型指针,这正是Format%p 所期望和需要的。

我假设在以前的版本中,在这种情况下,编译器用于执行自动转换:Pointer(@FooConst),但由于{$TYPEDADDRESS ON} 指令,它没有太大意义。

【讨论】:

  • 但在你的情况下,它不是一个函数,而是一种引用函数的新类型......
  • 没错。但是在这种情况下,我为什么可以将@D 作为参数传递给Format,其中D 是IntegerDoublestringrecord 等类型的类型常量或变量?你的论点也应该适用于此。
  • 您似乎也忽略了在 XE7 中,程序会为 TYPEDADDRESS 设置产生编译器错误。我建议您再次阅读问题的那一部分。我知道这有点棘手,因为我在一个问题中讨论了许多不同的编译器行为。
  • 如果错误在 TYPEDADDRESS 关闭的情况下出现,我们应该在 QC Embarcadero 中打开一个事件,因为这次它不可能是一个错误......
  • 它应该使用或不使用 TYPEDADDRESS,就像它对任何其他类型一样。
猜你喜欢
  • 2017-10-03
  • 2014-05-17
  • 2012-07-13
  • 2017-11-28
  • 2014-12-30
  • 2012-05-06
  • 1970-01-01
  • 2012-03-20
相关资源
最近更新 更多