Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何将nil传递给var参数?_Delphi_Delphi 7 - Fatal编程技术网

Delphi 如何将nil传递给var参数?

Delphi 如何将nil传递给var参数?,delphi,delphi-7,Delphi,Delphi 7,有相当多的API例程将指向某个变量的指针作为已转换为var参数的参数,但可以根据Windows帮助文件指定为nil指针 例如,ChangeDisplaySettings函数声明为: function ChangeDisplaySettings(var lpDevMode: TDeviceMode; dwFlags: DWORD): Longint; stdcall; 但Windows帮助文件明确指出,“在动态模式更改后,为lpDevMode参数传递NULL是返回默认模式的最简单方法。”正确的翻

有相当多的API例程将指向某个变量的指针作为已转换为var参数的参数,但可以根据Windows帮助文件指定为nil指针

例如,ChangeDisplaySettings函数声明为:

function ChangeDisplaySettings(var lpDevMode: TDeviceMode; dwFlags: DWORD): Longint; stdcall;
但Windows帮助文件明确指出,“在动态模式更改后,为lpDevMode参数传递NULL是返回默认模式的最简单方法。”正确的翻译应该是:

function ChangeDisplaySettings(lpDevMode: PDeviceMode; dwFlags: DWORD): Longint; stdcall;

我发布这个问题和答案是为了帮助新手解决这些问题,而不必重新声明函数。我仍然记得这在一开始对我来说是个问题。

一个解决方案是使用指针代替var参数重新声明任何此类函数,但有一个更简单的解决方案。只需将取消引用的nil指针强制转换为正确的类型,例如,对于ChangeDisplaySettings示例,使用以下命令将显示模式重置为默认注册表设置:

ChangeDisplaySettings(TDeviceMode(nil^), 0);

通过这种方式,您可以传递恰好位于内存地址零处的var参数——编译器很高兴,您可以传递一个指向API例程的nil指针


编辑:根据David Hefferman的评论,标准做法似乎是重新声明这些例程。就我个人而言,如果可以的话,我更喜欢在我自己的单位中使用标准声明,但是出于专业工作的目的,我建议开发人员遵循标准实践。

除了其他有帮助的答案和评论之外,我还有另一个想法。在本例中,无论谁从头部翻译了此API,都没有仔细查看API文档。如果他们这样做了,很明显,通过“零”是一件有效的事情

在这种情况下,正确的做法是声明一组重载,它们都引用相同的导入。一个是很好的“var”参数版本,另一个是“指向结构的指针”版本。这将使您可以直接传入TDeviceMode变量(无需获取变量的地址),并在必要时仍传入nil。编译器将“nil”与指向结构的指针“匹配”,然后引用该结构。由于这两个API解析为同一个API,并且传递参数的实际方式没有差异,因此一切仍按预期工作

因为当您应该能够传递一个“nil”时,该API没有重载版本,这是一个API转换错误。请随时在上的报告中指出这一点


为了记录在案,这些年来我在产品中做了很多API翻译。。。可以肯定的是,I是一个愚蠢的开发人员,他没有对此进行适当的研究:)。

重新声明函数是正确的solution@David谢谢作为德尔福经验丰富的老手,我尊重你的意见。不过,就我个人而言,我不喜欢到处都有标准例程的重新声明,我更喜欢在任何可能的地方使用标准例程。然而,我将更新我的答案,以表明标准实践似乎更喜欢重新声明。应该有两个重载。一个带有var,另一个带有指向struct的指针。然后调用方可以选择。我个人觉得第二个选项更自然。@Andreas,我也更喜欢使用第二个选项,但在没有声明指针类型的情况下列出了第一个选项。另一方面,David Hefferman对重新声明的偏好是在新版本的Delphi修复标准声明时不会导致错误的唯一解决方案@Jannie-这个解决方案的另一种破坏方式是,如果编译器被更改,以正确地识别这种类型转换的诡计是破坏参数约定,并拒绝无效的代码。本例中提供的声明仅作为副作用是正确的。不要管“标准实践”,正确的解决方案是使用正确的声明,而不是设法添加一个副作用来抵消另一个副作用。@Deltics,我同意正确的解决方案是正确的声明,但我严重怀疑编译器是否会像您描述的那样更改。将内存地址类型转换为指向某个类型的指针,然后取消对它的引用,对于var参数来说是非常有效的。在本例中,我们只使用nil(地址0)。当然,nil在Delphi中有特殊的含义,但我仍然怀疑他们是否会为了阻止程序员以非标准方式使用该语言而开始禁止某些值。我想补充的是,这种方法不适用于“out”参数,它们可能是在函数开始执行时隐式初始化的。好的,从Borland Pascal的时代起就一直在练习,但实际上,我更喜欢更接近原文的翻译,即始终使用指针。这样做的好处是,当在Delphi中使用“var”时,API的文档(几乎都是基于C的)不会引起任何混淆(最小惊讶原则)。文档显示一个指针,翻译有一个指针。没有混乱,不需要如此仔细地阅读文档。重载只对已经存在的转换是必需的。此外,对于尚未标记为重载的函数,不可能声明重载,因此您必须修改原始导入单元(这并不总是可能的),或者隐藏原始导入单元,并在您自己的单元中声明两个重载函数,其中一个是准确的重新声明。这就是为什么我建议在质量门户中报告它。我从来没有建议过,如果原稿没有被标记为重载,就可以完成。我实际上是想说服你和Embarcadero放弃“尽可能使用var而不是指针类型”
ChangeDisplaySettings(PDeviceMode(nil)^, 0);