是否将子程序结果分配给数据副本?

时间:2016-12-16 07:36:14

标签: perl reference pass-by-value

我试图了解分配子程序的结果是否会导致复制该数据。

sub maketext { 'text' };

my $foo = maketext();
my $foo_ref = \$foo;

my $bar_ref = \maketext();

在上面的示例中,$foo_ref的创建是否会比创建$bar_ref产生一个副本?

我怎样才能使自己相信它们的等同性或不等同性?

3 个答案:

答案 0 :(得分:4)

数据副本似乎发生

sub maketext {
    my $text = 'text';
    say \$text;
    return $text;
}

my $bar_ref = \maketext();
say $bar_ref;

打印

SCALAR(0x11f5818)
SCALAR(0x11cfa68)

在sub中创建的数据地址和$bar_ref指向的数据的地址不相同。

从表面上看,当函数返回时,必须复制数据,并对其进行引用。

另一种可能性是,即使原始数据超出范围,也会保留对原始数据的引用,就像在闭包中一样。但是,此处函数首先返回 ,然后操作其返回值。所以我不知道任何机制如何知道数据是做什么的,因此数据被适当地复制。

您正在创建一个匿名标量引用,但不在函数返回中。

在没有相应变量的情况下创建对标量的引用的方法是

my $scalar_ref = \do { my $var };

或使用do { \my $var },或者使用子

sub anon_scalar_ref {
    # ...
    return \my $var;
}

但是,我不知道你对此有何用处。也许你想做

sub maketext {
    # ... define $text ...
    return \$text;
}

当您将此返回值分配给变量时,不会生成额外的数据副本,因为它是返回的引用。

答案 1 :(得分:3)

是的,它会复制。

use Devel::Peek qw( Dump );

sub maketext {
    my $text = 'text';
    Dump($text);
    return $text;
}

my $ref = \maketext();
Dump($$ref);

输出:

SV = PV(0x8b18a0) at 0x8dbe38   <-- $text is at 0x8dbe38
  REFCNT = 1
  FLAGS = (POK,IsCOW,pPOK)
  PV = 0x8d9f70 "text"\0        <-- String buffer at 0x8d9f70
  CUR = 4
  LEN = 10
  COW_REFCNT = 1
SV = PV(0x8b1920) at 0x8b0cc8   <-- $$ref is at 0x8b0cc8
  REFCNT = 1
  FLAGS = (POK,IsCOW,pPOK)
  PV = 0x8d9f70 "text"\0        <-- String buffer at 0x8d9f70
  CUR = 4
  LEN = 10
  COW_REFCNT = 1

但是,由于写时复制(COW)功能,不会复制字符串缓冲区。事实上,出于同样的原因你甚至没有复制my $text = 'text';。这意味着常量$text$$ref都共享相同的字符串缓冲区(直到它们的一个字符串缓冲区被编辑),即使它们是完全不同的标量。

您可以通过使用左值子

来避免复制返回的值
use Devel::Peek qw( Dump );

sub maketext :lvalue {
    my $text = 'text';
    Dump($text);
    return $text;
}

my $ref = \maketext();
Dump($$ref);

输出:

SV = PV(0xe43c80) at 0xe6e238
[...]
SV = PV(0xe43c80) at 0xe6e238
[...]

答案 2 :(得分:1)

Perl 必须复制数据。否则,对变量$foo的任何后续修改都将尝试修改字符串常量'text',这会导致代码死亡。

这就是

中发生的事情
for ( 'text' ) {
    $_ = 'test';
}

引发错误

  

尝试修改只读值