为什么编码会引发“在@_ 中使用未初始化的值”?

Dan*_*ité 4 perl

使用 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)。

ike*_*ami 5

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)