-1
#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;
}

This code gave me the output as below:

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

OK, I understand that p is a wild pointer, so this should be an undefined behavior. But I've tried to run this code on some other X86_64 machines and the outputs were exactly the same, which means that I can always get the start and Segmentation fault.

I'm wondering why I can always get that start.

I used gdb to debug this code:

(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

As you can see, we find that there is such a line: callq 0x4008c8 <Car::start()>.

So, again, I know that this should be an undefined behavior but I'm thinking if there is some reason which could explain why the member function start could be invoked by a wild pointer.

Yves
  • 11,597
  • 17
  • 83
  • 180
  • The compiler doesn't actually use the value of the pointer to determine the address of the `start` function (since it's not virtual), so the call works even if the pointer has a garbage value. – 500 - Internal Server Error Oct 15 '19 at 11:00
  • 3
    Any attempt to reason about __Undefined Behaviour__ is doomed to failure. – Richard Critten Oct 15 '19 at 11:02
  • 1
    *"should be an undefined behavior"*. It *is* undefined behavior, but that doesn't mean the program *has to* crash on it. – walnut Oct 15 '19 at 11:02
  • Looks to me as a cousin of this https://stackoverflow.com/questions/26687808/c-anullptr-foo-is-legal – StoryTeller - Unslander Monica Oct 15 '19 at 11:04
  • The member function does not use `this` in any way, so it's not all that surprising that the compiler can generate code that will correctly print `start`. With that said, I completely agree that trying to reason about UB is pointless. – super Oct 15 '19 at 11:06

2 Answers2

2

OK, I understand that p is a wild pointer

That's unusual terminology. The uninitialised value is said to be indeterminate.

so this should be an undefined behavior

This is UB indeed. The behaviour of reading an indeterminate value is undefined.

I'm wondering why I can always get that start.

Because behaviour of the program is undefined.

Note that just because you've gotten "start" in your experiments so far, does not mean that you will always get it. One cannot make assumptions based on undefined behaviour.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

Short answer: "start()" is a non-virtual function.

Explanation: For non-virtual functions, C++ determines what function it calls depending on the pointer type - not on the type of the actual refereced object.

So it finds the function via the pointer type and then, as you don't use the referenced object in "start()" (you don't access any data members), your program doesn't produce a segmentation fault.

If you call a virtual member function such as "stop()", however, C++ uses dynamic dispatch to determine the function to be used. Therefore it tries to get a pointer to the virtual method table from the object and this results in the segmentation fault.

SolAeterna
  • 103
  • 4