WriteLn()如何真正起作用?

时间:2009-03-06 04:04:38

标签: delphi arguments

自恐龙时代以来,Turbo Pascal和现在的Delphi都有一个Write()和WriteLn()程序,可以悄悄地做一些简洁的事情。

  • 参数的数量是可变的;

  • 每个变量都可以是各种类型的;你可以提供整数,双数,字符串,布尔值,并按任意顺序混合它们;

  • 您可以为每个参数提供其他参数:

  

写( '你好':10, '世界!':7); //对齐参数

  • 它甚至在代码完成drowdown中以特殊方式显示:
    • 写([var F:文件]; P1; [...,PN])
    • WriteLn([var F:File]; [P1; [...,PN]])

现在我输入了这个,我注意到Write和WriteLn在代码完成下拉列表中没有相同的括号。因此看起来这不是自动生成的,但它是由某人硬编码的。

无论如何,我能够自己编写这样的程序,还是所有这些神奇的硬编码编译技巧?

9 个答案:

答案 0 :(得分:25)

Writeln就是我们所说的编译器“魔术”功能。如果你查看System.pas,你将找不到一个像你期望的那样声明的Writel。编译器从字面上将其全部分解为对各种特殊运行时库函数的单独调用。

简而言之,没有办法在不修改编译器的情况下实现与内置writeln完全相同的自己的版本。

答案 1 :(得分:4)

正如艾伦所说,你不能编写自己的功能,完成所有相同的事情。

但是,您可以编写一个文本文件驱动程序来执行自定义操作,并使用标准Write(ln)写入文本文件驱动程序。我们在旧的DOS时代就这样做了:)

(前一语句上下文中的“Driver”只是一段Pascal代码,它通过在系统单元IIRC中切换指针而挂钩到系统中。自从我上次使用这个技巧以来已经很久了。)< / p>

答案 2 :(得分:2)

据我所知,pascal标准不包含变量参数。

话虽如此,IIRC,GNU Pascal让你说的是:   Procecdure Foo(a:整数; b:整数; ......);

尝试在编译器的语言文档中搜索“Variable Argument Lists”或“conformant arrays”。以下是后者的示例:http://www.gnu-pascal.de/demos/conformantdemo.pas

正如上一张海报所说,writeln()是神奇的。我认为问题与在pascal函数中如何组装堆栈有关,但是自从我考虑堆栈中的内容以来,这已经很长时间了。)

但是,除非您正在编写“writeln”函数(已经编写过),否则您可能不需要 来实现带有变量参数的过程。尝试迭代或递归:)

答案 3 :(得分:2)

这是神奇的编译器行为而不是常规过程。不,没有办法编写这样的子程序(不幸的是!)。代码生成解析实际参数及其类型的计数,并在编译时转换为适当的RTL调用(例如 Str())。这反对经常建议的数组const (实际上是单变量数组形式参数),这导致在运行时执行相同操作。我发现后来接近笨拙,它在某种程度上损害了代码的可读性,并且Bugland(Borland / Inprise / Codegear / Embarcadero /将其命名)破坏了Code Insight的变体开放数组构造函数(是的,我很在意,我使用OutputDebugString(PChar(格式) ('...',[...]))))和代码完成在那里(或根本没有)正常工作。 因此,模拟魔术行为的最接近的可能方式是声明大量重载子程序(实际上很多,在特定位置每个特定的形式参数类型)。人们可以称之为kludge,但这是获得变量参数列表灵活性的唯一方法,可以隐藏在单独的模块中。

PS:我故意省略格式说明符,因为语法不允许分号使用 Str() Write() Writeln( )正在接受它们。

答案 4 :(得分:1)

是的,您可以在Delphi和朋友(例如免费的pascal,Kylix等)中进行,但不能在更“标准”的帕斯卡中进行。查找变量开放数组参数,这些参数使用如下语法:

procedure MyProc(args : array of const);

(已经过了几年,我没有手册,所以在继续之前检查细节)。这为您提供了一个可以从中提取RTTI的TVarData(或类似内容)的开放数组。

但需要注意的一点是:我认为你不能匹配x:y格式化语法(这是特殊的),并且可能需要使用稍微冗长的包装器。

答案 5 :(得分:1)

大多数已经说过了,但我想补充一些内容。

首先,您可以使用格式化功能。将几乎任何类型的变量转换为字符串并控制其大小非常棒。虽然它有它的缺点:

myvar := 1;
while myvar<10000 do begin
  Memo.Lines.Add(Format('(%3d)', [myVar]));
  myvar := myvar * 10;
end;

产地:

(  1)
( 10)
(100)
(1000)

所以尺寸是最小尺寸(就像:x:y构造一样)。

要获得最少量的变量参数,您可以使用默认参数和重载函数:

procedure WriteSome(const A1: string; const A2: string = ''; const A3: string = '');

procedure WriteSome(const A1: string); overload;
procedure WriteSome(const A1: Integer); overload;

答案 6 :(得分:1)

你不能在旧的Pascal中编写自己的写/写。它们是由编译器,格式化,对齐等生成的。这就是为什么一些程序员喜欢C语言,甚至是灵活的标准函数,例如printf,scanf,可以由任何有能力的程序员实现。

如果您倾向于创建比C供应商实现的更高性能的东西,您甚至可以为C创建相同的printf函数。它们中没有神奇的诡计,你的代码只需要“遍历”变量参数。

P.S。

但正如MarkusQ指出的那样,Pascal(Free Pascal,Kylix等)的一些变体可以促进变量参数。我对帕斯卡尔进行了修补,自DOS日起,Turbo Pascal 7。

答案 7 :(得分:1)

Writeln不是基于“数组的const”,而是由编译器分解为各种调用,将参数转换为字符串,然后调用原始的字符串。 “LN”只是将lineending写为字符串的函数。 (OS依赖)。基元的过程变量(函数指针)是文件类型(Textrec / filerec)的一部分,这就是它们可以自定义的原因。 (例如TP中的AssignCrt)

如果启用{$ I +}模式,则在每个元素之后,调用iocheck函数。

上面提到的GPC结构是无限的C开放阵列。 FPC(以及afaik Delphi也支持),但语法不同。

过程somehting(a:数组const); cdecl;

将转换为与C,printf样式兼容的ABI。这意味着相关函数(在这种情况下是某些函数)无法获取参数的数量,但必须依赖于formatstring解析。所以这与const数组不同,这是安全的。

答案 8 :(得分:1)

虽然不是你问题的直接答案,但我想补充以下评论: 我最近使用Writeln(...)语法将一些代码重写为使用StringList,使用Format(...)填充'lines',只使用简单的IntToStr(...),FloatToStr(...)函数和等。

这种变化的主要原因是速度提升。使用StringList和SaveFileTo比WriteLn,Write组合快得多。

如果您正在编写一个创建大量文本文件的程序(我正在开发一个网站创建程序),这会产生很大的不同。