我很抱歉提出一个非常基本的问题.请考虑以下示例:
const
c1 = 1; // Is this Byte or ShortInt?
c2 = 1234; // Is this Word or Smallint?
c3 = 123456; // Is this Cardinal or Integer?
Run Code Online (Sandbox Code Playgroud)
阅读本文档后,我可以得出结论,负值被解释为有符号,正值被解释为无符号.但是,例如123456(根据文档将被解释为Cardinal)也可以在上下文中使用Integer,我的意思是它用Integer在计算中使用常量的变量中.因此,是否始终保证不断地将Cardinal类型转换为Integer必要的?
该文档(XE8是最新的版本,因为我写这篇文章),告诉你,真正的常量有型.但是,在指定实际类型时,文档会产生误导.当我说出误导时,我有些善意.
如果您阅读此官方文档,那么您会倾向于认为无符号类型比签名类型更受欢迎.但是这个程序表明情况并非如此:
program SO32160057_overloads;
{$APPTYPE CONSOLE}
procedure foo(value: UInt8); overload;
begin
Writeln('UInt8');
end;
procedure foo(value: UInt16); overload;
begin
Writeln('UInt16');
end;
procedure foo(value: UInt32); overload;
begin
Writeln('UInt32');
end;
procedure foo(value: UInt64); overload;
begin
Writeln('UInt64');
end;
procedure foo(value: Int8); overload;
begin
Writeln('Int8');
end;
procedure foo(value: Int16); overload;
begin
Writeln('Int16');
end;
procedure foo(value: Int32); overload;
begin
Writeln('Int32');
end;
procedure foo(value: Int64); overload;
begin
Writeln('Int64');
end;
const
ZeroInt32 = Int32(0);
ZeroUInt16 = UInt16(0);
begin
foo(127);
foo(128);
foo(32767);
foo(32768);
foo(2147483647);
foo(2147483648);
foo(9223372036854775807);
foo(9223372036854775808);
foo(ZeroInt32);
foo(ZeroUInt16);
foo(UInt8(0));
end.
Run Code Online (Sandbox Code Playgroud)
输出是:
Int8 UInt8 Int16 UInt16 Int32 UInt32 Int64 UInt64 Int32 UInt16 UInt8
我们来看看另一个程序:
program SO32160057_comparisons;
var
Int8var: Int8 = 0;
Int16var: Int16 = 0;
Int32var: Int32 = 0;
begin
if Int8var < 127 then ;
if Int8var < 128 then ; // line 10
if Int8var < Int16(128) then ; // line 11
if Int16var < 32767 then ;
if Int16var < 32768 then ; // line 13
if Int16var < Int32(32768) then ; // line 14
if Int32var < 2147483647 then ;
if Int32var < 2147483648 then ; // line 16
if Int32var < Int64(2147483648) then ;
end.
Run Code Online (Sandbox Code Playgroud)
编译器发出以下警告:
(10): W1022 Comparison always evaluates to True (10): W1023 Comparing signed and unsigned types - widened both operands (11): W1022 Comparison always evaluates to True (13): W1022 Comparison always evaluates to True (13): W1023 Comparing signed and unsigned types - widened both operands (14): W1022 Comparison always evaluates to True (16): W1022 Comparison always evaluates to True (16): W1023 Comparing signed and unsigned types - widened both operands
因此,通过我的实证分析,编译器会查看整数文字的值,并通过查找以下列表中可以表示值的第一个类型来确定其类型:
Int8UInt8Int16UInt16Int32UInt32Int64UInt64可以使用类型转换语法指定类型来覆盖此规则.例如,要声明一个Int32具有价值0,你会写Int32(0).
现在让我们将该规则应用于您在问题中给出的具体示例,即123456.根据上面的规则,列表中可以表示此值的第一个类型是Int32.也称为Integer.
现在,因为这是一个无符号类型,您可能希望与无符号UInt32变量进行比较将导致警告W1023,比较有符号和无符号类型.但事实并非如此.编译器识别出这123456是一个正值,我们正在比较两个正值.另一方面,警告发出-123456.
program SO32160057_123456;
var
UInt32var: UInt32 = 0;
begin
if UInt32var > 123456 then ; // line 7
if UInt32var > -123456 then ; // line 8
end.
Run Code Online (Sandbox Code Playgroud)
编译器发出以下警告:
(8): W1022 Comparison always evaluates to True (8): W1023 Comparing signed and unsigned types - widened both operands
回答你的原始问题:
每个常量的数据类型是什么?
它们或多或少都是无类型的,并且在使用它们时会假定一种类型,就像文字值一样.它们不占用空间并且通常没有地址,除非它们必须(例如字符串,集合等).ISTM,您可以将它们视为"代表文字的符号".它们可以与#defineC中的简单s(IOW,文本替换)或汇编程序中的立即值进行比较.所以'C'可以是a AnsiChar,a WideChar,an AnsiString,a WideString或an UnicodeString,甚至可以分配给a PChar,PWideChar或PAnsiChar.类型取决于你分配的内容,就像文字一样.整数值或浮点类型,集类型等相同.
请注意,编译器(以及 - IMO不正确 - 文档)将为您提供一个类型,如果您要求,并且通常是可以保存它的最小整数类型,或者一个Extended或Double用于浮点值,或枚举或集合类型它属于.但那就是AFAICT,只有你问.
只有"无类型"或真常量才能用于常量表达式.正如你所说,类型常量(占用真实空间的IOW常量)或多或少像"不可变变量"(是的,我知道这是一个矛盾),IOW,它们有一个地址,一个类型,一个定义的大小,但是它们不是,呃,变量或可变的(正如大卫所说,忘记可写的类型常量).
我相信这里的一些人会完全不同意,但是,嘿,这就是我一直认为真正的常数,而这种观点从未让我失望过.
如何强制常量具有特定的数据类型?我的意思是除了类型常量之外的另一种方式,如果我是对的,它实际上是常量变量.
你可以通过强制转换给出一个真正的常量:
const
Bla = UInt64(1234); // $0000000000001234
Doo = Cardinal(-1); // $FFFFFFFF
Duh = Shortint(rfReplaceAll);
Run Code Online (Sandbox Code Playgroud)
但是我不认为你可以转换为其中一种浮点类型.这种演员阵容通常是禁止的,也适用于常数(AFAIK,目前无法测试).
现在期待一些尖锐的评论或支持.:-)
因此,是否始终保证不断地将
Cardinal类型转换为Integer必要的?
不需要演员阵容.这种类型并不总是Cardinal无论如何,即使文档这么说(正如我所说,我认为他们错了).是很简单的,如果你使用的文字值123456在该点.你可以说它也123456有类型,但实际上,它更容易假装它没有.类型取决于上下文,它可能会被编译为类似的东西
MOV EAX,123456
MOV [TheVariable],EAX
Run Code Online (Sandbox Code Playgroud)
解释方式取决于变量的类型.例如,如果你这样做:
MyDouble := 123456;
Run Code Online (Sandbox Code Playgroud)
这并不意味着a Cardinal已编译并且Double必须进行转换.它直接编译为Double值123456.0.可能存在转换,但仅在编译器内部.
所以,IMO,不要再担心这种常数的类型了.简单地将它们视为表示文字的符号,并假设它们将获得您期望它们获得的类型.你很少会错的.
看看以下代码:
const
CHi = 'Hello';
CInt = $1234;
CTInt: Word = $1234;
var
CVInt: Word = $1234;
procedure Test;
var
A: AnsiString;
U: UnicodeString;
I: Integer;
D, E, F: Double;
begin
A := CHi;
U := CHi;
I := CInt;
D := CInt;
E := CTInt;
F := CVInt;
Writeln(A, U, I, D); // Just to make this compile.
end;
Run Code Online (Sandbox Code Playgroud)
和拆卸这个:
Project44.dpr.25: A := CHi;
00419584 8D45FC lea eax,[ebp-$04]
00419587 BA40964100 mov edx,$00419640
0041958C E84BE0FEFF call @LStrLAsg
Project44.dpr.26: U := CHi;
00419591 8D45F8 lea eax,[ebp-$08]
00419594 BA54964100 mov edx,$00419654
00419599 E8A2DFFEFF call @UStrLAsg
Project44.dpr.27: I := CInt;
0041959E C745F434120000 mov [ebp-$0c],$00001234
Project44.dpr.28: D := CInt;
004195A5 33C0 xor eax,eax
004195A7 8945E8 mov [ebp-$18],eax
004195AA C745EC0034B240 mov [ebp-$14],$40b23400
Project44.dpr.29: E := CTInt;
004195B1 0FB705A0D54100 movzx eax,[$0041d5a0]
004195B8 8945D4 mov [ebp-$2c],eax
004195BB DB45D4 fild dword ptr [ebp-$2c]
004195BE DD5DE0 fstp qword ptr [ebp-$20]
004195C1 9B wait
Project44.dpr.30: F := CVInt;
004195C2 0FB705A2D54100 movzx eax,[$0041d5a2]
004195C9 8945D4 mov [ebp-$2c],eax
004195CC DB45D4 fild dword ptr [ebp-$2c]
004195CF DD5DD8 fstp qword ptr [ebp-$28]
004195D2 9B wait
Run Code Online (Sandbox Code Playgroud)
如您所见,当分配给a AnsiString或a 时,字符串常量的值不相同UnicodeString.复制反汇编的数据部分并不容易,因此您必须在自己的Delphi IDE中检查它,但是在地址处$0041961C,有一个文字AnsiString(refcount -1)'Hello',而在地址处$00419630,那里是一个文字的UnicodeString'Hello'.这意味着常量被编译为上下文所需的类型,并且不再需要显式或隐式转换(即从UnicodeStringto到AnsiString,或从Wordto到Double).
另请注意,与真常量相反,类型常量确实需要转换代码.该字被加载为movzx eax,[$0041d5a0],即转换Word为DWord,然后存储为DWord,加载到FPU中DWord,然后存储为64位浮点值(Double).这是一个你看不到的转换,因为它不是真正的常量.
我知道有些人会说:"好吧,它是UnicodeString,但是编译器首先将它转换为AnsiString." 国际海事组织,就像说:"汽车的原始车身是灰色的,但在销售(指定)之前,它必须涂成绿色." 我宁愿说:"出售的汽车是绿色的,无论它是如何变色的." 当然,文字和常量(IOW,在一const节中定义的符号)将具有默认类型,在查询时给出并且没有请求特定类型,但是出于所有实际目的,如果您只是将它们视为"文字",则最简单使用名称标签",而不是特定类型,IOW 实际类型取决于上下文.这是怎么回事无所谓.
我认为大卫和我的意思相同,但用不同的词语和不同的词语.他说:它有类型A,但它被编译器转换为类型B并且结果被编译.这正是我的意思,"类型取决于上下文." 无需转换.将"类型UInt16" 常量赋值给a 时,可以看到相同的情况Double:不需要转换,它直接编译为64位值并存储在Double.没有明确或暗示的转换.所以在那种情况下,声明的常量$1234具有64位浮点值,而不是a UInt16.
正如您所看到的,类型化常量的代码是1到1等效于变量的代码,表明类型化常量可以被视为"不可变变量",即使该术语实际上不存在.
另请注意,真常量甚至没有地址.您可以轻松地自己尝试:
if @CInt = nil then;
Run Code Online (Sandbox Code Playgroud)
你会看到它没有编译(Variable required错误).但现在尝试:
if @CTInt = nil then;
Run Code Online (Sandbox Code Playgroud)
没有这样的错误,它也支持类型化常量是"不可变变量"这一事实.