为什么SetString内在函数会导致PChar参数出现“不兼容类型”错误?

时间:2012-11-28 21:27:35

标签: delphi delphi-xe2

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

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;

如果我删除了引用运算符,那么来自$X+的隐式转换将进入救援并进行方法编译。为什么编译器拒绝使用引用运算符的代码是我无法理解的。

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


如果我在HLanguages.GetISO639LangName范围内添加一个具有等效原型作为内在原型的测试用例,这个错误将会神奇地消失:

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

4 个答案:

答案 0 :(得分:5)

您必须明确地将其转换为PChar

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

正如你所说,SetString()对第二个参数类型要求很高。它必须是PChar PWideCharPAnsiChar,具体取决于字符串类型本身。

我怀疑这是因为SetString()被定义为重载了stringWideStringAnsiString作为第一个参数。因此,为了验证正确的签名,它需要与所有参数类型完全匹配:

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中的回归。

答案 1 :(得分:4)

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

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

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

SetString(Result, LCData, Length); 

无论你将Typed @ operator设置为。

,都会编译

答案 2 :(得分:3)

我知道这并没有回答关于SetString的具体问题,但我想指出你可以通过简单的写作来做同样的事情

Result := LCData;

当分配给字符串时,Delphi将具有 ZERO 起始索引的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个字符。

答案 3 :(得分:0)

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

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

SetString(Result, @LCData[Low(LCData)], Length);