0
votes

So I messing around with virtual functions, trying to find a way to mitigate their cost, and I encountered an entirely unknown error. My entire code follows;

#include <iostream>
    #include <cmath>

    class Base
    {
    public:
        virtual void func() = 0;
    };

    class Der1 : public Base
    {
    public:
        virtual void func();
    };
    void Der1::func(){std::cout << "I am Der1\n";}

    class Der2 : public Base
    {
    public:
        virtual void func();
    };
    void Der2::func(){std::cout << "I am Der2\n";}

    void myFornction(Base* B){
        std::cout << "Within Fornction " << B << '\n';
        for (int i = 0; i < 10; i++)
            B->func();
    }

    using namespace std;

    int main()
    {
        Der2* B;
        std::cout << "Actual Address " << B << '\n';
        myFornction(B);
        return 0;
    }

this was compiled with the GCC compiler that was included with codeblocks (unfortunately the version is unlisted), and here is the assembly of the function at fault;

24  void myFornction(Base* B){
0x00401373  push   %ebp
0x00401374  mov    %esp,%ebp
0x00401376  sub    $0x28,%esp
25      std::cout << "Within Fornction " << B << '\n';
0x00401379  movl   $0x46e03a,0x4(%esp)
0x00401381  movl   $0x477860,(%esp)
0x00401388  call   0x468e68 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x0040138D  mov    0x8(%ebp),%edx
0x00401390  mov    %edx,(%esp)
0x00401393  mov    %eax,%ecx
0x00401395  call   0x449524 <std::ostream::operator<<(void const*)>
0x0040139A  sub    $0x4,%esp
0x0040139D  movl   $0xa,0x4(%esp)
0x004013A5  mov    %eax,(%esp)
0x004013A8  call   0x468f44 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char)>
26      for (int i = 0; i < 10; i++)
0x004013AD  movl   $0x0,-0xc(%ebp)
0x004013B4  jmp    0x4013c5 <myFornction(Base*)+82>
0x004013C2  incl   -0xc(%ebp)
0x004013C5  cmpl   $0x9,-0xc(%ebp)
0x004013C9  setle  %al
0x004013CC  test   %al,%al
0x004013CE  jne    0x4013b6 <myFornction(Base*)+67>
27          B->func();
0x004013B6  mov    0x8(%ebp),%eax
0x004013B9  mov    (%eax),%eax
0x004013BB  mov    (%eax),%eax /// The program fails with a segmentation fault here
0x004013BD  mov    0x8(%ebp),%ecx
0x004013C0  call   *%eax
28  }
0x004013D0  leave
0x004013D1  ret

The program exits with a segmentation fault when run, looking at the assembly I can tell that the seg fault happens during look up, but I cannot think of a reason or resolution for this. Also, it is kind of random that this happens now, as I have been working with some of my own virtual classes for sometime now with no problems. Why now? And why would this specific code generate a fault?

3
Really? what is B pointing to?John3136
if you don't know why this crashes, you shouldn't be thinking about mucking with the vftable...thang
I didn't even notice the initialized pointeruser4578093
@user4578093 don't edit the answer into the question, just accept the answer and that is sufficient on SO :)M.M

3 Answers

1
votes

You should initialize B in main() before passing it, because it doesn't point anywhere.

Der2* B = new Der2();
0
votes

Der2* B; is a pointer that doesn't point anywhere. You dereference it which causes undefined behaviour.

Perhaps you meant Der2 *B = new Der2; or something.

0
votes

You declare Der2* B, but don't assign it anything, so it has no valid value. By default, GCC doesn't produce a warning, but you should generally compile with -Wall. If you do, you will see

tmp.cpp: In function ‘int main()’:
tmp.cpp:35:43: warning: ‘B’ is used uninitialized in this function [-Wuninitialized]
         std::cout << "Actual Address " << B << '\n';
                                           ^

Using an uninitialized value is undefined behaviour, and for pointers it generally does cause segfaults, like what you're experiencing. Specifically, the line, B->func();. It requires B be dereferenced to get the vptr and find what function to call. On my machine, B gets implicitly initialized to zero, so even dereferencing it signals the segfault.

Solution: Either make B a local variable and change the signature of myFornction to

void myFornction(Base& B);

or declare B like this:

Base* B = new Der2();

By the way, Base should have a virtual destructor.

class Base
{
public:
  virtual void func() = 0;
  virtual ~Base() { }
};

This will ensure that if you define a data member in Der2, when you call delete B, Der2's destructor will also be invoked. Otherwise, only Base's will be.