C++ 为什么我可以通过通配符指针调用成员函数

C++ 为什么我可以通过通配符指针调用成员函数,c++,pointers,gdb,member,C++,Pointers,Gdb,Member,好的,我知道p是一个野生指针,所以这应该是一个未定义的行为。但我曾尝试在其他一些X86_64机器上运行此代码,结果完全相同,这意味着我始终可以获得开始和分段错误 我想知道为什么我总是能得到start 我使用gdb调试此代码: me@Ubuntu:~/tmp$ ./a.out start Segmentation fault (core dumped) (gdb)b main 断点1位于0x40084e:文件main.cpp,第16行。 (gdb)r 启动程序:/home/zyh/tmp/a.o

好的,我知道
p
是一个野生指针,所以这应该是一个未定义的行为。但我曾尝试在其他一些X86_64机器上运行此代码,结果完全相同,这意味着我始终可以获得
开始
分段错误

我想知道为什么我总是能得到
start

我使用
gdb
调试此代码:

me@Ubuntu:~/tmp$ ./a.out
start
Segmentation fault (core dumped)
(gdb)b main
断点1位于0x40084e:文件main.cpp,第16行。
(gdb)r
启动程序:/home/zyh/tmp/a.out
断点1,main()位于main.cpp:16
16 p->start();
(gdb)显示器/5i$pc
1:x/5i$pc
=>0x40084e:mov-0x8(%rbp),%rax
0x400852:mov%rax,%rdi
0x400855:callq 0x4008c8
0x40085a:mov-0x8(%rbp),%rax
0x40085e:mov(%rax),%rax
如您所见,我们发现有这样一行:
callq 0x4008c8

同样,我知道这应该是一个未定义的行为,但我在想是否有某种原因可以解释为什么成员函数
start
可以被一个野生指针调用

好的,我知道p是一个野生指针

这是不寻常的术语。未初始化的值称为不确定值

所以这应该是一个未定义的行为

这是真的。读取不确定值的行为是未定义的

我想知道为什么我总是能开始

因为程序的行为是未定义的

请注意,仅仅因为到目前为止你已经在实验中取得了“开始”,并不意味着你将永远得到它。不能根据未定义的行为做出假设。

简短回答: “start()”是一个非虚拟函数。

说明: 对于非虚函数,C++根据指针决定它调用的函数。 type-不是实际引用对象的类型

因此,它通过指针类型找到函数,然后,由于您不使用“start()”中的引用对象(您不访问任何数据成员),因此您的程序不会产生分段错误

如果调用一个虚拟成员函数,如“StUTE()”,C++使用来确定要使用的函数。


因此,它试图从对象中获取指向的指针,这将导致分段错误。

编译器实际上不使用指针的值来确定
start
函数的地址(因为它不是虚拟的),因此,即使指针有垃圾值,调用也会工作。任何试图对未定义行为进行推理的尝试都注定会失败。“应该是未定义的行为”。这是未定义的行为,但这并不意味着程序必须在其上崩溃。在我看来,成员函数不以任何方式使用
this
,因此编译器可以生成正确打印
start
的代码也就不足为奇了。尽管如此,我完全同意试图对UB进行推理是毫无意义的。
me@Ubuntu:~/tmp$ ./a.out
start
Segmentation fault (core dumped)
(gdb) b main
Breakpoint 1 at 0x40084e: file main.cpp, line 16.
(gdb) r
Starting program: /home/zyh/tmp/a.out
Breakpoint 1, main () at main.cpp:16
16          p->start();
(gdb) display /5i $pc
1: x/5i $pc
=> 0x40084e <main()+8>: mov    -0x8(%rbp),%rax
   0x400852 <main()+12>:        mov    %rax,%rdi
   0x400855 <main()+15>:        callq  0x4008c8 <Car::start()>
   0x40085a <main()+20>:        mov    -0x8(%rbp),%rax
   0x40085e <main()+24>:        mov    (%rax),%rax