【问题标题】:Why does encode raise "Use of uninitialized value within @_"?为什么编码会引发“在@_ 中使用未初始化的值”?
【发布时间】:2015-07-22 15:34:09
【问题描述】:

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

$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;

调整代码,如果 undef 被传递给 encode 而不是 $_[0],它不再抱怨。如果传递了临时变量中$_[0] 的副本而不是$_[0],它也不会抱怨。

我的问题是:这些版本之间的 Perl 会发生什么变化来解释新的行为? Perl 在 Encode.pm 第 147 行的 @_ 中究竟看到了什么?


附录:在test_encode的开头从Devel::Peek添加Dump($_[0]);,它输出:

Perl 5.14.2:

SV = PVLV(0x23a2c10) 在 0x2340998 REFCNT = 1 旗帜 = (GMG,SMG) IV = 0 净值 = 0 PV = 0 魔法 = 0x235f950 MG_VIRTUAL = &PL_vtbl_substr MG_TYPE = PERL_MAGIC_substr(x) 类型 = x 关门 = 0 塔格伦 = 0 TARG = 0x235e370 SV = PV(0x233ec20) 在 0x235e370 REFCNT = 2 标志 = (PADMY,POK,pPOK) PV = 0x23576b0 ""\0 CUR = 0 LEN = 16

Perl 5.18.2:

SV = PVLV(0x25c07c0) 在 0x2546cb8 REFCNT = 1 旗帜 = (GMG,SMG) IV = 0 净值 = 0 PV = 0 魔法 = 0x2567dd0 MG_VIRTUAL = &PL_vtbl_substr MG_TYPE = PERL_MAGIC_substr(x) 类型 = x 关门 = 0 塔格伦 = 1 TARG = 0x256f328 标志 = 0 SV = NULL(0x0) 在 0x256f328 REFCNT = 2 旗帜 = (PADMY)

不知道该怎么想,但末尾的 SV 部分有很大不同,看起来像空字符串与 NULL(0x0)。

【问题讨论】:

  • @toolic: 但 5.14 (wheezy) 中的 Encode.pm 中同样包含 use warnings;

标签: perl


【解决方案1】:

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.

Since 5.16.0,警告现在在实际执行子字符串操作时发生,而不是在调用 substr 时发生。当substr用作左值时,实际的子串操作是在取值或存储在返回的标量中时执行的。

$ 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.

然后完成子字符串操作以允许以下工作:

$ perl -wE'$_ = "abc"; substr($_, 0, 1) = "!!!"; say'
!!!bc

由于现在在子字符串操作完成时会发生警告,因此 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.

为什么警告开始发生在子字符串操作实际执行的地方,而不是在调用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>

scalar 前缀substr 将其称为右值,尽管没有记录。

$ 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

你也可以强制字符串化。

$ perl -MO=Concise,-exec -e'1 for     "".substr($_, 0, 1)' 2>&1 | grep substr
8  <@> substr[t2] sK/3

【讨论】:

  • 所以考虑到 substr 的延迟评估,有没有一种简单的方法可以使警告静音? (首先要避免substr(undef,...))。
  • 不使用use warnings; 将使警告静音...但这不是您真正要问的。阅读我刚刚添加到底部的部分。
  • 琐事:返回可分配值的四个运算符:substrkeysvec$#array
【解决方案2】:

有趣。如果你做几乎相同的事情但是:

my $a=undef;
my $b = substr($a,0,1);
my $r=test_encode($b);

它工作正常。

或者:

my $r=test_encode(scalar substr($a,0,1));

所以我想我不得不说 - 这与来自 substr 的返回值和上下文有关。

例如@_[0] 不是未定义的 - @_ 是未定义的。

Encode 模块有:

#
# $Id: Encode.pm,v 2.75 2015/06/30 09:57:15 dankogai Exp $
#
package Encode;
use strict;
use warnings;

这将覆盖您的 no warnings 指令 - 但隐藏这样的警告无论如何都不是真正可取的。不过,这已经有一段时间了:

2.18 2006/06/03 20:28:48
! bin/enc2xs
  overhauled the -C option
  - added ascii-ctrl', 'null', 'utf-8-strict' to core
  - auto-generated Encode::ConfigLocal no longer use v-string for version
  - now searches modules via File::Find so Encode/JP/Mobile is happy
! Byte/Byte.pm CN/CN.pm EBCDIC/EBCDIC.pm JP/JP.pm KR/KR.pm Symbol/Symbol.pm
  use strict added; though all they do is load XS, it's
  still better a practice
! *.pm
  use warnings added to all of them for better practices' sake.

因此,我建议您在运行时使用旧版本的 Encode。

【讨论】:

  • 旧的 Encode.pm 是 $Id: Encode.pm,v 2.42 2010/12/31 22:48:10 dankogai Exp
  • 嗯,我会再深入一点。我怀疑我们会在某处的perldelta 中找到它。
  • Re "@_[0] is not undefined - @_ is undefined",这是没有意义的;数组不能未定义,只能为空。 LvTARG( $_[0] ) 未定义。
猜你喜欢
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-20
  • 2014-08-23
  • 1970-01-01
相关资源
最近更新 更多