delphi – 使用来自数组的值时,CompareMem似乎不起作用
所以我的程序中有一个测试用例
procedure MemCheck<T>(x,y : T); var a,b : T; vals : array of T; c : T; begin a := x; b := y; c := a; SetLength(vals,4); vals[0] := a; vals[1] := a; vals[2] := a; vals[3] := a; c := T(vals[2]); //c := a ; workes fine Check ( CompareMem(@c,@y,SizeOf(T)),'Memory compare check' ); end; 这个测试用例失败了,我不知道为什么 MemCheck< String>(‘a’,’a’); 我用的时候工作正常 c:= a;而不是c:= T(vals [2]); 解决方法
您正在将字符串文字传递给MemCheck().由于它们是相同的值,因此编译器将它们合并在可执行文件中.所以你实际上是将一个常量字符串文字传递给x和y参数.字符串文字是只读的,引用计数为-1.将字符串文字分配给String变量将指针按原样复制到内存块,而不分配新内存或增加内存块的引用计数.
在填充vals数组之前,a,b和c变量只是字符串文字指针的副本.它们都指向内存中的相同数据块. System.StringRefCount()函数确认它们都返回引用计数-1,证明它们都指向字符串文字. 在语句vals [2]:= a;中,a指向字符串文字,因此RTL分配新的String实例并将字符串文字的内容复制到新的内存块中. a仍然具有-1的引用计数,但是vals [2]现在具有引用计数1,证明已执行分配.陈述c:= T(vals [2]);然后将分配的String分配给String变量,以便分配的数据的引用计数递增. StringRefCount()确认c现在的引用计数为2. 因此,CompareMem()因此失败,因为您正在比较指向两个不同内存块的两个String变量的内部数据指针的值,因此它们的指针值不同并且比较失败. 当你改变语句c:= T(vals [2]);到c:= a;,a仍然指向内存中的字符串文字,这样指针就会被原样复制到c而不执行任何分配.因此,CompareMem()成功,因为现在您正在比较指向同一内存块的两个String变量的内部数据指针的值,因此比较成功. 所以真正的问题是 – 为什么语句vals [2]:= a;执行新的分配而不是像任何其他String一样复制数据指针:= String;分配? 赋值a:= x;,b:= y;和c:= a;调用System.@ UStrLAsg()函数,它允许指向字符串文字的字符串按原样分配给另一个字符串而不执行分配. 声明vals [2]:= a;正在调用System.@ UStrAsg()函数,当源String指向字符串文字时,它总是生成一个新的分配副本. 为什么编译器选择在vals [2]中使用@UStrAsg()而不是@UStrLAsg():= a;声明?这就是当赋值的左侧是动态数组内的String而不是独立变量时编译器的工作方式.如果vals是一个静态数组(vals:array [0..3]的String;),编译器将选择使用@UStrLAsg()而不是@UStrAsg(),然后CompareMem()将成功. @UStrLAsg()通常在将String分配给本地String时使用,因此通常可以安全地使用字符串文字. @GetrAsg()通常在将String分配给全局String时使用,其中需要复制文字以避免在文本可能存在于可在分配后卸载的DLL / Package中时出现潜在的内存错误. 因此,在分配给动态数组时,编译器必须小心谨慎,因为它不知道或不能假设数组中String实例的生命周期. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |