为什么我可以在它被声明之前没有错误地获得子程序地址?
Why I can get subroutine address before it is declared without error?
我有下一个节目:
use warnings;
use strict;
BEGIN {
print \&mysub;
}
sub mysub {};
print \&mysub;
其输出:
CODE(0x118e890)CODE(0x118e890)
BEGIN
块在编译时处理。那时 sub mysub
的定义还没有被编译器看到。但是程序仍然打印正确的子程序地址,它在定义时会有。
为什么我这里没有报错?这是某种自动复活吗?
非常有趣的问题。我把它写成一个答案而不是评论,因为它会很长,但仍然有一些我不完全确定的地方。
我相信你的直觉是正确的,它是一种自动生成的形式。
Devel::Peek 可以让更多人了解正在发生的事情。
我稍微修改了你的代码:
use warnings;
use strict;
use Devel::Peek;
$|++;
BEGIN {
Dump( \&mysub );
print \&mysub;
};
sub mysub {};
Dump( \&mysub );
print \&mysub;
我添加了 $|++
以便缓冲不会造成混淆,并添加了对 Devel::Peek::Dump
的调用以查看参考 \&mysub
。这是我系统上的输出:
SV = IV(0x2628628) at 0x2628638
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x26286e0
SV = PVCV(0x2640750) at 0x26286e0
REFCNT = 2
FLAGS = (DYNFILE)
COMP_STASH = 0x25ffdb0 "main"
ROOT = 0x0
GVGV::GV = 0x26287a0 "main" :: "mysub"
FILE = "/tmp/autov.pl"
DEPTH = 0
FLAGS = 0x1000
OUTSIDE_SEQ = 0
PADLIST = 0x0
OUTSIDE = 0x0 (null)
CODE(0x26286e0)SV = IV(0x25fff20) at 0x25fff30
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x26286e0
SV = PVCV(0x2640750) at 0x26286e0
REFCNT = 2
FLAGS = (DYNFILE)
COMP_STASH = 0x25ffdb0 "main"
START = 0x262ea50 ===> 1
ROOT = 0x262ea10
GVGV::GV = 0x26287a0 "main" :: "mysub"
FILE = "/tmp/autov.pl"
DEPTH = 0
FLAGS = 0x1000
OUTSIDE_SEQ = 371
PADLIST = 0x2648620
PADNAME = 0x2630180(0x2667f70) PAD = 0x2628770(0x262f020)
OUTSIDE = 0x2600140 (MAIN)
CODE(0x26286e0)
请注意 Dump
的输出在两次调用之间如何变化。
第一次调用 Dump
时,我们只有一个空标量的引用。
second次,在真正定义子程序之后,可以看到关于子程序的细节被充实了:即PADLIST
(现在不为空),PADNAME
和 START
(我不是 Perl 专家,但我认为 this 是子例程的实际 "pointer")。
希望对您有所帮助。如果您深入研究问题,我很想知道您会发现什么。
是的,这是一种自动复活的形式。当需要对子项的引用并且子项不存在时,将创建一个存根。
use strict;
use warnings qw( all );
use feature qw( say );
sub test {
say defined(&mysub) ? "defined (".\&mysub.")"
: exists(&mysub) ? "exists (".\&mysub.")"
: "doesn't exist";
}
test();
my $ref = \&mysub;
test();
eval("sub mysub { } 1") or die($@);
test();
输出:
doesn't exist
exists (CODE(0xab8cd8))
defined (CODE(0xab8cd8))
我有下一个节目:
use warnings;
use strict;
BEGIN {
print \&mysub;
}
sub mysub {};
print \&mysub;
其输出:
CODE(0x118e890)CODE(0x118e890)
BEGIN
块在编译时处理。那时 sub mysub
的定义还没有被编译器看到。但是程序仍然打印正确的子程序地址,它在定义时会有。
为什么我这里没有报错?这是某种自动复活吗?
非常有趣的问题。我把它写成一个答案而不是评论,因为它会很长,但仍然有一些我不完全确定的地方。
我相信你的直觉是正确的,它是一种自动生成的形式。
Devel::Peek 可以让更多人了解正在发生的事情。
我稍微修改了你的代码:
use warnings;
use strict;
use Devel::Peek;
$|++;
BEGIN {
Dump( \&mysub );
print \&mysub;
};
sub mysub {};
Dump( \&mysub );
print \&mysub;
我添加了 $|++
以便缓冲不会造成混淆,并添加了对 Devel::Peek::Dump
的调用以查看参考 \&mysub
。这是我系统上的输出:
SV = IV(0x2628628) at 0x2628638
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x26286e0
SV = PVCV(0x2640750) at 0x26286e0
REFCNT = 2
FLAGS = (DYNFILE)
COMP_STASH = 0x25ffdb0 "main"
ROOT = 0x0
GVGV::GV = 0x26287a0 "main" :: "mysub"
FILE = "/tmp/autov.pl"
DEPTH = 0
FLAGS = 0x1000
OUTSIDE_SEQ = 0
PADLIST = 0x0
OUTSIDE = 0x0 (null)
CODE(0x26286e0)SV = IV(0x25fff20) at 0x25fff30
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x26286e0
SV = PVCV(0x2640750) at 0x26286e0
REFCNT = 2
FLAGS = (DYNFILE)
COMP_STASH = 0x25ffdb0 "main"
START = 0x262ea50 ===> 1
ROOT = 0x262ea10
GVGV::GV = 0x26287a0 "main" :: "mysub"
FILE = "/tmp/autov.pl"
DEPTH = 0
FLAGS = 0x1000
OUTSIDE_SEQ = 371
PADLIST = 0x2648620
PADNAME = 0x2630180(0x2667f70) PAD = 0x2628770(0x262f020)
OUTSIDE = 0x2600140 (MAIN)
CODE(0x26286e0)
请注意 Dump
的输出在两次调用之间如何变化。
第一次调用 Dump
时,我们只有一个空标量的引用。
second次,在真正定义子程序之后,可以看到关于子程序的细节被充实了:即PADLIST
(现在不为空),PADNAME
和 START
(我不是 Perl 专家,但我认为 this 是子例程的实际 "pointer")。
希望对您有所帮助。如果您深入研究问题,我很想知道您会发现什么。
是的,这是一种自动复活的形式。当需要对子项的引用并且子项不存在时,将创建一个存根。
use strict;
use warnings qw( all );
use feature qw( say );
sub test {
say defined(&mysub) ? "defined (".\&mysub.")"
: exists(&mysub) ? "exists (".\&mysub.")"
: "doesn't exist";
}
test();
my $ref = \&mysub;
test();
eval("sub mysub { } 1") or die($@);
test();
输出:
doesn't exist
exists (CODE(0xab8cd8))
defined (CODE(0xab8cd8))