Delphi 对于非寄存器大小的操作数,shl和shr的行为是什么?

Delphi 对于非寄存器大小的操作数,shl和shr的行为是什么?,delphi,Delphi,这个问题的灵感来源于我试图回答另一个问题: 我能找到的按位移位运算符的唯一用法是: 操作x shl y和x shr y将x的值向左或向右移位y位,y位(如果x是无符号整数)等于x乘以或除以2^y;结果与x的类型相同。例如,如果N存储值01101(十进制13),则N shl 1返回11010(十进制26)。请注意,y的值是按x类型的大小来解释的。因此,例如,如果x是整数,那么x shl 40被解释为x shl 8,因为整数是32位,而40 mod 32是8 考虑一下这个计划: {$APPTYPE

这个问题的灵感来源于我试图回答另一个问题:

我能找到的按位移位运算符的唯一用法是:

操作x shl y和x shr y将x的值向左或向右移位y位,y位(如果x是无符号整数)等于x乘以或除以2^y;结果与x的类型相同。例如,如果N存储值01101(十进制13),则N shl 1返回11010(十进制26)。请注意,y的值是按x类型的大小来解释的。因此,例如,如果x是整数,那么x shl 40被解释为x shl 8,因为整数是32位,而40 mod 32是8

考虑一下这个计划:

{$APPTYPE CONSOLE}
program BitwiseShift;
var
  u8: Byte;
  u16: Word;
  u32: LongWord;
  u64: UInt64;
begin
  u8 := $ff;
  Writeln((u8 shl 7) shr 7);
  // expects: 1 actual: 255

  u16 := $ffff;
  Writeln((u16 shl 15) shr 15);
  // expects: 1 actual: 65535

  u32 := $ffffffff;
  Writeln((u32 shl 31) shr 31);
  // expects: 1 actual: 1

  u64 := $ffffffffffffffff;
  Writeln((u64 shl 63) shr 63);
  // expects: 1 actual: 1
end.
我已经在32位和64位Windows编译器的XE3和XE5中运行了这个程序,并且输出是一致的,如上面代码中所述

我希望
(u8 shl 7)shr 7
将完全在8位类型的上下文中进行评估。因此,当位移位超过8位类型的末尾时,这些位将丢失

我的问题是为什么程序会这样做


有趣的是,我把程序翻译成C++,在我的64位MIW4.4.3上获得了相同的输出。
#包括
#包括
int main()
{
uint8_t u8=0xff;

std::cout 7)我将您的测试修改为

procedure TestByte;
var
  u8 : Byte;
  LShift : Integer;
begin
  Writeln( 'Byte' );
  u8 := $FF;
  LShift := 7;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 15;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 31;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 63;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
end;

procedure TestWord;
var
  u8 : Word;
  LShift : Integer;
begin
  Writeln( 'Word' );
  u8 := $FF;
  LShift := 7;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 15;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 31;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 63;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
end;

procedure TestLongWord;
var
  u8 : LongWord;
  LShift : Integer;
begin
  Writeln( 'LongWord' );
  u8 := $FF;
  LShift := 7;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 15;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 31;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 63;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
end;

procedure TestUInt64;
var
  u8 : UInt64;
  LShift : Integer;
begin
  Writeln( 'UInt64' );
  u8 := $FF;
  LShift := 7;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 15;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 31;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
  LShift := 63;
  Writeln( IntToHex( u8, 16 ), '-', LShift : 2, ' ', IntToHex( u8 shl LShift, 16 ), ' ', IntToHex( ( u8 shl LShift ) shr LShift, 16 ) );
end;

begin
  TestByte;
  TestWord;
  TestLongWord;
  TestUInt64;
end.
它给了我这个结果

Byte 00000000000000FF- 7 0000000000007F80 00000000000000FF 00000000000000FF-15 00000000007F8000 00000000000000FF 00000000000000FF-31 0000000080000000 0000000000000001 00000000000000FF-63 0000000080000000 0000000000000001 Word 00000000000000FF- 7 0000000000007F80 00000000000000FF 00000000000000FF-15 00000000007F8000 00000000000000FF 00000000000000FF-31 0000000080000000 0000000000000001 00000000000000FF-63 0000000080000000 0000000000000001 LongWord 00000000000000FF- 7 0000000000007F80 00000000000000FF 00000000000000FF-15 00000000007F8000 00000000000000FF 00000000000000FF-31 0000000080000000 0000000000000001 00000000000000FF-63 0000000080000000 0000000000000001 UInt64 00000000000000FF- 7 0000000000007F80 00000000000000FF 00000000000000FF-15 00000000007F8000 00000000000000FF 00000000000000FF-31 0000007F80000000 00000000000000FF 00000000000000FF-63 8000000000000000 0000000000000001 字节 00000000000000 FF-700000000 7F80 00000000000000 FF 00000000000000 FF-15 0000000000 7F8000 000000000000000 FF 00000000000000 FF-31 00000000 80000000000000000000000 1 00000000000000 FF-63 00000000 80000000000000000000000 1 单词 00000000000000 FF-700000000 7F80 00000000000000 FF 00000000000000 FF-15 0000000000 7F8000 000000000000000 FF 00000000000000 FF-31 00000000 80000000000000000000000 1 00000000000000 FF-63 00000000 80000000000000000000000 1 长词 00000000000000 FF-700000000 7F80 00000000000000 FF 00000000000000 FF-15 0000000000 7F8000 000000000000000 FF 00000000000000 FF-31 00000000 80000000000000000000000 1 00000000000000 FF-63 00000000 80000000000000000000000 1 UInt64 00000000000000 FF-700000000 7F80 00000000000000 FF 00000000000000 FF-15 0000000000 7F8000 000000000000000 FF 00000000000000 FF-31 000000 7F80000000 00000000000000 FF 00000000000000 FF-63 800000000000000000000000 1 因此,在内部,这些值不会以声明的类型进行处理,原因是:

隐式类型转换的一种特殊情况是类型提升,其中 编译器会自动扩展的二进制表示形式 整数或浮点类型的对象。升级通常是 与小于目标平台的本地类型的类型一起使用 ALU在进行算术和逻辑运算之前,为了使 操作是可能的,或者如果ALU可以使用更多 C和C++对对象进行这样的升级 布尔、字符、宽字符、枚举和短整数 升级为int的类型,对于float类型的对象 升级为双精度。与其他类型转换不同,升级 切勿丢失精度或修改存储在对象中的值

所以在下面的代码中

var
  u8: Byte;

begin
  u8 := $ff;
  Writeln((u8 shl 7) shr 7);
..
u8
值在
shl
之前提升为32;要修复结果,需要显式类型转换:

  Writeln(Byte(u8 shl 7) shr 7);

C++标准,第4.5节积分促销:

char、signed char、unsigned char、short int或 如果int可以表示,则无符号短int可以转换为int类型的右值 源类型的所有值;否则,源右值可以为 已转换为类型为unsigned int的右值


为了检查Delphi在类型升级中是否遵循相同的约定,我编写了以下应用程序:

var
  u8: Byte;
  u16: Word;
  u32: LongWord;

procedure Test(Value: Integer); overload;
begin
  Writeln('Integer');
end;

procedure Test(Value: Cardinal); overload;
begin
  Writeln('Cardinal');
end;

begin
  u8 := $ff;
  Test(u8);     // 'Integer'
  u16 := $ffff;
  Test(u16);    // 'Integer'
  u32 := $ffffffff;
  Test(u32);    // 'Cardinal'
  Readln;
end.

所以我相信这里的Delphi和C++之间没有区别。

场景背后发生的事情其实很有趣。 给定以下Delphi应用程序:

program BitwiseShift;
var
  u8: Byte;
begin
  //all in one go
  u8 := $ff;
  Writeln((u8 shl 7) shr 7);   
  // expects: 1 actual: 255

  //step by step
  u8 := $ff;
  u8:= u8 shl 7;
  u8:= u8 shr 7;
  WriteLn(u8);  
  // expects: 1 actual: 1
end.
产生以下组件(在XE2中)


问题中的代码表明,它们不是以声明的类型处理的。除了得到特殊处理的32/64位整数之外,它看起来非常像是底层ISA被反射回了语言中。@DavidHeffernan我猜
u8:byte;u8:=$ff;u8:=u8 shl 7;u8:=u8 shr 7;
应该是预期的d ResultInDect.或更紧凑的
((u8 shl 7)和$ff)shr 7
。我刚刚用TP55进行了测试,结果类似(寄存器大小为16而不是32)。因此我猜按位操作使用(max)默认情况下注册大小变量。为什么不直接询问Embarcadero?他们应该从Delphi 3手册中给出关于整数类型的正确答案:
在执行任何算术运算之前,将任何字节大小的操作数转换为与Smallint和word兼容的中间字大小的操作数。
@LURD这些是b根据DOCS的分类,即“代码> >,”,“div”,“mod”,“SHL”,“SHR”,作为,将操作归类为乘法运算符。这意味着编译器对它们应用相同的表达式语法。不,这是当你应该学习C++理解Delphi时的情况。这对我有什么帮助。我清楚地看到了什么。我正在寻找一些文档。所以我相信这里没有Delphi和C++的区别。C++标准没有定义Delphi语言。
BitwiseShift.dpr.10: Writeln((u8 shl 7) shr 7);
004060D3 33D2             xor edx,edx
004060D5 8A1594AB4000     mov dl,[$0040ab94]
004060DB C1E207           shl edx,$07
004060DE C1EA07           shr edx,$07
004060E1 A114784000       mov eax,[$00407814]  <<--- The result is NOT a byte!!
004060E6 E895D6FFFF       call @Write0Long
004060EB E864D9FFFF       call @WriteLn
004060F0 E8A7CCFFFF       call @_IOTest
BitwiseShift.dpr.13: u8 := $ff;
004060F5 C60594AB4000FF   mov byte ptr [$0040ab94],$ff
BitwiseShift.dpr.14: u8:= u8 shl 7;
004060FC C02594AB400007   shl byte ptr [$0040ab94],$07
BitwiseShift.dpr.15: u8:= u8 shr 7;
00406103 33C0             xor eax,eax
00406105 A094AB4000       mov al,[$0040ab94]
0040610A C1E807           shr eax,$07
0040610D A294AB4000       mov [$0040ab94],al
BitwiseShift.dpr.16: WriteLn(u8);
00406112 33D2             xor edx,edx
00406114 8A1594AB4000     mov dl,[$0040ab94]
0040611A A114784000       mov eax,[$00407814]
0040611F E85CD6FFFF       call @Write0Long
00406124 E82BD9FFFF       call @WriteLn
00406129 E86ECCFFFF       call @_IOTest
Writeln(byte(byte(u8 shl 7) shr 7));
// expects: 1 actual: 1

Project24.dpr.19: Writeln(byte(byte(u8 shl 7) shr 7));
00406135 8A1594AB4000     mov dl,[$0040ab94]
0040613B C1E207           shl edx,$07
0040613E 81E2FF000000     and edx,$000000ff
00406144 C1EA07           shr edx,$07
00406147 81E2FF000000     and edx,$000000ff
0040614D A114784000       mov eax,[$00407814]
00406152 E829D6FFFF       call @Write0Long
00406157 E8F8D8FFFF       call @WriteLn
0040615C E83BCCFFFF       call @_IOTest