<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rainux's Journal&#187; Delphi Archives  &laquo; Rainux&#039;s Journal</title>
	<atom:link href="http://rainux.org/category/programming/delphi/feed/" rel="self" type="application/rss+xml" />
	<link>http://rainux.org</link>
	<description>Rubyist of Vimmer</description>
	<lastBuildDate>Tue, 10 Aug 2010 23:41:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>收到 Borland China 寄来的 Delphi 2005 试用版光盘</title>
		<link>http://rainux.org/borland-china-delphi-2005</link>
		<comments>http://rainux.org/borland-china-delphi-2005#comments</comments>
		<pubDate>Mon, 27 Dec 2004 08:12:03 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Programming_编程]]></category>

		<guid isPermaLink="false">http://rainux.sefans.com/blog/?p=37</guid>
		<description><![CDATA[虽然早用上了 Delphi 2005 Update 1 正式版，这张光盘已经没有实际意义，但还是挺兴奋的，毕竟是 Borland 寄给我的东西，呵呵。 平心而论 Delphi 2005 是个不错的产品，虽然我们可以找出一堆数落她的理由。比如启动太慢，占用内存太多，IDE 太像 Visual Studio 而失去了 Delphi 传统的风格，编辑器反应比较慢等等。不过在同一个 IDE 里同时支持 Delphi for Win32、Delphi for .NET 以及 C# 确实是个很诱人的卖点。以及新增的 Refactoring、UML Modeling 等等都是很实用很重要的功能。 Delphi 2005 是个不错的产品，但 Borland 对她的宣传确实有些过了。 标签：Delphi, Programming_编程 相关日志 关于 Delphi 参数传递方式的一点研究 (3) SCColoredID，星际争霸彩色 ID 修改器 v0.2.0，支持 Windows Vista (11) 发现一个 ACDSee 7.x 可能是因为硬编码造成的问题 (2) [...]]]></description>
			<content:encoded><![CDATA[<p>虽然早用上了 Delphi 2005 Update 1 正式版，这张光盘已经没有实际意义，但还是挺兴奋的，毕竟是 Borland 寄给我的东西，呵呵。</p>

<p>平心而论 Delphi 2005 是个不错的产品，虽然我们可以找出一堆数落她的理由。比如启动太慢，占用内存太多，IDE 太像 Visual Studio 而失去了 Delphi 传统的风格，编辑器反应比较慢等等。不过在同一个 IDE 里同时支持 Delphi for Win32、Delphi for .NET 以及 C# 确实是个很诱人的卖点。以及新增的 Refactoring、UML Modeling 等等都是很实用很重要的功能。</p>

<p>Delphi 2005 是个不错的产品，但 Borland 对她的宣传确实有些过了。</p>

	标签：<a href="http://rainux.org/tag/delphi/" title="Delphi" rel="tag">Delphi</a>, <a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a><br />

	<h4>相关日志</h4>
	<ul class="st-related-posts">
	<li><a href="http://rainux.org/delphi" title="关于 Delphi 参数传递方式的一点研究 (July 29, 2004)">关于 Delphi 参数传递方式的一点研究</a> (3)</li>
	<li><a href="http://rainux.org/sccoloredid-starcraft-colorful-id-trainer/" title="SCColoredID，星际争霸彩色 ID 修改器 v0.2.0，支持 Windows Vista (October 12, 2005)">SCColoredID，星际争霸彩色 ID 修改器 v0.2.0，支持 Windows Vista</a> (11)</li>
	<li><a href="http://rainux.org/acdsee-7x" title="发现一个 ACDSee 7.x 可能是因为硬编码造成的问题 (January 17, 2005)">发现一个 ACDSee 7.x 可能是因为硬编码造成的问题</a> (2)</li>
	<li><a href="http://rainux.org/%e4%bd%bf%e7%94%a8-linux-shell-io-%e9%87%8d%e5%ae%9a%e5%90%91%e7%ae%80%e5%8c%96%e5%b0%8f%e7%a8%8b%e5%ba%8f%e7%9a%84%e6%97%a5%e5%bf%97%e8%ae%b0%e5%bd%95" title="使用 Linux shell I/O 重定向简化小程序的日志记录 (March 12, 2007)">使用 Linux shell I/O 重定向简化小程序的日志记录</a> (0)</li>
	<li><a href="http://rainux.org/exerb%e2%80%94%e2%80%94%e5%b0%86-ruby-%e8%84%9a%e6%9c%ac%e8%bd%ac%e6%8d%a2%e6%88%90-windows-%e4%b8%8b%e7%9a%84%e5%8f%af%e6%89%a7%e8%a1%8cexe%e6%96%87%e4%bb%b6%ef%bc%81" title="Exerb——将 Ruby 脚本转换成 Windows 下的可执行(.exe)文件！ (October 4, 2007)">Exerb——将 Ruby 脚本转换成 Windows 下的可执行(.exe)文件！</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://rainux.org/borland-china-delphi-2005/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于 Delphi 参数传递方式的一点研究</title>
		<link>http://rainux.org/delphi</link>
		<comments>http://rainux.org/delphi#comments</comments>
		<pubDate>Thu, 29 Jul 2004 07:42:30 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Programming_编程]]></category>

		<guid isPermaLink="false">http://rainux.sefans.com/blog/?p=7</guid>
		<description><![CDATA[某次看 D6DG 说默认的参数传递方式因为会为变量产生本地副本所以会消耗额外的内存，而 const 方式会优化字符串和记录类型的参数传递时的内存占用。从而猜测 const 方式的参数传递实际也是按地址传递，只是编译器强制不允许函数内的代码修改 const 方式传递进去的变量而已。 为了证实我的猜想，特设计以下实验，本实验中使用到了字符串(本文提到的字符串都是指 Delphi 默认的字符串 AnsiString)内部结构中的引用计数。 关于字符串的引用计数，结合 D6DG 中的说明和偶的实际研究，得出的结论是，字符串地址实际上是其内容的第一个字符的地址，在此地址之前的 12 字节内存中的内容才是字符串内部结构中的头部，分别是 32 位字符串占用的内容空间大小，32 位引用计数，32 位字符串长度(仅在 Delphi 7 中验证，推测 Delphi 7 以前的 32 位 Delphi 中的字符串结构都应该是这样，未验证)。 以下代码中 sGlobal 为全局字符串变量。 procedure Method1(S: string); var P: PInteger; begin // S := S + 'k'; // 字符串实际上是指针，这里只是将其强制转换为 PInteger 备用 P := PInteger(S); [...]]]></description>
			<content:encoded><![CDATA[<p>某次看 D6DG 说默认的参数传递方式因为会为变量产生本地副本所以会消耗额外的内存，而 const 方式会优化字符串和记录类型的参数传递时的内存占用。从而猜测 const 方式的参数传递实际也是按地址传递，只是编译器强制不允许函数内的代码修改 const 方式传递进去的变量而已。</p>

<p>为了证实我的猜想，特设计以下实验，本实验中使用到了字符串(本文提到的字符串都是指 Delphi 默认的字符串 AnsiString)内部结构中的引用计数。</p>

<p>关于字符串的引用计数，结合 D6DG 中的说明和偶的实际研究，得出的结论是，字符串地址实际上是其内容的第一个字符的地址，在此地址之前的 12 字节内存中的内容才是字符串内部结构中的头部，分别是 32 位字符串占用的内容空间大小，32 位引用计数，32 位字符串长度(仅在 Delphi 7 中验证，推测 Delphi 7 以前的 32 位 Delphi 中的字符串结构都应该是这样，未验证)。</p>

<p>以下代码中 sGlobal 为全局字符串变量。</p>

<pre name="code" class="delphi">procedure Method1(S: string);
var
  P: PInteger;
begin
  // S := S + 'k';
  // 字符串实际上是指针，这里只是将其强制转换为 PInteger 备用
  P := PInteger(S);
  // 得到字符串地址左偏移 8 字节的地址，也就是字符串的 32 位引用计数存储的位置
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('S 引用计数: ' + IntToStr(P^));
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('S 地址: ' + IntToStr(Integer(Pointer(S))));
  Form1.Memo1.Lines.Add('');
end;

procedure Method2(var S: string);
var
  P: PInteger;
begin
  P := PInteger(S);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('S 引用计数: ' + IntToStr(P^));
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('S 地址: ' + IntToStr(Integer(Pointer(S))));
  Form1.Memo1.Lines.Add('');
end;

procedure Method3(const S: string);
var
  P: PInteger;
begin
  P := PInteger(S);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('S 引用计数: ' + IntToStr(P^));
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('S 地址: ' + IntToStr(Integer(Pointer(S))));
  Form1.Memo1.Lines.Add('');
end;

procedure sGlobalInfo;
var
  P: PInteger;
begin
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('sGlobal 地址: ' + IntToStr(Integer(sGlobal)));
  Form1.Memo1.Lines.Add('');
end;
</pre>

<p>在主程序里分别以初始化后的 sGlobal 为实参调用这几个过程后，我们会看到三种方式 S 的地址都跟 sGlobal 的一样，这初步说明 var 方式和 const 方式确实都是按地址传递参数。</p>

<p>但不可思议的是，默认参数传递方式不会是为变量产生本地副本吗？为什么 S 的地址还会跟 sGlobal 一样呢？因为一开始我设计这个实验时并没有添加显示引用计数的代码，所以很是迷茫。之后想起了 Borland 使用了引用计数的方式和 copy-on-write 技术(对于字符串等使用这两个技术的数据类型的变量 A、B，将 A 赋值给 B 时实际上只是赋值 A 的地址给 B，并增加 A 的引用计数，直到两者其中一个被修改时才为 B 申请新的内存空间并且复制 A 的内容，同时减少 A 的引用计数)来优化字符串的操作。于是查阅 D6DG 并且观察 Delphi 的汇编代码和内存(在 CPU 窗口中)得出了本文开头关于字符串引用计数的结论。</p>

<p>这样，添加了显示引用计数的代码之后我们就可以观察到，使用默认方式时 S 和 sGlobal 的引用计数，都是 2，而其他方式时两者的引用计数都是 1，这更有力的说明了 const 方式确实是按地址传递，而不是像默认方式那样只是增加引用计数。这样，当我们把第一个过程中第一行代码的注释去掉后再次运行程序，可以看到默认方式时 S 的地址已经跟 sGlobal 不同了，同时两者的引用计数都是 1 了，说明确实是在 S 被修改后才产生本地副本(copy-on-write)。</p>

<p>那么，现在我们知道参数类型为字符串时， const 方式的参数传递实际是传递参数地址，这可以优化内存的使用，而参数为其他数据类型时是不是这样呢？</p>

<p>为此修改刚才的程序如下，其中 iGlobal 为全局整形变量。</p>

<pre name="code" class="delphi">procedure Method1(I: Integer);
begin
  Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
  Form1.Memo1.Lines.Add('');
end;

procedure Method2(var I: Integer);
begin
  Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
  Form1.Memo1.Lines.Add('');
end;

procedure Method3(const I: Integer);
begin
  Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
  Form1.Memo1.Lines.Add('');
end;

procedure iGlobalInfo;
begin
  Form1.Memo1.Lines.Add('iGlobal 地址: ' + IntToStr(Integer(@iGlobal)));
  Form1.Memo1.Lines.Add('');
end;
</pre>

<p>在主程序里分别以初始化后的 iGlobal 为实参调用这个过程后，可以看到只有 var 方式中 I 的地址跟 iGlobal 一样，而默认方式和 const 都会为 iGlobal 产生本地副本，可见确实如 D6DG 所说 const 会(也只会)优化字符串和记录类型的参数传递时的内存占用。</p>

<p>至此实验目的达到，还附带了解了 string 的内部格式。</p>

<p>附上<a href="/stuff/20040729.Delphi.01.rar" rel="nofollow" >实验程序源代码</a>。</p>

	标签：<a href="http://rainux.org/tag/delphi/" title="Delphi" rel="tag">Delphi</a>, <a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a><br />

	<h4>相关日志</h4>
	<ul class="st-related-posts">
	<li><a href="http://rainux.org/borland-china-delphi-2005" title="收到 Borland China 寄来的 Delphi 2005 试用版光盘 (December 27, 2004)">收到 Borland China 寄来的 Delphi 2005 试用版光盘</a> (0)</li>
	<li><a href="http://rainux.org/sccoloredid-starcraft-colorful-id-trainer/" title="SCColoredID，星际争霸彩色 ID 修改器 v0.2.0，支持 Windows Vista (October 12, 2005)">SCColoredID，星际争霸彩色 ID 修改器 v0.2.0，支持 Windows Vista</a> (11)</li>
	<li><a href="http://rainux.org/acdsee-7x" title="发现一个 ACDSee 7.x 可能是因为硬编码造成的问题 (January 17, 2005)">发现一个 ACDSee 7.x 可能是因为硬编码造成的问题</a> (2)</li>
	<li><a href="http://rainux.org/%e4%bd%bf%e7%94%a8-linux-shell-io-%e9%87%8d%e5%ae%9a%e5%90%91%e7%ae%80%e5%8c%96%e5%b0%8f%e7%a8%8b%e5%ba%8f%e7%9a%84%e6%97%a5%e5%bf%97%e8%ae%b0%e5%bd%95" title="使用 Linux shell I/O 重定向简化小程序的日志记录 (March 12, 2007)">使用 Linux shell I/O 重定向简化小程序的日志记录</a> (0)</li>
	<li><a href="http://rainux.org/exerb%e2%80%94%e2%80%94%e5%b0%86-ruby-%e8%84%9a%e6%9c%ac%e8%bd%ac%e6%8d%a2%e6%88%90-windows-%e4%b8%8b%e7%9a%84%e5%8f%af%e6%89%a7%e8%a1%8cexe%e6%96%87%e4%bb%b6%ef%bc%81" title="Exerb——将 Ruby 脚本转换成 Windows 下的可执行(.exe)文件！ (October 4, 2007)">Exerb——将 Ruby 脚本转换成 Windows 下的可执行(.exe)文件！</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://rainux.org/delphi/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
