我很抱歉提出一个非常基本的问题。请考虑以下示例:
Router.map(function() {
this.route('home', {path : '/'});
this.resource('posts', function() {
this.route('new', {path: '/:post_id'});
});
});
阅读此documentation后,我可以得出结论,负值被解释为 signed ,而正值被解释为 unsigned 。但是,例如 123456 (根据文档将被解释为const
c1 = 1; // Is this Byte or ShortInt?
c2 = 1234; // Is this Word or Smallint?
c3 = 123456; // Is this Cardinal or Integer?
)也可以在Cardinal
的上下文中使用,我的意思是它用于Integer
在计算中使用常量的变量。因此,常量保证始终为Integer
,以便对Cardinal
进行必要的转换?
答案 0 :(得分:7)
documentation(XE8是我写的最新版本)告诉你 true constants 有一个类型。但是,在指定实际类型时,文档会产生误导。当我说出误导时,我有些善意。
如果您阅读此官方documentation,那么您会倾向于认为无符号类型比签名类型更受欢迎。但是这个程序表明情况并非如此:
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.
输出结果为:
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.
编译器发出以下警告:
(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
因此,通过我的实证分析,编译器会查看整数文字的值,并通过查找以下列表中可以表示值的第一个类型来确定其类型:
Int8
UInt8
Int16
UInt16
Int32
UInt32
Int64
UInt64
可以使用类型转换语法指定类型来覆盖此规则。例如,要声明值为Int32
的{{1}},您需要编写0
。
现在让我们将该规则应用于您在问题中提供的具体示例,即Int32(0)
。根据上面的规则,列表中可以表示此值的第一个类型是123456
。也称为Int32
。
现在,因为这是一个无符号类型,您可能希望与无符号Integer
变量进行比较将导致警告W1023,比较有符号和无符号类型。但事实并非如此。编译器认识到UInt32
是一个正值,我们正在比较两个正值。另一方面,警告以123456
发出。
-123456
编译器发出以下警告:
(8): W1022 Comparison always evaluates to True (8): W1023 Comparing signed and unsigned types - widened both operands
答案 1 :(得分:5)
回答您的原始问题:
每个常量的数据类型是什么?
它们或多或少是无类型的,并且在使用它们时会采用类型,就像文字值一样。它们不占用空间并且通常没有地址,除非它们必须(例如字符串,集合等)。 ISTM你可以考虑它们&#34;表示文字的符号&#34;。可以将它们与C(IOW,文本替换)中的简单#define
或汇编程序中的立即值进行比较。因此'C'
可以是AnsiChar
,WideChar
,AnsiString
,WideString
或UnicodeString
,甚至可以分配给PChar
PWideChar
,PAnsiChar
或Extended
。类型取决于你分配的内容,就像文字一样。整数值或浮点类型,集类型等相同。
请注意,编译器(以及 - IMO不正确 - 文档)会在您询问时为您提供一个类型,通常这是可以容纳它的最小整数类型,或Double
或{{1}用于浮点值,或者它所属的枚举或集合类型。但那就是AFAICT,只有你问。
只有&#34;无类型&#34;或者常量表达式中可以使用真常量。正如你所说,类型常量(占用真实空间的IOW常量)或多或少类似于不可变变量&#34; (是的,我知道这是一个矛盾的说法),IOW,他们有一个地址,一个类型,一个定义的大小,但它们不是,呃,变量或者是可变的(正如大卫所说,忘了可写类型常量)。
我确信这里的一些人会完全不同意,但是,嘿,这就是我一直认为真正的常数,而这种观点从来没有让我失望过。
如何强制常量具有特定数据类型?我的意思是除了类型常量之外的另一种方式,如果我正确的话,它实际上是常量变量。
你可以通过强制转换给出一个真正的常量:
const
Bla = UInt64(1234); // $0000000000001234
Doo = Cardinal(-1); // $FFFFFFFF
Duh = Shortint(rfReplaceAll);
但是,我不认为你可以转换为其中一种浮点类型。这种演员阵容通常是禁止的,也适用于常数(AFAIK,现在不能进行测试)。
现在期待一些尖锐的评论或支持。 : - )
因此,常量保证始终为
Cardinal
,以便对Integer
进行必要的转换吗?
不需要施法。无论如何,类型并不总是Cardinal
,即使文档这么说(正如我所说,我认为它们是错误的)。是就好像您在该位置使用了字面值123456
。你可以说123456
也有一种类型,但实际上,假装它并不容易。类型取决于上下文,它可能会编译为类似
MOV EAX,123456
MOV [TheVariable],EAX
如何解释它取决于变量的类型。例如,如果你这样做:
MyDouble := 123456;
这并不意味着编译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;
以及对此的反汇编:
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
如您所见,当分配给AnsiString
或UnicodeString
时,字符串常量的值不同。复制反汇编的数据部分并不容易,因此您必须在自己的Delphi IDE中进行检查,但在地址$0041961C
处,有一个文字AnsiString(refcount -1)&#39;您好&#39;,在地址$00419630
,有一个文字的UnicodeString&#39; Hello&#39;。这意味着常量被编译为上下文所需的类型,并且没有显式或隐式转换(即从UnicodeString
到AnsiString
,或从Word
到Double
)是必需的。
另请注意,与 true 常量相反,类型常量确实需要转换代码。该单词加载为movzx eax,[$0041d5a0]
,即从Word
转换为DWord
,然后存储为DWord
,以DWord
的形式加载到FPU中,然后存储为64位浮点值(Double
)。这是一个你不会看到的转换,因为它不是真正的常量。
我知道有些人会说:&#34;好吧,它是UnicodeString,但是编译器首先将它转换为AnsiString。&#34;国际海事组织,就像说:&#34;汽车的原始车身是灰色的,但在销售(指定)之前,它必须涂成绿色。&#34;我宁愿说:&#34;出售的汽车是绿色的,无论它是如何获得这种颜色的。&#34;当然,文字和常量(IOW,const
部分中定义的符号)将具有默认类型,在查询时给出并且没有请求特定类型,但用于所有实际目的 ,如果您只是将它们视为&#34;带有名称标签&#34;的文字,而不是属于特定类型,则最简单,IOW 实际类型取决于上下文< / strong>即可。这是怎么回事。
我认为大卫和我的意思相同,但用不同的词语和不同的词语。他说:它有类型A,但它被编译器转换为类型B并且结果被编译。这正是我所说的&#34;类型取决于上下文。&#34;无需转换。分配&#34;类型UInt16
&#34;的常量时可以看到相同的情况。到Double
:不需要转换,它直接编译为64位值并存储在Double
中。没有明确或暗示的转换。因此,在这种情况下,声明为$1234
的常量具有64位浮点值,而不是UInt16
。
正如您所看到的,类型化常量的代码是1到1等效于变量的代码,表明类型化常量可以被视为&#34;不可变变量&#34;,即使该术语并不存在。
另请注意,真常量甚至没有地址。您可以自己轻松尝试:
if @CInt = nil then;
您将看到它无法编译(Variable required
错误)。但现在尝试:
if @CTInt = nil then;
没有这样的错误,这也支持类型化常量是&#34;不可变变量&#34;。