火星颂
November 28th, 2004
呵呵,顺应暂时的默认风格贴一个火星颂,点击 more 可以看到 Flash 动画。
火星,是太阳系九大行星之一。
火星的外观呈火红色,亮度的变化很明显。
由于离心率比较大,火星的近日距和远日距相差了四千两百万公里。
因此火星冲日时与地球的距离有了较大的变化。
火星上是否有生命,甚至是否有像人一样的高级生命?成了人们非常感兴趣的问题。
本贴的出现,可作为研究火星生物的重要资料,具有很高的学术价值。
Read the rest of this entry »
Blog Reloaded!
November 27th, 2004
Blog 荒废好久了(汗,好像也没认真经营过),近日心血来潮决定把 Blog 好好用上。鉴于以前的 Nucleus CMS 外观普通,自定义比较复杂,决定将其换掉。
花了一晚上来试用 CCF 上某贴里大家推荐的 PHP Blog,最后感觉唯有 WordPress 功能强大使用简单并且遵循众多标准,自己用 CSS 做 Style 也很容易,所以大家就看到这个新的界面了,hoho~
该日志未加标签。关于 Delphi 参数传递方式的一点研究
July 29th, 2004
某次看 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);
// 得到字符串地址左偏移 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;
在主程序里分别以初始化后的 sGlobal 为实参调用这几个过程后,我们会看到三种方式 S 的地址都跟 sGlobal 的一样,这初步说明 var 方式和 const 方式确实都是按地址传递参数。
但不可思议的是,默认参数传递方式不会是为变量产生本地副本吗?为什么 S 的地址还会跟 sGlobal 一样呢?因为一开始我设计这个实验时并没有添加显示引用计数的代码,所以很是迷茫。之后想起了 Borland 使用了引用计数的方式和 copy-on-write 技术(对于字符串等使用这两个技术的数据类型的变量 A、B,将 A 赋值给 B 时实际上只是赋值 A 的地址给 B,并增加 A 的引用计数,直到两者其中一个被修改时才为 B 申请新的内存空间并且复制 A 的内容,同时减少 A 的引用计数)来优化字符串的操作。于是查阅 D6DG 并且观察 Delphi 的汇编代码和内存(在 CPU 窗口中)得出了本文开头关于字符串引用计数的结论。
这样,添加了显示引用计数的代码之后我们就可以观察到,使用默认方式时 S 和 sGlobal 的引用计数,都是 2,而其他方式时两者的引用计数都是 1,这更有力的说明了 const 方式确实是按地址传递,而不是像默认方式那样只是增加引用计数。这样,当我们把第一个过程中第一行代码的注释去掉后再次运行程序,可以看到默认方式时 S 的地址已经跟 sGlobal 不同了,同时两者的引用计数都是 1 了,说明确实是在 S 被修改后才产生本地副本(copy-on-write)。
那么,现在我们知道参数类型为字符串时, const 方式的参数传递实际是传递参数地址,这可以优化内存的使用,而参数为其他数据类型时是不是这样呢?
为此修改刚才的程序如下,其中 iGlobal 为全局整形变量。
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;
在主程序里分别以初始化后的 iGlobal 为实参调用这个过程后,可以看到只有 var 方式中 I 的地址跟 iGlobal 一样,而默认方式和 const 都会为 iGlobal 产生本地副本,可见确实如 D6DG 所说 const 会(也只会)优化字符串和记录类型的参数传递时的内存占用。
至此实验目的达到,还附带了解了 string 的内部格式。
附上实验程序源代码。
标签:Delphi, Programming_编程最近使用 foobar2000 的一些心得
May 14th, 2004
关于 ID3v2 tag support
如果不选择 Write ISO-8859-1 tags instead of UTF-16 和 Decode and write ISO-8859-1 tags using system codepage (non-standard),foobar2000 写入的 ID3v2 tag 将无法被包括 Winamp、Tag&Rename 在内的很多软件正常读取,原因应该是这些软件的 ID3v2 tag 实现都不够标准吧。因为这些软件对 ID3v2 的实现中的字符编码不一样,ID3v2 标准并没有规定使用什么样的字符编码方式,所以才造成了这个问题。多谢 Rainice 提醒。
(未完成……)
标签:Software_软件Let the hunt begin!
May 14th, 2004
一直以来都有写 Blog 的冲动,不过由于我的慵懒加上时间的琐碎,一直都没真正动手。今天中午在家上 WC 时突然想起可以把最近玩 foobar2000 的一点经验跟朋友分享,就很自然的想起了 Blog。碰巧下午没课,便在 Rainice 的推荐下下载了 Windix 使用的 Nucleus 的 3.0 RC 版本和 Windix 提供的 Plugins 跟 Skin/Templates。在自己的服务器上搞了半天才对这个东西有了一点点了解 -_-#
感谢 Windix 的 Plugins、Skin/Templates 和 Rainice 的推荐 ;)
准备写第一个 Blog 的时候,一下子就想起了 Warden 的这句 “Let the hunt begin!”。
该日志未加标签。