活跃一下D版的气氛,出个趣味只是题。下面的代码中,定义了一个记录类型,定义了2个同名的方法(同不同名无所谓),v1和v2的x字段都初始化为10,分别调用2个方法后。提问:
1、v1.x与v2.x的值分别为多少?(要求不要在机上测试)
2、加入从记录定义中去掉y字段,v1.x与v2.x的值又分别为多少?(要求不要在机上测试)
3、为什么会是这样的结果?希望大家讨论。
type
  TTest = record
    x: Integer;
    y: Integer;
  end;procedure IncX(var v: TTest); overload;
begin
  Inc(v.x);
end;function IncX: TTest; overload;
begin
  Inc(Result.x);
end;var
  v1, v2: TTest;
begin
  v1.x := 10;
  v2.x := 10;
  v1 := IncX;
  IncX(v2);
end;

解决方案 »

  1.   

    v1.x 1
    v2.x 11不知道对不对
      

  2.   

    1 结果都是11
    2 v1 结果随机 v2 结果11v1是局部变量的时候是直接把v1当result的
    如果改成全局变量result是栈中的另一块地址 返回后再赋值给v1 这样的话就得到随机的值了当v1只有一个integer的字段的时候 好像就没有特殊处理
      

  3.   

    v1 := IncX;
    这样的赋值?
     没见过
      

  4.   

     v1 := IncX;
    这样的语句不懂, 
    偶是delphi新手
      

  5.   


    v1.x因为incX()是对一个未初始化的result进行inc,结果是不确定的
    一般未初始化就是0,看编译器是统一更新为0还是不做处理
    incX()这样的函数应该绝对不允许出现
      

  6.   

    还是发哥厉害,想出这样的题来折腾人我想答案主要取决于delphi编译器对结构体类型的Result的处理方式(似乎lz也有这方面的暗示),要么直接传入地址赋值,要么先创建一个局部变量再拷贝。如果这个猜想成立的话,那Y就成为一个关键,是不是只有一个x的时候,会采用直接传入地址?(因为调用过程对x赋值可以保证记录体的完整性,即要么成功,要么失败)而有y的时候,则会采用后一种方式(即随机值)?
      

  7.   

    IncX代码确实有问题。
    一般来说,编译系统再返回值时应该会在堆栈上建立临时变量,Pascal更应该如此。
      

  8.   

    我觉得目前编译器为1,11是正确的,但是为什么存在y定义的时候就显示成11,11,这个确实没想通,不过这种错误的IncX: TTest代码实际应用没有多少意义,估计只有了解和研究编译器的时候才需要考虑这些问题。
    在实际使用中搞这个有点无聊。
      

  9.   


    哎,被发哥忽悠了猜错了,Delphi对结构体的处理方式是一样的,都是在栈上面创建临时变量,赋值后再Copy。连续运行两次就知道了。
        v1 := IncX;
        v1 := IncX;唯一一点不同是有y的时候第一次Result的初始化的值均为0,不知道这是和编译器有关的,还是说,其中有什么玄机?
      

  10.   

    应该和编译器相关的,我在d2010下面,有y的时候,第一次Result的值全为初始值,没有y的时候,全部为随机值。
      

  11.   

    编译器怎么传参数跟基础一点关系都没有,有几个知道eax和st(0)是啥东西的,而且不知道寄存器对理解语言本身一点影响都没有。除了写内嵌汇编的时候需要了解一下究竟参数是怎么传的以外,平时根本就没必要关心
      

  12.   

    呵呵,聪明人还是不少的,ZuoBaoquan和lhylhy分析的基本接近事实真相,看样子不用到100楼就要结贴了。Delphi函数在返回值的处理上,对于32简单数据类型(不包括浮点数类型),是使用eax返回的,如Integer等;对于64位简单数据类型,是使用edx:eax返回的,如int64类型,而对于结构体返回值来说,长度在32位及以下是通过eax返回的,32位以上与lhylhy说的有点区别,不是“32位以上用寄存器返回指针”,而是在调用前传递了一个默认指针参数,如本贴的function IncX: TTest;函数,在TTest类型长度大于32位时,实际上等同于于procedure IncX(var v: TTest)或者procedure IncX(v: ^TTest)。这类问题虽然在实际代码极少出现,但也并非是有些人认为的“无聊”或者是与Delphi基础知识无关的问题,它至少反映了Delphi在复杂数据类型返回值问题上与其它语言的区别,这也是Delphi函数高效性特征的一种表现。如C/C++,用函数返回一个复杂结构体是采用逐字段一一赋值的,这种返回的效率是相当低的,所以,我们很少看到直接返回复杂结构体的C/C++函数函数,而大多数是采用void Proc(TTest &v)或者void Proc(TTest *v)这种返回形式,而在Delphi中,返回复杂结构体的函数却是比比皆是,特别是在Delphi属性的返回值上,就只能是这种形式。另外,本贴第一句就说了,发贴的目的是“活跃一下D版的气氛”,没想到有些人比我这个老头还古板,认为没意思,甚至是无聊,确实叫我汗颜了。
      

  13.   

    那你猜这几个是eax返回呢,还是变成参数呢?
    T1 = packed record
      a: Byte;
      b: Word;
    end;T2 = record
      x: array[1..3]of Byte;
    end;T3 = record
      DynArr: array of Integer;
    end;T4 = record
      S: string;
    end;
    它们可都是32位及32位以下的delphi传参数的细节多着呢,传 procedure/function of object 类型的参数还有bug,但这些东西只是编译器的实现,跟基础一点关系都没有。
      

  14.   

    我说的第一个结构不用猜,eax返回,另外的传递指针
      

  15.   

    T3 = record 
      DynArr: array of Integer; 
    end; T4 = record 
      S: string; 
    end; 
    这两个的字段在Delphi内部本身就属于指针
      

  16.   

    T2 = record 
      x: array[1..3]of Byte; 
    end;
    这个为什么是传递指针而不是靠eax返回?表面看来,这个数据却是不超过32位,但实际上数组类型在Delphi是另外处理的,它的第一个元素前面还有隐含的计数字段,这和string类型有些相似,所以实际上是大于32位的
      

  17.   

    更正:这和string[]类型有些相似(短字符串)
      

  18.   

    so? interface也是指针,string也是指针,TObject也是指针,TClass也是指针,Pointer也是指针,PInteger也是指针,您是想说,因为上面这些都是指针,所以作为返回值的时候它们的用法都是一样的么?
    我当然会测了,只是不知道别人会不会。比如这样的代码
    type
      T1  = packed record
        a : Byte;
        b : Word;
      end;
      function foo: T1;
      begin
      end;
    var t : t1;
    begin
      t := foo;
      ~~~~~~~~
    我看到反汇编成了
      mov eax, esp
      call foo
    当然就知道是传指针了而不是eax返回了,不知道您测过没?
    让人说啥好吧……静态数组没有数组长度和引用计数信息,Low/High/Length对静态数组来说都是编译期运算符。而且这个也很好测啊,SizeOf(T2)明明就是3的,不知道隐含计数字段还能放哪
      

  19.   

    T2 = record 
      x: array[1..3]of Byte; 
    end; 
    这个确实是我张冠李戴搞错了,搞成动态数组了,谢谢指正,它是靠eax返回的,而  T1  = packed record 
        a : Byte; 
        b : Word; 
      end;
    是eax返回没错的,不知你是怎么测试的
      

  20.   

    看到
      mov eax, esp
      call foo
    还能坚持说是靠eax返回,那我也没啥好说的了
      

  21.   

    我明白了,呵呵,你是想着法子和我这老头子绕弯弯,你把record前加了pached,我还以为是在我前面定义的记录修改的。如此一来,如果Delphi还用eax返回,有可能造成其它变量被破坏,所以只好传递指针了。算了,算我服你了,我没你懂得多。行了吧?
    结贴,以后再也不搞此类游戏把戏了。
      

  22.   

    1、2、4字节的集合、记录、静态数组参数是作为8、16、32位值传送的,否则传32位指针(对记录类型有一个例外,非寄存器调用约定时,小尺寸的记录类型参数通过栈传送其长度是32位对齐的)。见Delphi帮助。
      

  23.   

    阿发伯在45楼的解释大部分是正确的,只是有一个小问题,“对于结构体返回值来说,长度在32位及以下是通过eax返回的”恰好不包括3字节长度的。
      

  24.   

      谢谢DelphiGuy,我在发贴时只是考虑了趣味性,而没有考虑packed的记录类型,因为定义3字节长度记录,如果不是packed记录类型,也会按4字节处理,这时肯定是靠eax返回,但是加了packed,再靠eax返回值,就有可能破坏临近返回值的其它变量的低字节。
      呵呵,我见C/C++版经常有此类趣味贴,一时兴起,发了这么个贴,没想到D版这么无趣,也给我自己带来没趣。
      

  25.   

    delphi版应该多一些这样的贴子。
      

  26.   

    都是11 ,不过看了上面的回答,让我这Delphi新手有了新的领悟啊~~
      

  27.   

    都说了,显然上面我列的4种情况都是传指针的,不光是3字节的情况。就跟c语言讨论 a=a+++a+++a++ 等于几一样,这些问题本来就没什么意思
      

  28.   

    不懂Delphi 帮你们顶出100楼
      

  29.   

    支持一下
    Pascal快忘光了
      

  30.   

    严格来说,这种incx()函数的写法同pascal的严格语法之间是有冲突的,偏重于这样写法的人原来都不是将pascal作为程序母语的人.
      

  31.   

    不错!
    ========================================
    http://bbs.dameng.com
    ========================================
      

  32.   

    v1.x不确定,v2.x=11。
    Inc(Result.x);相当于引用了一个没初始化的变量,其值应该不确定,以前遇到过。
    IncX(v2); 这个传进来的是地址,函数内的操作将影响到函数外的变量。
    第二种方法本人经常用到:
    procedure ReagSrvInfo(var v:TSrvInfo);
    var
      IniFile:TiniFile;
      CrFileName:string;
    begin
      CrFileName:=GetExePath+IniFileName;
      try
        IniFile:=TiniFile.Create(CrFileName);
        v.IP:=IniFile.ReadString('SqlSrvInfo','IP','');
        try
          v.DBName:=IniFile.ReadString('SqlSrvInfo','DBName','');
          if v.DBName<>'' then
            v.DBName:=Decrypt(v.DBName,MyKey);
          v.UserName:=IniFile.ReadString('SqlSrvInfo','UserName','');
          if v.UserName<>'' then
            v.UserName:=Decrypt(v.UserName,MyKey);
          v.UserPWD:=IniFile.ReadString('SqlSrvInfo','UserPWD','');
          if v.UserPWD<>'' then
            v.UserPWD:=Decrypt(v.UserPWD,MyKey);
        except
          v.UserName:='';
          v.UserPWD:='';
        end;
      finally
        FreeAndNil(IniFile);
      end;
    end;