Почему я могу вызвать функцию-член через дикий указатель - PullRequest
0 голосов
/ 15 октября 2019
#include <iostream>
using namespace std;

class Car{
public:
    void start() {
        std::cout << "start" << std::endl;
    };
    virtual void stop() {
        std::cout << "stop" << std::endl;
    };
};

int main() {
    Car* p;// = new Car;
    p->start();
    p->stop();
    return 0;
}

Этот код дал мне вывод, как показано ниже:

me@Ubuntu:~/tmp$ ./a.out
start
Segmentation fault (core dumped)

ОК, я понимаю, что p - это дикий указатель, поэтому это должно быть неопределенное поведение. Но я попытался запустить этот код на некоторых других машинах X86_64, и выходные данные были точно такими же, что означает, что я всегда могу получить start и Segmentation fault.

Мне интересно, почему явсегда можно получить это start.

Я использовал gdb для отладки этого кода:

(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

Как видите, мы находим, что есть такая строка: callq 0x4008c8 <Car::start()>.

Итак, опять же, я знаю, что это должно быть неопределенное поведение, но я думаю, если есть какая-то причина, которая могла бы объяснить, почему функция-член start может вызываться диким указателем.

Ответы [ 2 ]

2 голосов
/ 15 октября 2019

Краткий ответ: "start ()" - это не виртуальная функция.

Объяснение: Для невиртуальных функций C ++ определяет, какую функцию он вызывает, в зависимости от типа указателя - не для типа фактического ссылочного объекта.

Таким образом, он находит функцию через тип указателя и затем, поскольку вы не используете ссылочный объект в "start ()" (вы не обращаетесь к каким-либо элементам данных), ваша программа не выдает ошибку сегментации.

Однако, если вы вызываете функцию виртуального члена, такую ​​как "stop ()", C ++ использует динамическую диспетчеризацию для определения функции, которая будет использоваться. Поэтому он пытается получить указатель на таблицу виртуальных методов от объекта, что приводит к ошибке сегментации.

2 голосов
/ 15 октября 2019

ОК, я понимаю, что p - это дикий указатель

Это необычная терминология. Неинициализированное значение называется неопределенным.

, поэтому это должно быть неопределенное поведение

Это действительно UB. Поведение чтения неопределенного значения не определено.

Мне интересно, почему я всегда могу получить это начало.

Поскольку поведение программы не определено.

Обратите внимание, что только то, что вы уже получили «начало» в своих экспериментах, не означает, что вы всегда получите его. Нельзя делать предположения на основе неопределенного поведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...