http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/compdirswriteabletypedconstants_xml.html
Writeable consts refers to the use of a typed const as a variable modifiable at runtime. For example: Copy Code
const
foo: Integer = 12;
begin
foo := 14;
end.
Writeable consts refers to the use of a typed const as a variable modifiable at runtime. For example: Copy Code
const
foo: Integer = 12;
begin
foo := 14;
end.
procedure test(const arr:tmyarr);
并不表示数组的内容不能改.表示的意思是arr这个变量不呢个被重新赋值而已.同理,传递const指针类型也表示指针本身的值不能被修改,指针指向数据的内容是可以改的.
1. 如果不使用动态数组,而使用固定大小的数组(静态数组),编译通不过。
2. 使用动态数组,但是把函数procedure test(const arr:tmyarr);直接声明为procedure test(const arr:array of char);编译通不过。
3. 如果使用和动态数组同为指针类型的string,编译通不过,无论直接声明string类型还是间接定义string类型。
也就是说,只有在参数为动态数组,且数组类型为间接定义的情况下,修改此数组元素可以编译通过,显然这是BUG,正常不应该编译通过的。4. 还有一个更严重的BUG,我先不说(涉及array of char)。:)
我在8楼里第3点说的意思是动态数组和string本质都是指针,如果“const 指针类型”参数只是不允许修改指针本身,指向的内容可以修改,那么标准应该是一致的。
莫名其妙。“string是copyonwrite的”和const参数能否修改内容有什么关系?你解释了个马呀。“因为指针类型和@操作符的存在,想限制修改数组的内容是不可能的。”和楼主说的const参数能被修改的BUG有什么关系???楼主用了“指针类型和@操作符”去修改参数了吗?你来解释一下,为什么
type
TArray1 = array of char;procedure Check1(const A: TArray1);
begin
A[0] := '1'; // passed
end;
允许修改动态数组的内容而
procedure Check2(const A: array of char);
begin
A[0] := '1'; // "Error: E2064 Left side cannot be assigned to"
end;
不允许修改动态数组的内容?
都说错了,你还在那“确切点说”。并非所有const参数都是引用,还有传值的,Delphi Language Reference中,“Constant parameters”部分明确说了“A constant (const) parameter is like a local constant or read-only variable. ... Using const allows the compiler to optimize code for structured- and string-type parameters.”
arr与arr[0]本来就不是同一东西...procedure test(const arr:tmyarr);
var
arr1:tmyarr;
begin
arr:=arr1; //这样才不能
arr[1] := 'X';
end;
//----------------------------------------------
1. 如果不使用动态数组,而使用固定大小的数组(静态数组),编译通不过。
2. 使用动态数组,但是把函数procedure test(const arr:tmyarr);直接声明为procedure test(const arr:array of char);编译通不过。
3. 如果使用和动态数组同为指针类型的string,编译通不过,无论直接声明string类型还是间接定义string类型。
//----------------------------------------------
首先说下,关于const指针的问题,早有人讨论过,可以去问度娘,这不是bug这里不再赘述
问题1 这个错误的根本原因不在传递的是指针还是指针的指针,而是在delphi函数参数的类型规范上,delphi中参数实际上是以变长数组的形式传递的,与C/C++不同,Delphi中是通过可变类型(TVarRec)的开放数组来指定变长参数的。它允许向一个方法传递未指定大小的数组。这也就是为什么静态数组的参数编译不通过,至于如果你非要传递静态数组进去,也不是不可以,记得传递指针的指针,顺便引用19L的回复
procedure test(const arr:tmyarr);
var
arr1:tmyarr;
begin
arr:=arr1; //这样才不能
arr[1] := 'X';//这样是可以的
end;
问题2 你确定编译不通过?反正我这是没问题的问题3 同问题2,我这里是没问题的
procedure testb(const a:array of string);
begin
ShowMessage(a[0]);
end;
关于DelphiGuy的看法,我想探讨下:
这是不是个BUG,我认为还不一定.
1.使用 "固定大小的数组(静态数组),编译通不过" 这个不足以证明问题.
原因:"动态数组" 这个名字的命名 容易 混淆"动态数组"与"静态数组"的差别. 我构造一个如下的东西:
TRec = record
x: Integer;
end;
PRec = ^TRec;
我可以说: TRec是"静态记录" PRec 是"动态记录",那么他的表现和"动态数组"与"静态数组"
其实是一样的.(或许你会觉得我这是在诡辩,^_^) 作为语言使用者,这的确让人费解.但是从编译的角度来看,也是合理的.起码内部编译机制上,我
举的例子与,"动,静态数组"是一样的.
以上论点的一个证据是: 当不使用const对参数进行修饰时,"静态记录","静态数组"都会产生
值拷贝,而"动态数组","动态记录"都只是传递指针而已.2.
type
tmyarr = array of char;
arr:tmyarr;
与直接 array of char;
Pascal语言本身就不认为是同一类型,例证:
var
v1: tmyarr;
v2: array of char;
begin
v1 := v2; //编译无法通过,因为编译器认为他们是不同的类型3.
这个,我没看太明白.
实践出真知。“关于const指针的问题”我后面再举个例子证明楼主说的问题确实是个BUG。你解释的问题1,根本就是无的放矢,并不适用于楼主的例子。实际测试过没有?还是你自认为对Delphi的编译器足够了解以至于不需要实践就可以下结论?你说的“Delphi中是通过可变类型(TVarRec)的开放数组来指定变长参数的。它允许向一个方法传递未指定大小的数组”这说的“变长参数”,是用open array constructor[]括起来的若干个参数(数量不定),使用这种open array parameter的函数被调用的时候编译器隐含传递一个参数([]之中参数的数量),但是open array parameter也可以不用[]构造器传参数,而直接传一个array of <type>的动态数组,这两种使用方式的参数传递并不一样,后者在函数被调用的时候只传入一个指针。
2:
procedure Check3(const A: array of char);
begin
A[0] := '1'; // "Error: E2064 Left side cannot be assigned to"
end;你居然能编译通过???什么Delphi版本?D7和DXE都通过不了。3:我都说了“如果使用和动态数组同为指针类型的string,编译通不过,无论直接声明string类型还是间接定义string类型。”你还来个array of string!array of string是string类型吗?:)
不是同一类型,但是在上述定义中,array of char类型包含tmyarr类型。
Delphi帮助 一节中明确说了“The previous example creates a function that takes any array of Char elements, including (but not limited to) dynamic arrays. To declare parameters that must be dynamic arrays, you need to specify a type identifier:type TDynamicCharArray = array of Char;
function Find(A: TDynamicCharArray): Integer;”v1 := v2; //编译无法通过,但是可以v1 := tmyarr(v2); 或者tmyarray(v2) := v1;
“关于const指针的问题,早有人讨论过,可以去问度娘,这不是bug这里不再赘述
...顺便引用19L的回复
procedure test(const arr:tmyarr);
var
arr1:tmyarr;
begin
arr:=arr1; //这样才不能
arr[1] := 'X';//这样是可以的
end;”是想当然,按这种理解,const指针参数(指的并不仅限于传入显式指针类型,也包括传入的参数实际是指针的情况,比如string和动态数组)只是指针本身不允许修改,指向的内容可以修改,看起来说得头头是道,实际上根本没有这种标准!我在8楼都明确说了“也就是说,只有在参数为动态数组,且数组类型为间接定义的情况下,修改此数组元素可以编译通过”,某些人就是不愿意实践一下,你看看把const arr:tmyarr换成const arr:array of char、const arr:string之类的结果怎样,你那套逻辑解释得通吗?
{$APPTYPE CONSOLE}
program testbug;type
TArray1 = array of char;
TArray2 = array[0..2] of char;
TString1 = string;
TString2 = string[3];var
a: TArray1;
b: TArray2;
c: array of char;
d: TString1;
e: TString2;
f: string;procedure Check1(const A: TArray1);
begin
A[0] := '1'; // compiling passed, BUG1
end;procedure Check2(const A: TArray2);
begin
A[0] := '1'; // "Error: E2064 Left side cannot be assigned to", OK
end;procedure Check3(const A: array of char);
begin
A[0] := '1'; // "Error: E2064 Left side cannot be assigned to", OK
end;procedure Check4(const A: TString1);
begin
A[1] := '1'; // "Error: E2064 Left side cannot be assigned to", OK
end;procedure Check5(const A: TString2);
begin
A[1] := '1'; // "Error: E2064 Left side cannot be assigned to", OK
end;procedure Check6(const A: string);
begin
A[1] := '1'; // "Error: E2064 Left side cannot be assigned to", OK
end;{ main }
begin
SetLength(a, 3);
b[0] := '0';
SetLength(c, 3);
SetLength(d, 3);
e[1] := '0';
SetLength(f, 3);
Check1(a);
Check2(b);
Check3(c); // "Error: E2010 Incompatible types: 'array of Char' and 'Dynamic array'", BUG2
Check4(d);
Check5(e);
Check6(f);
end.看看你能不能解释得通为什么只有Check1能编译通过?:)
至于string为什么编译不通过,是因为字符串虽然是指针,但是他是一个特殊的指针,是会被默认初始化的,也就是初始化的内存依旧会被保护
//--------------------------------
ta=array of Char;
procedure test(const a:ta);
begin
a[0]:='c';//指针指向的指针的内容是可以修改的,因为const只限定了指针指向的指针内容不允许修改
a[1]:='d';
end;
//----------------------------------------
ta=array [0..1] of Char;
//procedure test(const a:arry of char);//同下面不允许修改
procedure test(const a:ta);
begin
a[0]:='c';//直接编译不通过,leftside不允许修改,因为数组指针和指向静态数组内存的指针指向的内存都受到了保护
a[1]:='d';
end;
//----------------------------------------
ta=array [0..1] of Char;procedure test( a:ta);
begin
a[0]:='c';//可以编译,但是修改的内容无效,这是因为没有const修饰的参数传递的是副本而不是指针
a[1]:='d';
end;
你唠叨了这么多,根本不着边际。
Delphi帮助中Constant parameters部分明确说了:“A constant (const) parameter is like a local constant or read-only variable. Constant parameters are similar to value parameters, except that you can't assign a value to a constant parameter within the body of a procedure or function, nor can you pass one as a var parameter to another routine. (But when you pass an object reference as a constant parameter, you can still modify the object's properties.)”
显然这已经表明了const参数不能修改的性质(当然是不能直接修改,通过一个指针指向实际数据来间接修改是可以的),至于所谓“当该内存的数据被修改时会抛出异常”则纯属胡扯。但这不是问题的关键,问题的关键是你(还有某些其他人)是怎么解释楼主说的BUG“不是BUG”的?是所谓““关于const指针的问题,早有人讨论过,可以去问度娘,这不是bug”吧,也就是const指针参数(指的并不仅限于传入显式指针类型,也包括传入的参数实际是指针的情况,比如string和动态数组)只是指针本身不允许修改,指向的内容可以修改,没错吧???那就请你用这个“理论”解释一下为什么const A: tmyarr指向的内容允许修改而const A: array of char指向的内容不允许修改?不要东拉西扯。另外,“而传递指向动态数组的指针类型时,实际上是一个指针的指针,所以const只保护了指针指向的指针不被修改,而指针的指针的内存被修改则不会抛出异常”,建议你实践之后再说,不要满嘴跑火车,我在24楼都说了“但是open array parameter也可以不用[]构造器传参数,而直接传一个array of <type>的动态数组,这两种使用方式的参数传递并不一样,后者在函数被调用的时候只传入一个指针。”显然楼主的例子是直接传入一个动态数组,不是使用[]的open array parameter!传入的是一个指向数据的指针,不是指针的指针!你能不能先研究一下编译器生产的代码再出来说?
当然我现在也没仔细去找来源(懒了,呵呵).
过程里参数的定义,使用array of xxx,应该是属于开放数组的范畴,而不是动态数组.简单搜索了下,找到一个<Pascal精要>中的一段文字:
在Delphi 4中,给定类型的开放数组与动态数组完全兼容(动态数组将在第8章中介绍)。动态数组的语 法与开放数组相同,区别在于你可以用诸如array of Integer指令定义变量,而不仅仅是传递参数以下列举一个不算非常合理的证据,可以证明函数/过程中的参数使用 array of 与
定义中的 array of 是不同的;如下:
procedure Proc(parm: array of const); 是可以的
但是无法定义:
type
TDynArray = array of const;我只是求教,别向我开炮,我也希望找到足够的证据证明它是BUG,毕竟这种含义不明确的
东西会给使用者造成困绕.
这个问题我前面已经说过几次了,Delphi帮助中明确说了open array parameter包含同类型的动态数组(但不仅限于此),open array parameter的传递有两种方式,一种是通过[]构造器,另一种是直接传入同型的动态数组。楼主的例子是后一种使用。如果不是BUG,那么对于const open array parameter的修改性应该是一致的,起码对于const(实际的)指针类型应该保持一致。
见28楼的代码,check1~check6都是传入const指针,而只有check1能编译通过,其他通不过,显然前面某些人用“const指针”解释不是BUG的说法是没有说服力的。另外对比check1和check3,参数类型都是const open array parameter,唯一的区别是check1严格限制必须传入动态数组,不能使用[]构造器,check3两者都可接受,实际调用的情况是两者都传入动态数组,但是checkl1能编译通过,check3不行,显然语义上的一致性被破坏还没有合理的解释,这就属于BUG。
"对比check1和check3,参数类型都是const open array parameter"
我不这么认为.考察check1的参数:
认为check1是明确说明,参数必须是动态数组
1.使用const,数组成员可以修改.
我的理解是,无论是否使用const修饰,实际传递的是指针的指针.因此,数组成员可以修改是合理的.
2.不使用const,则数据长度可以修改,即可以使用SetLength函数
使用const,则无法调用SetLength(编译通不过),考虑第1条,传递的指针的指针,那么const
导致动态数组无法调用SetLength就非常合理了.考察check2的参数
我认为check2也是明确说明,参数必须是静态数组
1.使用const,数组成员不可修改
也很好理解,静态数组,成员不可修改,理应如此
2.无论是否使用const,无法使用SetLength,也是合理的考察check3的参数
我认为check3也说明参数是开放数组,而不是动态数组,开放数组不仅兼容动态数组,也应该
兼容静态数组.我想,我构造一个静态数组应该也是可以传入.
因此,我在加入了,Check3(b)测试,编译通过.也就是说,开放数组可以接受静态数组,那么,
理所当然的,开放数组参数不能套用动态数组的规则,而要套用2者间最严格的规则.
无法使用SetLength. 经验验证,也的确如此.
你提供的代码.我编译过了的. ^_^(编译不过的代码注释掉,check3参数改成const A: array of byte可以通过,const A: array of char不行,这是个大BUG)
奇怪了,我编译const A: array of byte 与 const A: array of char都无法通过呀.
难道,是我们编译器不同?我用的DelphiXE2 update4
比如使用const修饰string参数时会导致string的引用计数不增加.
interface也是一样.如果使用const修改接口参数,则不会隐含调用_addref.因此,在多线程中使用const参数,需要格外小心.
check3参数改成const A: array of byte实际调用也应该传入一个array of byte类型的变量:
var
c: array of byte;procedure Check3(const A: array of byte);
begin
//
end;begin
check3(c);
end.是可以编译通过的,测试了D7和DXE。
大佬,别着急,别着急.讨论问题而已.不要激动.
或许我们严格的理解不一样.这样吧.我提个问题.
对于过程: procedure check3(v: array of char); 适用动态数组规则,
那么也就是可以使用 SetLength(v,100);
而如果发生了,check3被传入了一个静态数组,那么这个SetLength该如何作为呢
你说的很对,但是与讨论的问题无关。很明确28楼代码的check1到check6,参数的传递都传指针,不是传值,也不是传指针的指针。所谓“实际传递的是指针的指针.因此,数组成员可以修改是合理的”这种理论,funxu说了一次,你还说,都是不动手想当然的结果。:)
说下我的观点先:
我认为这个可以不算BUG,但是语意上还让人误解的,或者说容易产生歧异的.
没有铁证证明它是个BUG,即便提交给易博龙,他们也应该不会理睬.但是,作为建议
我觉得的确可以让开放数组的定义与动态数组更大一点.还好有lz的回复,不然都没法回复了.
你又扯远了,这里讨论的是const open array parameter允许修改内容是否BUG的问题,你把const修饰去掉举例有什么说服力?
而且,你的例子举反了吧,我说的是“既然“开放数组不仅兼容动态数组...”而不是反之,那么开放数组参数的限制应该适用于动态数组参数,好比const integer参数不允许被修改,const byte参数也不应该允许被修改一样,后者是前者的类型子集。”
是“开放数组参数的限制应该适用于动态数组参数”,而不是反之,而你的例子是拿动态数组规则往开放数组参数上套,这不反了吗?
本来应该编译通过(如果不写数组的话),但是参数如果是const A: array of char就通不过,所以说是BUG。