【问题标题】:Casting Delphi 2009/2010 string literals to PAnsiChar将 Delphi 2009/2010 字符串文字转换为 PAnsiChar
【发布时间】:2011-01-01 17:06:12
【问题描述】:

所以问题是是否可以将 Delphi 2009/2010 中的字符串文字(或 const 字符串)直接转换为 PAnsiChar,或者他们是否需要先对 AnsiString 进行额外的转换才能使其工作?

背景是我正在使用 C 接口调用旧版 DLL 中的函数,该 C 接口具有一些需要 C 样式字符指针的函数。在过去(在 Delphi 2009 之前),像下面这样的代码就像一个魅力(其中 C DLL 函数的参数是 LPCSTR):

要么:

LegacyFunction(PChar('Fred'));

const
  FRED = 'Fred';
...
LegacyFunction(PChar(FRED));

所以在更改为 Delphi 2009(现在是 2010 年)时,我将调用更改为:

LegacyFunction(PAnsiChar('Fred'));

const
  FRED = 'Fred';
...
LegacyFunction(PAnsiChar(FRED));

这似乎有效,我从函数调用中得到了正确的结果。然而,应用程序中存在一些明显的不稳定性,这似乎主要是通过调用遗留函数的代码第二次或第三次发生(在迁移到 IDE 的 2009 版本之前不存在)。在对此进行调查时,我意识到 Delphi 2009/2010 中的本机字符串文字(和 const 字符串)是一个 Unicode 字符串,因此我的转换可能有误。这里和其他地方的例子似乎表明这个调用应该更像这样:

LegacyFunction(PAnsiChar(AnsiString('Fred')))

让我感到困惑的是,对于第二个示例中的上述代码,将字符串文字直接转换为 PAnsiChar 不会产生任何编译器警告。如果不是字符串文字,而是转换字符串 var,我会收到一个可疑的转换警告(并且字符串会被破坏)。这(以及字符串在 DLL 中可用的事实)使我相信编译器正在做一些魔术来正确地将字符串文字解释为预期的字符串类型。这是正在发生的事情,还是双重转换(首先是 AnsiString,然后是 PAnsiChar)真的有必要,而我的代码中缺少它是难以追踪不稳定的原因吗?同样的答案是否也适用于 const 字符串?

【问题讨论】:

    标签: delphi pointers string


    【解决方案1】:

    “形象化”巴里·凯利和梅森·惠勒的话:

    const
      FRED = 'Fred';
    
    var
      p: PAnsiChar;
      w: PWideChar;
    begin
      w := PWideChar(Fred);
      p := PAnsiChar(Fred);
    
    In ASM:
    Unit7.pas.32: w := PWideChar(Fred);
    00462146 BFA4214600       mov edi,$004621a4     
    // no conversion, just a pointer to constant/"-1 RefCounted" UnicodeString
    
    Unit7.pas.33: p := PAnsiChar(Fred);
    0046214B BEB0214600       mov esi,$004621b0
    // no conversion, just a pointer to constant/"-1 RefCounted" AnsiString
    

    正如您在 PWideChar/PChar(FRED) 和 PAnsiChar(FRED) 两种情况下看到的,没有转换,Delphi 编译器生成 2 个常量字符串,一个 AnsiString 和一个 UnicodeString。

    【讨论】:

    • 即使没有类型转换,您也会得到相同的结果。
    • 这是关于编译器发出的两个不同内部常量的有趣信息。
    【解决方案2】:

    正如 Mason Wheeler 指出的那样,只要您在字符串 const 中包含非 ANSI 字符,一切都很好。如果您有以下情况:

    const FRED = 'Frédérick';
    

    我很确定 Delphi 2009/2010 会发出字符集提示(并自动应用字符串转换 - 因此提示)或比较失败(ISO-8859-1 中的“Frédérick”与 UTF-16 不同) .

    如果您的 const 中可以包含“特殊”字符,则需要调用字符串转换。

    以下是一些 TStringList 的基本示例:

    TStringList.SaveToFile(DestFilename, TEncoding.GetEncoding(28591)); //ISO-8859-1 (Latin1)
    TStringList.SaveToFile(DestFilename, TEncoding.UTF8);
    

    【讨论】:

      【解决方案3】:

      对于类型推断的常量(只能从字面量初始化),编译器会在编译时更改实际文本,而不是在运行时。这意味着它知道转换是否会丢失数据,因此如果没有,它不需要警告您。

      【讨论】:

      • 而且,除非存在编译器需要帮助消除歧义的重载函数,否则根本不需要类型转换。
      【解决方案4】:

      常量(包括字符串字面量)在默认情况下是无类型的,编译器会将它们适应在您使用它们的上下文中有效的任何格式。只要您的字符串字面量中没有非 ANSI 字符,在这种情况下,编译器将字符串生成为 ANSI 而不是 Unicode 不会有任何问题。

      【讨论】:

        猜你喜欢
        • 2010-09-21
        • 2023-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多