【问题标题】:Delphi 10.3 functions CharUpper and CharUpperW differ Delphi 10.4Delphi 10.3 函数 CharUpper 和 CharUpperW 不同 Delphi 10.4
【发布时间】:2020-11-01 01:17:16
【问题描述】:

有人知道为什么 Delphi 10.3 中的 CharUpperCharUpperW 声明与 Delphi 10.4 中的声明不同。

Delphi 10.3 中的正确声明

    var
      chr   :WideChar;
    begin      
      chr := WideChar(CharUpperW(PWideChar('a'))); //chr = 'A'
//    chr := WideChar(CharUpperW(WideChar('a')));  //raise exeption: "access violation...

Delphi 10.4 中的正确声明

    var
      chr   :WideChar;
    begin   
//    chr := WideChar(CharUpperW(PWideChar('a')));  //raise exeption: "access violation...
      chr := WideChar(CharUpperW(WideChar('a')));   //chr = 'A'

编辑:Remy Lebeau 对 PWideChar 的解释是正确的,但关于 Delphi 10.4 版和更早版本仍然存在差异!

Lebeau解释代码样例在10.4及更早版本编译,但函数输出不同。 10.4 之前的所有版本都得到正确的输出“A”!

var
  char , chr : WideChar;
begin
  chr := 'a';
  char := WideChar(CharUpperW(PWideChar(chr)));
end;

此示例在 10.4 下无法正常工作,输出为随机字符。

当然... 函数CharUpperW的声明在Delphi的船版中是一样的。

LPWSTR = PWideChar; 
function CharUpperW(lpsz: LPWSTR): LPWSTR; stdcall;**

编辑:在 10.4 下添加反汇编代码

umCommon.pas.114: chr := 'a';
0064C52C 66BB6100         mov bx,$0061
umCommon.pas.115: char := WideChar(CharUpperW(PWideChar(chr)));
0064C530 8D45FC           lea eax,[ebp-$04]
0064C533 8BD3             mov edx,ebx
0064C535 E8EEE7DBFF       call @UStrFromWChar
0064C53A 8B45FC           mov eax,[ebp-$04]
0064C53D E8C2E7DBFF       call @UStrToPWChar
0064C542 50               push eax
0064C543 E8809DDCFF       call CharUpperW

10.3下的反汇编代码

umCommon.pas.114: chr := 'a';
0063A905 66BB6100         mov bx,$0061
umCommon.pas.115: char := WideChar(CharUpperW(PWideChar(chr)));
0063A909 0FB7C3           movzx eax,bx
0063A90C 50               push eax
0063A90D E8AAB1DDFF       call CharUpperW

【问题讨论】:

  • 你能比较CharUpperW在10.3和10.4中的声明吗(单位Winapi.Windows.pas)?
  • @zed CharUpperW 在 10.3 和 10.4 中的声明是一样的! LPWSTR = PWideChar;函数 CharUpperW(lpsz: LPWSTR): LPWSTR;标准调用;
  • 10.4 从单个字符中生成隐藏字符串,并将指向该字符串的指针指向 CharUpperW,然后它就停止了。

标签: delphi


【解决方案1】:

Win32 CharUpperW() 函数不接受单个WideChar 作为输入,而只接受一个PWideChar。但是该指针的解释取决于它的高位字是零(低位字包含单个字符)还是非零(整个指针指向一个以空字符结尾的字符串) .

CharUpperW(WideChar('a')) 在任何版本中甚至可以编译的唯一方法是,Embarcadero 是否添加了自己的重载,该重载将单个 WideChar 作为输入(我没有安装任何 10.x 版本来验证) .

恕我直言,CharUpperW() 使用起来有点危险,因为滥用了指针。我不会相信它与类型转换的文字,而是使用一个变量,这样你就可以确保你给它的正是它真正想要的:

var
  chr : array[0..1] of WideChar;
begin
  chr[0] := 'a';
  chr[1] := #0;
  CharUpperW(chr);
end;
var
  chr : WideChar;
begin
  chr := 'a';
  char := WideChar(CharUpperW(PWideChar(chr)));
end;

话虽如此,RTL 中还有其他函数可以处理此任务,请改用这些函数。

更新:我为此创建了支持票:

RSP-31498: Bad codegen breaks Winapi.Windows.CharUpperW()

【讨论】:

  • Delphi 10.3 中没有CharUpperW 的重载版本,但两种变体都编译成功。另外,看看这个反汇编代码:imgur.com/OEZ3843 - 编译器将 char 值放入堆栈,而不是指针。当我们使用 WideChar('a') 调用函数时,它会放置指针,这会导致 AV。这一切意味着什么?
  • 抱歉,您的带有 WideChar 第二个示例的代码在 10.4 版中仍会得到与早期版本的 Delphi 不同的输出。检查我的答案的更正...
  • 我明白了。 10.3 工作正常 - 它为 PWideChar('a') 放置单个字符值或为 WideString 输入放置指针。正是docs 所要求的。 @GJ您可以查看10.4下的反汇编代码吗?我认为编译器对单字符输入有问题。
  • @GJ 您不应该编辑答案,而是将其添加到您的问题中。
  • 对不起,我知道...我要快速并按下 Remy Lebeau 编辑而不是我的...更正
【解决方案2】:

由于 Delphi 10.4 创建隐藏字符串并且CharUpperW 在处理单字符或以空字符结尾的字符串输入时充当different,因此您需要重写代码以使其与 10.4 和 10.3(及更早)版本保持一致:

var
  s: string;
begin
  s := 'a';
  CharUpperW(@s[1]);
end;

附:由于这些困难,我建议您使用 System.SysUtils 中的UpperCase / AnsiUpperCase

【讨论】:

  • 是的...UpperCase / AnsiUpperCase 为了我的目的要放慢速度。
  • @s[1]PChar(s) 一样
  • @RemyLebeau 不是。在第一种情况下编译器调用UniqueStringU 并且一切正常,在第二种情况下它调用UStrToPWChar 并且发生了AV。
  • @GJ。 “UpperCase/AnsiUpperCase 是为了我的目的而减速” - 你试过TCharHelper.ToUpper() 了吗?
  • @Remy Lebeau。不,我正在使用大写表: UpCaseTable :array[Char] of Char;在程序开始时填充。
【解决方案3】:

好的,在您的帮助下,我找到了答案... 实际上,CharUpperCharUpperW 函数在 Winapi.Windows 单元中,并且都调用相同的 API 调用 CharUpperW。 但是在单元Winapi.Windows 的Delphi 10.4 版中是新的函数重载:

function CharUpper(lpsz: LPWSTR): LPWSTR; overload; stdcall; external user32kernel name 'CharUpperW';
function CharUpper(tch: WideChar): WideChar; overload; stdcall;
begin
  Result := WideChar(IntPtr(CharUpperW(LPWSTR(IntPtr(tch)))));
end;

编译器采用这个重载函数而不是旧函数,结果在我的问题中描述。

为了避免这个重载函数,我们必须更正类型转换变量Chr 以获得正确的结果:

var
  Chr     :Char; //Or WideChar 
begin
  Chr := 'a';   
  Chr := Char(CharUpper(LPWSTR(ord(Chr))));

编辑:我们也可以像这样调用 CharUpperW API 函数:

var
  Chr     :WideChar; //or Char
begin
  Chr := 'a';   
  Chr := Char(CharUpper(LPWSTR(ord(Chr))));

反汇编代码相同:

umCommon.pas.109: Chr := 'a';
0063A906 66BB6100         mov bx,$0061
umCommon.pas.110: Chr := Char(CharUpperW(LPWSTR(ord(Chr))));
0063A90A 0FB7C3           movzx eax,bx
0063A90D 50               push eax
0063A90E E8A9B1DDFF       call CharUpperW
0063A913 8BD8             mov ebx,eax

【讨论】:

  • 函数CharUpper 和重载与它无关,但类型转换ord(Chr) 可以。再次打开反汇编代码,看看会发生什么。
  • @zed right 和 typecasted 也必须是 CharUpper 函数,修正答案。反汇编的代码现在是一样的。!
  • Compiler takes this overload function instead old one - 不,这是错误的!在您的示例代码中,您直接调用CharUpperW,因此编译器没有使用任何重载。由于编译器的新行为而发生错误。使用ord 添加类型转换会强制新的编译器表现得像以前一样。
  • @zed 没有功能 CharUpperWCharUpper 是一样的!检查单元Winapi.Windows中的功能。 CharUpperW 也超载了!
  • 我没有 10.4。如果CharUpperW以同样的方式重载,那么你可以像Chr := CharUpperW(Chr);一样使用它不是吗?
猜你喜欢
  • 2020-12-21
  • 1970-01-01
  • 2020-12-16
  • 1970-01-01
  • 1970-01-01
  • 2021-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多