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.

解决方案 »

  1.   

    又有人说编译器起错误.这情况下一般都是自己理解的不对
    procedure test(const arr:tmyarr);
    并不表示数组的内容不能改.表示的意思是arr这个变量不呢个被重新赋值而已.同理,传递const指针类型也表示指针本身的值不能被修改,指针指向数据的内容是可以改的.
      

  2.   

    嗯,const只是限制了 不能: arr:=temparr;
      

  3.   

    正解,一般当你觉得编译器有bug时,99.99%是你自己有bug
      

  4.   

    同样的程序,FREE PASCAL编译器就通不过,指示左边不能赋值,因为是常量参数。那至少说明,不是我的理解错误,而确实是编译器有缺陷,没有规范好,造成不一致的设计错误。在DELPHI里,虽然整个常量数组参数不能赋值,但却能改变其中的单个元素,那其实就是相当于整个变量都可以赋值改变呀。再进一步说,假设,这个例子不是用数组参数,而是用字符串,那么在过程里内,单个赋值通不过。但数组却可以通过。绝对让人困惑呀。
      

  5.   

    是你的理解问题,不要拿freepascal来说事。
      

  6.   

    这确实是一个BUG,BUG表现在:
    1. 如果不使用动态数组,而使用固定大小的数组(静态数组),编译通不过。
    2. 使用动态数组,但是把函数procedure test(const arr:tmyarr);直接声明为procedure test(const arr:array of char);编译通不过。
    3. 如果使用和动态数组同为指针类型的string,编译通不过,无论直接声明string类型还是间接定义string类型。
    也就是说,只有在参数为动态数组,且数组类型为间接定义的情况下,修改此数组元素可以编译通过,显然这是BUG,正常不应该编译通过的。4. 还有一个更严重的BUG,我先不说(涉及array of char)。:)
      

  7.   

    另外,string不是PChar,不能算是指针类型。
      

  8.   


    我在8楼里第3点说的意思是动态数组和string本质都是指针,如果“const 指针类型”参数只是不允许修改指针本身,指向的内容可以修改,那么标准应该是一致的。
      

  9.   

    string是copyonwrite的,不能算是普通的指针,所以不存在标准不一致的问题。
      

  10.   

    有一本是这样说的 var和const传递的都是指针。
      

  11.   


    莫名其妙。“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;
    不允许修改动态数组的内容?
      

  12.   


    都说错了,你还在那“确切点说”。并非所有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.”
      

  13.   

    这怎不是Bug????
    arr与arr[0]本来就不是同一东西...procedure test(const arr:tmyarr);
    var
      arr1:tmyarr;
    begin  
      arr:=arr1; //这样才不能
      arr[1] := 'X';
    end;
      

  14.   

    首先这不是bug,如果你认为他是bug,请百度delphi开放数组
    //----------------------------------------------
    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;
      

  15.   

    由他说去吧,他既然非要说这是bug,他向Embarcadero提交就是了。
      

  16.   


    关于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.
      这个,我没看太明白.  
      

  17.   


    实践出真知。“关于const指针的问题”我后面再举个例子证明楼主说的问题确实是个BUG。你解释的问题1,根本就是无的放矢,并不适用于楼主的例子。实际测试过没有?还是你自认为对Delphi的编译器足够了解以至于不需要实践就可以下结论?你说的“Delphi中是通过可变类型(TVarRec)的开放数组来指定变长参数的。它允许向一个方法传递未指定大小的数组”这说的“变长参数”,是用open array constructor[]括起来的若干个参数(数量不定),使用这种open array parameter的函数被调用的时候编译器隐含传递一个参数([]之中参数的数量),但是open array parameter也可以不用[]构造器传参数,而直接传一个array of <type>的动态数组,这两种使用方式的参数传递并不一样,后者在函数被调用的时候只传入一个指针。
      

  18.   


    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类型吗?:)
      

  19.   


    不是同一类型,但是在上述定义中,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;
      

  20.   

    这种解释:
    “关于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之类的结果怎样,你那套逻辑解释得通吗?
      

  21.   

    为了说明问题,给出一个具体例子:
    {$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能编译通过?:)
      

  22.   

    首先const不是不允许修改,而是告诉编译器,他指向的内存应该不会被修改,它保护了一快内存,当该内存的数据被修改时会抛出异常对于开放数组的参数delphi是会进行优化的,delphi官方文档里建议加上const或则var修饰符就是为了防止静态数组赋值时栈溢出的问题,这里的const修饰地优化这一点与编译器密切相关,虽然值类型参数传递按惯例需要将参数复本传递到函数中,但因为delphi编译器的优化,这里传递的实际上是地址。由于变量和数组被const修饰后,参数被优化成地址,当你的代码对其内存进行修改会抛出异常,而传递指向动态数组的指针类型时,实际上是一个指针的指针,所以const只保护了指针指向的指针不被修改,而指针的指针的内存被修改则不会抛出异常
    至于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;
      

  23.   


    你唠叨了这么多,根本不着边际。
    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!传入的是一个指向数据的指针,不是指针的指针!你能不能先研究一下编译器生产的代码再出来说?

      

  24.   

    to DelphiGuy,我还不太认同你的观点(千万别向我开炮,仅是探讨)这里涉及一个概念.动态数组,和 开放数组,这是我以前建立的概念,也不记得来源是哪里了.
    当然我现在也没仔细去找来源(懒了,呵呵).
    过程里参数的定义,使用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,毕竟这种含义不明确的
    东西会给使用者造成困绕.
      

  25.   


    这个问题我前面已经说过几次了,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。
      

  26.   


    "对比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者间最严格的规则.
      

  27.   

    补充一下  "考察check3的参数",由上面的结论,我也可以推断,无const定义的 array of char参数,
    无法使用SetLength. 经验验证,也的确如此.
      

  28.   

    我明确说了,28楼代码的check1到check6,参数的传递都传指针,不是传值,也不是传指针的指针,建议先检查一下编译器生成的代码(编译不过的代码注释掉,check3参数改成const A: array of byte可以通过,const A: array of char不行,这是个大BUG)再解释。
      

  29.   

    to DelphiGuy
    你提供的代码.我编译过了的. ^_^(编译不过的代码注释掉,check3参数改成const A: array of byte可以通过,const A: array of char不行,这是个大BUG)
    奇怪了,我编译const A: array of byte 与 const A: array of char都无法通过呀.
    难道,是我们编译器不同?我用的DelphiXE2 update4
      

  30.   

    “理所当然的,开放数组参数不能套用动态数组的规则,而要套用2者间最严格的规则.”哪个严格你理解正确吗?既然“开放数组不仅兼容动态数组...”而不是反之,那么开放数组参数的限制应该适用于动态数组参数,好比const integer参数不允许被修改,const byte参数也不应该允许被修改一样,后者是前者的类型子集。
      

  31.   

    我认为,参数传递也不光是传值或者传指针这么简单的内容.如果考虑string 以及 interface,可以发现,在参数传递过程中.const还有更隐含的含义.
    比如使用const修饰string参数时会导致string的引用计数不增加.
    interface也是一样.如果使用const修改接口参数,则不会隐含调用_addref.因此,在多线程中使用const参数,需要格外小心.
      

  32.   


    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。
      

  33.   


    大佬,别着急,别着急.讨论问题而已.不要激动.
    或许我们严格的理解不一样.这样吧.我提个问题.
    对于过程: procedure check3(v: array of char); 适用动态数组规则,
    那么也就是可以使用 SetLength(v,100);
    而如果发生了,check3被传入了一个静态数组,那么这个SetLength该如何作为呢
      

  34.   


    你说的很对,但是与讨论的问题无关。很明确28楼代码的check1到check6,参数的传递都传指针,不是传值,也不是传指针的指针。所谓“实际传递的是指针的指针.因此,数组成员可以修改是合理的”这种理论,funxu说了一次,你还说,都是不动手想当然的结果。:)
      

  35.   

    要下班回家了.回家了再讨论
    说下我的观点先:
        我认为这个可以不算BUG,但是语意上还让人误解的,或者说容易产生歧异的.
    没有铁证证明它是个BUG,即便提交给易博龙,他们也应该不会理睬.但是,作为建议
    我觉得的确可以让开放数组的定义与动态数组更大一点.还好有lz的回复,不然都没法回复了.
      

  36.   


    你又扯远了,这里讨论的是const open array parameter允许修改内容是否BUG的问题,你把const修饰去掉举例有什么说服力?
    而且,你的例子举反了吧,我说的是“既然“开放数组不仅兼容动态数组...”而不是反之,那么开放数组参数的限制应该适用于动态数组参数,好比const integer参数不允许被修改,const byte参数也不应该允许被修改一样,后者是前者的类型子集。”
    是“开放数组参数的限制应该适用于动态数组参数”,而不是反之,而你的例子是拿动态数组规则往开放数组参数上套,这不反了吗?
      

  37.   


    本来应该编译通过(如果不写数组的话),但是参数如果是const A: array of char就通不过,所以说是BUG。