Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
string:=const:为什么本地和结果的实现不同?_String_Delphi_Delphi Xe2_Refcounting - Fatal编程技术网

string:=const:为什么本地和结果的实现不同?

string:=const:为什么本地和结果的实现不同?,string,delphi,delphi-xe2,refcounting,String,Delphi,Delphi Xe2,Refcounting,在Delphi函数中,结果经常作为var参数实现(尽管有QC票据,但不是out参数) 字符串常量基本上是带有负refcounter的变量,这将抑制自动内存[de]分配 它确实抑制了它:下面的代码不会泄漏 type TDealRecord = record id_Type: Integer; Price: extended; Remark: String; end; const const_loop = 100000000; function TestVar: T

在Delphi函数中,结果经常作为var参数实现(尽管有QC票据,但不是out参数)

字符串常量基本上是带有负refcounter的变量,这将抑制自动内存[de]分配

它确实抑制了它:下面的代码不会泄漏

type
  TDealRecord = record
    id_Type: Integer;
    Price: extended;
    Remark: String;
  end;
const const_loop = 100000000;

function TestVar: TDealRecord;
//procedure TestVar;
var
  Li: Integer;
  LRec: TDealRecord;
begin
  for Li := 1 to const_loop do begin
     FillChar(Lrec,SizeOf(LRec), 0);
     LRec.Remark := 'Test';

//     FillChar(Result,SizeOf(Result), 0);
//     Result.Remark := 'Test';
  end;
end;
但是改变被操纵的变量,它立刻开始严重泄漏

function TestVar: TDealRecord;
//procedure TestVar;
var
  Li: Integer;
  LRec: TDealRecord;
begin
  for Li := 1 to const_loop do begin
//     FillChar(Lrec,SizeOf(LRec), 0);
//     LRec.Remark := 'Test';

     FillChar(Result,SizeOf(Result), 0);
     Result.Remark := 'Test';
  end;
end;
事实证明,
string:=const
是通过不同的调用实现的,具体取决于左值:

  • 结果:AnsiString->LStrAsg
  • 结果:破坏:->UStrAsg
  • 本地变量:UnicodeString:->ustrasg
  • 本地变量:AnsiString:->LStrLAsg
  • 虽然后两个是按预期克隆指针,但前两个是将字符串复制到新实例,就像我向它们添加
    UniqueString
    调用一样


    为什么会有这种差异?

    在与David Heffernan讨论之后,我开始认为Delphi编译器不知道它为变量指定的值是什么。一种有位置的“类型擦除”。它无法区分全局常量、局部堆栈变量和局部字符串表达式。它无法判断函数退出后源是否存在。虽然我们知道这是字符串文字、全局常量或任何生命周期与函数执行无关的东西,但编译器只是丢失了这些信息。相反,它扮演着防御角色,总是克隆价值观——只是为了有可能它将不复存在。我不确定,但这看起来很合理。尽管这种粗略的、不分青红皂白的codegen规则的结果在Delphi中又是一个难题:(

    在Delphi中,常量字符串总是在分配给另一个全局变量而不是局部变量时被复制,以避免在某些边界情况下发生访问冲突

    使用来源,卢克

    请参阅以下代码从System.pas中提取:

    {99.03.11
    分配给全局变量时使用此函数。
    复制文字是为了防止出现以下情况:
    分配的DLL或包为变量分配一个文本,然后
    卸载—从而导致字符串内存(在代码中
    要删除的DLL段,因此保留
    指向无效内存的全局变量。
    }
    程序_LStrAsg(var dest;const source);
    变量
    S、 D:指针;
    P:PStrRec;
    温度:长;
    开始
    S:=指针(源);
    如果是零那么
    开始
    P:=PStrRec(整数-sizeof(StrRec));
    如果P.refCnt<0,则//复制字符串文字
    开始
    温度:=P.长度;
    S:=\u新的翻译(临时);
    移动(指针(源)^,S^,临时);
    P:=PStrRec(整数-sizeof(StrRec));
    结束;
    联锁增量(P.refCnt);
    结束;
    ....
    
    因此,简而言之,根据设计,为了避免在卸载DLL或包时发生访问冲突,并且确实包含一些发送回主进程的常量值,始终创建本地副本

    您有两个功能:

    • LStrAsg
      UStrAsg
      ,当字符串有可能成为常量时由编译器生成-这是上面的代码
    • LStrLAsg
      ustrasg
      (添加的
      L
      代表“local”)由编译器在源字符串为local时生成,因此没有常量:在这种情况下,
      P.refCnt<0
      将不被检查,因此它将比上面的代码快

    这可能是由于具有局部作用域的变量与具有进行赋值的函数外部作用域的变量之间的差异造成的。当您赋值给
    结果
    变量时,您是在赋值给该函数外部可见的变量。当您赋值给局部时,它是该函数的专用变量。@David,是的,是的,但“那又怎样?”!为什么禁止将外部变量指向“-1”常量?如果需要,它将根据需要进行分离。在函数中主动这样做看起来是过早的优化,并且在“常识”下引入了完全不同的行为不会有什么区别。我只能认为编译器无法跟踪它从中分配的内容。它认为它分配的不是全局常量,而是堆栈上的一个内部表达式,这将丢失。那么立即克隆是有意义的。但这似乎太粗糙,太“全局”规则是它相当低效。Arnaud-看看_ustrasg和_LStrLAsg。它们也用于将常量复制到字符串。它们不会克隆它们。虽然有时可能会使用您的_LStrAsg,但有时Delphi会使用另一个具有不同行为的函数。为什么对看起来基本相同的操作进行拆分,这是问题的核心question.DLL/BPL卸载是公平的,但与const问题无关。BPL局部常量将与其BPL一起卸载。因此,感谢您的提示,但您的答案是不正确的。我实际上展示了以下代码(至少在XE2中)subversits ustrasg…当源字符串是本地字符串时,因此没有常量概念-它实际上是为字符串常量调用的,无论是命名的还是文字的。更重要的是,想象这样一个例程:
    procedure XXX;var s:string;h:THandle;begin h:=LoadPackage('PK1.BPL');s:=PK1.Unit1.Const1;UnloadPackage(h);ShowMessage(s);end;
    这一次DLL将被卸载,指向const的字符串指针将变为游离指针。我不认为Delphi会检测到这一点,并通过自动克隆字符串来保护我。
    P.refCnt<0不会被检查
    实际上也是不正确的。它将始终被检查,但如果它是负数,那么
    InterlockedIncrement
    将d被跳过,这实际上会增加多核环境中的速度。简历:你引用的是Delphi RTL源代码还是FreePascal源代码?如果是Delphi,那么是哪一个版本?看看其他Pascal编译器似乎很有趣,但不是Delphi。有任何事实性的评论吗?看起来我有一个个人仇恨粉丝:-DSorry,我
    { 99.03.11
      This function is used when assigning to global variables.
    
      Literals are copied to prevent a situation where a dynamically
      allocated DLL or package assigns a literal to a variable and then
      is unloaded -- thereby causing the string memory (in the code
      segment of the DLL) to be removed -- and therefore leaving the
      global variable pointing to invalid memory.
    }
    procedure _LStrAsg(var dest; const source);
    var
      S, D: Pointer;
      P: PStrRec;
      Temp: Longint;
    begin
      S := Pointer(source);
      if S <> nil then
      begin
        P := PStrRec(Integer(S) - sizeof(StrRec));
        if P.refCnt < 0 then   // make copy of string literal
        begin
          Temp := P.length;
          S := _NewAnsiString(Temp);
          Move(Pointer(source)^, S^, Temp);
          P := PStrRec(Integer(S) - sizeof(StrRec));
        end;
        InterlockedIncrement(P.refCnt);
      end;
    ....