使用 Perl v5.14.2(由 Debian Wheezy 提供)此代码:
use Encode qw(encode);
no warnings "all";
sub test_encode {
return Encode::encode("utf8", $_[0]);
}
my $a=undef;
my $r=test_encode(substr($a,0,1));
Run Code Online (Sandbox Code Playgroud)
在 中产生一个空字符串$r。我没问题。
使用 Perl 5.18.2 (Ubuntu 14.04),它似乎产生以下输出:
在 /usr/lib/perl/5.18/Encode.pm 第 147 行的列表赋值中使用 @_ 内未初始化的值。
(即使在主范围中禁用了警告,显然这不是警告。编辑:根据答案,这绝对是警告):
该列表分配将是Encode.pm:
146 sub encode($$;$) {
147 my ( $name, $string, $check ) = @_;
148 return undef unless defined $string;
149 $string .= ''; # stringify;
Run Code Online (Sandbox Code Playgroud)
调整代码,如果undef传递给encode而不是$_[0],它不再抱怨。如果$_[0]在临时变量中传递了一个副本而不是$_[0],它也不会抱怨。
我的问题是:这些版本之间的 Perl 会发生什么变化来解释新行为?Perl@_在 Encode.pm 第 147 行中到底看到了什么?
附录:Dump($_[0]);从Devel::Peek开始添加test_encode,它输出:
Perl 5.14.2:
SV = PVLV(0x23a2c10) 在 0x2340998
REFCNT = 1
旗帜 = (GMG,SMG)
IV = 0
NV = 0
PV = 0
魔法 = 0x235f950
MG_VIRTUAL = &PL_vtbl_substr
MG_TYPE = PERL_MAGIC_substr(x)
类型 = x
塔尔戈夫 = 0
塔格伦 = 0
目标 = 0x235e370
SV = PV(0x233ec20) 在 0x235e370
REFCNT = 2
标志 = (PADMY,POK,pPOK)
PV = 0x23576b0 ""\0
电流 = 0
伦 = 16
Perl 5.18.2:
SV = PVLV(0x25c07c0) 在 0x2546cb8
REFCNT = 1
旗帜 = (GMG,SMG)
IV = 0
NV = 0
PV = 0
魔法 = 0x2567dd0
MG_VIRTUAL = &PL_vtbl_substr
MG_TYPE = PERL_MAGIC_substr(x)
类型 = x
塔尔戈夫 = 0
塔格伦 = 1
目标 = 0x256f328
标志 = 0
SV = NULL(0x0) 在 0x256f328
REFCNT = 2
旗帜 = (PADMY)
不知道该怎么想,但最后的SV部分明显不同,看起来像空字符串与 NULL(0x0)。
这substr就是警告。
substr 当它的第一个参数未定义时发出警告。
$ perl -we'
my $x;
my $y = substr($x, 0, 1); # Line 3
'
Use of uninitialized value $x in substr at -e line 3.
Run Code Online (Sandbox Code Playgroud)
从 5.16.0 开始,警告现在发生在实际执行子字符串操作而不是substr调用时。当substr用作左值时,当 a 值被提取或存储在返回的标量中时,将执行实际的子字符串操作。
$ perl -we'
my $x;
my $r = \substr($x, 0, 1);
my $y = $$r; # Line 4
'
Use of uninitialized value in scalar assignment at -e line 4.
Run Code Online (Sandbox Code Playgroud)
然后完成子字符串操作以允许以下工作:
$ perl -wE'$_ = "abc"; substr($_, 0, 1) = "!!!"; say'
!!!bc
Run Code Online (Sandbox Code Playgroud)
由于警告现在在子字符串操作完成时发生,因此 Encode 中操作的上下文决定了警告是否可见。
$ 5.14.2t/bin/perl -e'use warnings; my $r = \substr(my $x, 0, 1); no warnings; my $y = $$r;'
Use of uninitialized value in scalar assignment at -e line 1.
$ 5.14.2t/bin/perl -e'no warnings; my $r = \substr(my $x, 0, 1); use warnings; my $y = $$r;'
$ 5.22.0t/bin/perl -e'use warnings; my $r = \substr(my $x, 0, 1); no warnings; my $y = $$r;'
$ 5.22.0t/bin/perl -e'no warnings; my $r = \substr(my $x, 0, 1); use warnings; my $y = $$r;'
Use of uninitialized value in scalar assignment at -e line 1.
Run Code Online (Sandbox Code Playgroud)
为什么警告开始发生在子字符串操作实际执行的地方而不是何时substr被调用?我猜,但它可能是为了解决以下和类似的问题:
$ perl -wE'
my $x = "def";
my $r = \substr($x, 0, 1);
$x = "abc";
say "<$$r>";
'
<a>
$ 5.14.2t/bin/perl -wE'
my $x;
my $r = \substr($x, 0, 1);
$x = "abc";
say "<$$r>";
'
Use of uninitialized value $x in substr at -e line 4.
<>
$ 5.22.0t/bin/perl -wE'
my $x;
my $r = \substr($x, 0, 1);
$x = "abc";
say "<$$r>";
'
<a>
Run Code Online (Sandbox Code Playgroud)
前缀substrwithscalar将其称为右值,尽管没有记录。
$ perl -MO=Concise,-exec -e'1 for substr($_, 0, 1)' 2>&1 | grep substr
7 <@> substr[t4] sKM/3
^
This flag causes the special lvalue behaviour.
$ perl -MO=Concise,-exec -e'1 for scalar substr($_, 0, 1)' 2>&1 | grep substr
7 <@> substr[t2] sK/3
Run Code Online (Sandbox Code Playgroud)
您还可以强制字符串化。
$ perl -MO=Concise,-exec -e'1 for "".substr($_, 0, 1)' 2>&1 | grep substr
8 <@> substr[t2] sK/3
Run Code Online (Sandbox Code Playgroud)