Delphi 中,使用 const 修饰的字符串函数参数的问题

wxieyang 2010-07-29 07:58:24
先看看一个例子:
var  
SS: String;

procedure test1(const s: String);
begin
try
SS := '你好';
ShowMessage(s);
except
end;
end;

procedure TfrmMain.btnTestClick(Sender: TObject);
begin
SS := 'Hello';
test1(SS);
end;


猜猜 ShowMessage(s) 会显示什么?

答案是:乱码!
意外吧。

简单分析如下:
使用 const 修饰的字符串参数,不会导致字符串引用此时的变化,当 执行到

SS := '你好'

时,SS原来指向的字符串由于引用次数递减至零而释放,但是,这时参数 s 依然指向这个已经释放的内存空间,
因此,在随后的

Showmessage(s)

时,会显示乱码。

从程序分析上看,加入了 const 修饰的字符串参数,比不加 const 参数修饰的字符串参数,少了调用增加字符串引用计数以及在函数结束的时候调用减少字符串引用计数两个步骤
因此,在函数不是处在核心的位置被频繁调用的情况下,还是不要使用 const 修饰符的好。毕竟相对于性能的一点提升,还是稳定运行比较重要。

详细的讨论,可以看我的博客

Delphi 中 函数参数中的 const 修饰符的本质以及注意事项
...全文
467 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
liangpei2008 2010-08-23
  • 打赏
  • 举报
回复
很不错的细节问题,鼓励一下LZ
wxieyang 2010-08-23
  • 打赏
  • 举报
回复
楼上你好,我不是说如何解决这个问题,实际上这样的情况应该比较少,我只是说会存在这样的问题而已,以后大家在使用的时候注意别翻这样的错误。
其实,只要调用使用 const 修饰的字符串阐述的函数时,调用前,字符串的引用次数是1的,如果在被调用函数没有结束之前改变了当做参数的那个变量的内容(这种情况应该在多线程应用中可能会碰到),就可能会出现问题。
oloveuxyz 2010-08-05
  • 打赏
  • 举报
回复
procedure test1(const s: string);
begin
try
SS := '你好';
ShowMessage(Utf8ToAnsi(s));
except
on E: Exception do
begin
ShowMessage(E.Message);
end;
end;
end;

Utf8ToAnsi一下就OK 了~
wxieyang 2010-08-05
  • 打赏
  • 举报
回复
沉得好快啊,呵呵
楼上的兄弟,我讨论的不是被 const 修饰符声明的参数的可写问题,而是 const 修饰符会存在什么意外的情况。让使用 delphi 的人注意这种情况,避免造成莫名的错误
iamduo 2010-07-29
  • 打赏
  • 举报
回复
强~!
自己把自己覆盖掉了。
MinxSoft 2010-07-29
  • 打赏
  • 举报
回复
我觉得,楼主只是在演示如何滥用指针而已
SQLDebug_Fan 2010-07-29
  • 打赏
  • 举报
回复
const会少一次内存申请和复制的操作,你的SS很短,所以你感觉不出来,你把SS加载10M大小的字符串,就可以明显感觉到效率的差别了。
SQLDebug_Fan 2010-07-29
  • 打赏
  • 举报
回复
你的代码风格也存在问题,用了全局变量还把它当成参数,另外尽量少用全部变量。
SQLDebug_Fan 2010-07-29
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 mwy654321 的回复:]

恩。这个细节很重要。你要是不贴出来,我还真没有注意到这个问题。
[/Quote]
无条件为你 2010-07-29
  • 打赏
  • 举报
回复
恩。这个细节很重要。你要是不贴出来,我还真没有注意到这个问题。
外声 2010-07-29
  • 打赏
  • 举报
回复
支持小白说的,不是不要用const而是应该尽量的使用const修饰符
麦客来了 2010-07-29
  • 打赏
  • 举报
回复
普通的应用差别不明显,如果进行图像处理和特定的算法,用不用const差别特别大,特别是多次调用的方法。
Harryfin 2010-07-29
  • 打赏
  • 举报
回复
其实我一直想知道用const和不用到底能差多远
麦客来了 2010-07-29
  • 打赏
  • 举报
回复
将一个字符串传递到一个方法中,如果不打算对字符串进行调整,可以将字符串声明为 const。在大多数情况下,使用const声明编译器将不再增加一个不可见的try finally块。如果没有使用const声明,编译器会假定方法内部可能会修改这个字符串,因此会建立一个局部的不可见的串变量来保持这个字符串。开始时将引用次数递增,而在结束时再予以递减,为了确保后者发生,编译器就会增加不可见的try...finally模块。

如下的代码:计算串中元音的个数

function countVowels(const S: string): Integer;
var
I: Integer;
begin
Result := 0;
for I := 1 to Length(S) do
if upcase(S[I]) in ['A', 'E', 'I', 'O', 'U'] then
Inc(Result);
end;


如果从函数语句中将关键字const移除,程序的运行速度将减慢,特别是字符串长度比较大的情况下,减慢特别明显,这是不可见的Try...finally模块的开销。


楼主的那段代码,实际上是针对const的不正确用法,没有代表性,推出结论:还是不使用const修饰符好,这是不正确的,相反,应该尽量的使用const修饰符。

SS是个全局常量,btnTestClick中调用作为参数调用test1方法,申明了const,就不应该在test1内部去修改SS的值。这与const声明的设计初衷是违背的,使用上是不当的,根据不当的例子推出的结论没有说服力。

QQ286251099 2010-07-29
  • 打赏
  • 举报
回复
基本的问题吧...
const 参数本来编译器是会检测你的写操作的 如果是 const i:integer 它就直接不让你操作
const p:pointer p 还是不可操作 p^ 可以操作

procedure Funp(const pr:PRect);
begin
pr.Left := 0;//Pass
//pr := nil;//Errrrrrr Left side cannot be assigned to
end;
procedure Funs(const s:string);
begin
s := '000';//Errrrrrr Left side cannot be assigned to
end;

procedure Fun2(const i:Integer);
begin
I := 0;//Errrrrr Left side cannot be assigned to
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Funs(Caption);
end;

wxieyang 2010-07-29
  • 打赏
  • 举报
回复
另外,回复8楼的,并不是不使用 const 就一定会出现内存复制,实际上,如果你在函数中没有出现导致内存复制的语句,delphi 仅仅是增加引用计数。
会引起内存复制的操作包括:
更改了原始字符串的内容(也就是传入的那个字符串内容),或者出现了按照数组方式访问字符串的操作,例如

ps := @s[1];

所以说,如果不使用const修饰,并且函数中也没有针对字符串参数而可能导致内存复制的语句,那么,不使用const修饰符,会比使用const修饰符多出两个函数调用(@LStrAddRef 和 @LStrClr)以及一个异常处理的代码(相当于加入了try finally end)。

wxieyang 2010-07-29
  • 打赏
  • 举报
回复
其实,我使用全局变量仅仅是测试这种错误。
不仅仅是全局变量,类中成成员变量一样涉及到这个问题。
如果类中某个方法采用 const 修饰符声明字符串参数,并且将类中的某个成员变量当参数传入这个函数,而在函数中,又更改了这个成员变量的值,一样会出问题。

我给的例子,仅仅是为了说明问题,并不是在说明什么编码标准的问题。

实际编程中,一个程序,不可能从头到尾都是由一个人来做,当涉及到多个人对代码进行维护的时候,谁也不能保证不会出现这种问题,而一旦出现,还不太好找错误。

而且我也说了,在不是核心函数或者频繁调用的函数时,慎用 const 修饰符,不是不能用,而是要知道怎么回事再去用。用了就要避免上面那种情况发生。仅此而已。

我要讨论的是使用 const 会带来什么副作用,让大家清楚会有什么问题,从而尽量避免。
至于编码规则以及代码书写规范,这些东西你可以约束自己去遵守,但是你不能保证修改你代码的人也遵守。
呵呵

828

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 非技术区
社区管理员
  • 非技术区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧