因此,在最近尝试修补bug时,黑客同事告诉我,由于传递给子例程(方法)的字符串的值可能非常大,因此通过$_[1]
访问它会避免内存复制。但是,我认为传递给子程序的任何值都首先被复制到@_
?所以在下面的例子中是两次复制的内存?或者我在传递给方法时所犯的副本是错的吗?
sub foo {
my $self = shift
$_[0] # access $str in @_ directly
my ( $str ) = @_; # makes another copy of @_
}
sub bar {
my $self = shift;
my $str = 'something very large';
$self->foo( $str ); #copies $str to the @_ of foo
}
这就是为什么我向作者建议允许传递标量引用,这将在传递给方法本身时避免复制(除了引用本身)。重申:是否将值传递给子例程意味着将值复制到@_
?
答案 0 :(得分:7)
根据http://perldoc.perl.org/perlsub.html(强调是我的):
传入的任何参数都显示在数组
@_
中。因此,如果您使用两个参数调用函数,那么这些参数将存储在$_[0]
和$_[1]
中。 数组@_
是一个本地数组,但其元素是实际标量参数的别名。特别是,如果更新元素$_[0]
,则更新相应的参数(或者如果它不可更新则发生错误)。如果参数是调用函数时不存在的数组或散列元素,则仅在修改它时(或者如果)修改该元素或者对其进行引用。 (Perl的某些早期版本创建了元素,无论元素是否已分配给。)分配给整个数组@_
会删除该别名,并且不会更新任何参数。
根据我的阅读,这似乎表明,默认情况下,@_
没有复制。
虽然我承认使用的语言有点迟钝。
答案 1 :(得分:5)
是的,@_
中的元素是别名。将参数传递给子例程时不会发生复制。
这意味着你可以做一些有用但令人惊讶的事情:
sub strip {
$_[0] =~ s{^\s+}{};
$_[0] =~ s{\s+$}{};
}
my $var = " foo ";
strip($var);
print $var; # "foo"
远距离的这种行动通常是令人惊讶和危险的。用户没有迹象表明strip
将修改其参数。更安全,更明显的做法是将值作为参考传递。
sub strip {
my $ref = shift;
$$ref =~ s{^\s+}{};
$$ref =~ s{\s+$}{};
}
my $var = " foo ";
strip(\$var);
print $var; # "foo"
这既节省了内存(只复制了引用),又可以在子例程中命名参数,并且由于它们必须传递引用,因此调用者可以知道它们的变量可能会被修改。
另一种方法是使用只读别名。这为您提供了不复制变量的内存优化,允许您命名变量,但可以防止您意外更改变量。
有几种方法可以实现这一目标,但Method::Signatures方便了。
use Method::Signatures;
func no_copy($string is alias is ro) {
# $string is an alias to $var
print "$string\n";
# But it cannot be altered because $string is read-only.
# This will throw an error.
$string .= "bar";
}
my $var = "foo";
no_copy($var);
答案 2 :(得分:2)
$_[0]
不会发生$str
不会发生的复制,因为它们是同一个变量的两个不同名称。
$ perl -E'my $str; sub { say \$str == \$_[0] ?1:0 }->( $str );'
1
这是在my ( $str ) = @_;
中制作副本的作业。