4

请原谅这个愚蠢的问题,但我很困惑。考虑以下方法(对不起,嘈杂的评论,这是一个正在开发的真实代码):

function HLanguages.GetISO639LangName(Index: Integer): string;
const
  MaxIso639LangName = 9;  { see msdn.microsoft.com/en-us/library/windows/desktop/dd373848 }
var
  LCData: array[0..MaxIso639LangName-1] of Char;
  Length: Integer;
begin
  { TODO : GetLocaleStr sucks, write proper implementation }
  //Result := GetLocaleStr(LocaleID[Index], LOCALE_SISO639LANGNAME, '??');
  Length := GetLocaleInfo(LocaleID[Index], LOCALE_SISO639LANGNAME, @LCData, System.Length(LCData));
  Win32Check(Length <> 0);
  SetString(Result, @LCData, Length); // "E2008 Incompatible types" here, but why?
end;

如果我删除了引用运算符,那么隐式转换 from$X+就会进行救援并编译方法。为什么编译器使用引用运算符拒绝此代码超出了我的理解。

这是 Delphi XE2,这种行为可能是它特有的。


而且,如果我在此错误范围内添加一个具有等效原型的测试用例虚拟对象作为内在原型,HLanguages.GetISO639LangName它将神奇地消失:

procedure SetString(var s: string; buffer: PChar; len: Integer);
begin
  { test case dummy }
end;
4

4 回答 4

5

您必须将其显式转换为PChar

SetString(result,PChar(@LCData),Length); 

正如您所说,SetString()对第二个参数类型要求很高。它必须是 aPChar要么 aPWideChar要么 a PAnsiChar,具体取决于字符串类型本身。

我怀疑这是由于SetString()定义为使用 a string、 aWideString或 aAnsiString作为第一个参数重载的事实。因此,为了验证正确的签名,它需要与所有参数类型完全匹配:

SetString(var s: string; buf: PChar; len: integer); overload;
SetString(var s: AnsiString; buf: PAnsiChar; len: integer); overload;
SetString(var s: WideString; buf: PWideChar; len: integer); overload;

当然,所有这些都是“内在的”,所以你不会在 system.pas 中找到这样的定义,而是直接一些类似_LStrFromPCharLen() _UStrFromPCharLen() _WStrFromPWCharLen()或这样的过程。

这种行为与早期版本的 Delphi 相同,并且不是 XE2 中的回归。

于 2012-11-28T21:33:12.563 回答
4

我认为那里存在编译器错误,因为行为与SetString您提供的重载函数的行为不同。此外,还有与 Typed @ 运算符编译器选项的交互。不知道你是怎么设置的。我总是启用它,但我怀疑我在那里是少数。

所以我不能解释奇怪的行为,也不能回答你问的确切问题。我怀疑回答它的唯一方法是查看编译器的内部结构,而我们中很少有人能做到这一点。

无论如何,如果它有帮助,我认为传递参数的最干净的方法是这样的:

SetString(Result, LCData, Length); 

无论您将 Typed @ 运算符设置为什么,它都会编译。

于 2012-11-28T22:28:11.273 回答
3

我知道这并不能回答有关 SetString 的具体问题,但我想指出,您可以通过简单地编写来做同样的事情

Result := LCData;

分配给字符串时,Delphi 将具有起始索引的 char 静态数组视为具有最大长度的 Null 终止字符串。考虑以下:

var
  IndexOneArray  : array [ 1 .. 9 ] of char;
  IndexZeroArray : array [ 0 .. 8 ] of char;
  S : string;
  T : string;
begin
  IndexOneArray  := 'ABCD'#0'EFGH';
  IndexZeroArray := 'ABCD'#0'EFGH';
  S := IndexOneArray;
  T := IndexZeroArray;

  ShowMessage (      'S has ' + inttostr(length(S)) + ' chars. '
                + #13'T has ' + inttostr(length(T)) + ' chars. ' );
end;

这将显示一条消息,即 S 有 9 个字符,而 T 有 4 个。它也适用于零索引数组有 9 个非空字符时。结果将是 9 个字符,无论以下内存位置是什么。

于 2012-11-29T13:48:43.337 回答
0

因为LCData是指向的指针array,而不是指向的Char。当然,有时会发生数组或记录或类以 char 类型变量开头,但结果不是静态类型编译器应该依赖的。

您必须将指针指向该数组中的字符,而不是指向数组本身。

SetString(Result, @LCData[Low(LCData)], Length); 
于 2016-04-14T09:52:14.570 回答