我试图了解分配子程序的结果是否会导致复制该数据。
sub maketext { 'text' };
my $foo = maketext();
my $foo_ref = \$foo;
my $bar_ref = \maketext();
在上面的示例中,$foo_ref
的创建是否会比创建$bar_ref
产生一个副本?
我怎样才能使自己相信它们的等同性或不等同性?
答案 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';
}
引发错误
尝试修改只读值