STRING 赋值后DELPHI编译器会给它尾部多预留多少内存(50)

  • 主题发起人 主题发起人 我爱PASCAL
  • 开始时间 开始时间

我爱PASCAL

Unregistered / Unconfirmed
GUEST, unregistred user!
有否编译器的编译指示符一般预留多大比如A: string;A := 'abcdefghijk';//实际可能很长,比如有几十MSetLength(a,length(a)+N);//这样会不会在别处内存空间新开辟一个length(a)+N的空间如果有百分之几的优化预留的话,编译器会不会不从新申请一个新的内存.这样诃以节约大量重复的复制时间.
 
setlength 好像每次都是申请新(reallocates )的内存,再把旧的值复制过去。For a long-string or dynamic-array variable, SetLength reallocates the string or array referenced by S to the given length.Existing characters in the string or elements in the array are preserved, but the content of newly allocated space is undefined.
 
VCL内部大量使用string,如果每个都预留,太浪费内存。
 
那如果设得比原来的短呢,应该不会再申请新的空间了吧.另外,如果直接访问超过STRING长度的字节会造成什么后果?
 
直接反来看一下不就清楚了?就算不反,看string的指针不就行了?
 
不懂汇编不会反,不过第二个办法可以试试,但是目前用不着了,在当前应用中我直接取到超出部分去了,就不用SETLENGTH了,因为不用那超出的实际内容,只是为了简化代码,因为不那样的话会在每一循环中判断是否超出,影响运行效率.
 
那如果设得比原来的短呢,应该不会再申请新的空间了吧.===应该也会重新申请的。我瞎猜。
 
用一句简单的程序调试var x:string;begin x:='xxxxxxxxxxxxxxxxxxxxxxxxxxxx';//设定中断调试器会得到这样的结果//Unit2.pas.28: x:='xxxxxxxxxxxxxxxxxxxxxxxxxxxx';0045206F 8D45FC lea eax,[ebp-$04]00452072 BAB4204500 mov edx,$004520b4 //指向字符串的内存指针00452077 E86426FBFF call @LStrLAsg@LStrLAsg的源代码在system单元里面可以找到我搜索了一下这个函数的资料搜索到一篇02年的文章14 楼xzgyb(老达摩)回复于 2002-11-15 17:37:12 得分 25具体看一下 procedure TMainForm.Button1Click(Sender: TObject); var s:string; begin s:='111'; listbox1.Items.Add (inttostr(integer(@s))); listbox1.Items.Add (inttostr(integer(pchar(s)))); listbox1.Items.Add (inttostr(integer(@s[1]))); listbox1.Items.Add (inttostr(integer(pchar(s)))); end; 的汇编码 这里s为局部变量 s := '111'的汇编码 lea eax, [ebp-$04] mov edx, $00464d00 //$00454d00即为'111'在数据段中的地址 call @LStrLAsg 再看看LStrAsg TEST EDX,EDX JE @@sourceDone { bump up the ref count of the source } MOV ECX,[EDX-skew].StrRec.refCnt //这里ECX为$FFFFFFFF INC ECX //所以这时ECX为0,跳到@@SourceDone,没有做下步引用计数加1的运算 JLE @@sourceDone { literal assignment -> jump taken } LOCK INC [EDX-skew].StrRec.refCnt @@sourceDone: { we need to release whatever the dest is pointing to } XCHG EDX,[EAX] { fetch str } //这步EAX存的就是'111'在数据段的地址,而 EDX为0 TEST EDX,EDX { if nil, nothing to do } JE @@done //返回 MOV ECX,[EDX-skew].StrRec.refCnt { fetch refCnt } DEC ECX { if < 0: literal str } JL @@done LOCK DEC [EDX-skew].StrRec.refCnt { threadsafe dec refCount } JNE @@done LEA EAX,[EDX-skew].StrRec.refCnt { if refCnt now zero, deallocate} CALL _FreeMem @@done: 此时s中存的是'111'在数据段的地址 而调用@s[1] lea eax, [ebp-$04] call @UniqueStringA UniqueStringA判断引用计数减1如果不等于零的话就调用NewAnsiString重新 分配一段内存,这时的引用计数就为1 同样的 s = '111' s如果为局部变量,s返回的是'111'在数据段的地址,它的引用计数为-1 s如果为全局变量,这句会调用_LStrAsg,_LStrAsg又调用NewAnsiString,所以 对全局变量s,的引用计数为1 s := ''; s := s + '111' 对于 s := ''; 汇编码如下 lea eax, [ebp-$04] call @LStrClr 而eax内容为0,@LStrClr什么也不做 s := s + '111'的汇编码 lea eax, [ebp-$04] mov edx, $00464d08 call @LStrCat 而LStrCat判断eax内容如为0就调用LStrAsg,这样在调用NewAnsiString 则产生一个引用计数为1的串 就是这样
 
var S: string;begin S := '0123456789'; ShowMessage(IntToStr(Integer(@s))); S := '0123456789123456789'; ShowMessage(IntToStr(Integer(@s))); SetLength(s, Length(s)+1000000); ShowMessage(IntToStr(Integer(@s)));得到的三个地址是一样的.不清楚是怎么回事另外楼上的汇编看不懂,像天书.
 
大内存块分配才会将导致内存重新分配,才会发生reallocmem, move...大内存定义是>1M如果是小内存块则不定,因为vcl的内存管理器给一般分配的内存时,可能是将空闲块分配,也可能重新分配,如果是空闲块分配,则重分配(realloc),则检查空闲是否适合新内存块,小于则截断后面内容,大于则重分配。 这个机制没法预知,你想控制,则自己写自己处理。
 
以上,看看getmem.inc所得,不过记得不清楚,给你参考。
 
因为那只是一个指针,不是内容vars:string;begin S := '0123456789';//结果: writeln(IntToStr(Integer(@s))); //4252192 writeln(IntToStr(Integer(@s[1])));//14795792 writeln(IntToStr(Integer(@s[2])));//14795793 S := '0123456789123456789'; writeln(IntToStr(Integer(@s)));//4252192 writeln(IntToStr(Integer(@s[1])));//14854233 writeln(IntToStr(Integer(@s[2])));//14854234 SetLength(s, Length(s)+1000000); writeln(IntToStr(Integer(@s)));//4252192 writeln(IntToStr(Integer(@s[1])));//214604880 writeln(IntToStr(Integer(@s[2])));//214604881 readln;后面的都变了
 
QQ在线所说我觉得说到最关键之处了,不过我经常会用到大内存,所以这点就要注意.
 
后退
顶部