As so many people said there are so many things wrong with this code but the fundamental error is a pointer math issue.
When the array b is passed to f(...) the array degrades to a pointer to the head of the array b.
It does not pass an array of B objects.  It only points to the head of the array.

(on my system with a 64-bit compiler)
class B has a size of 32 bytes.
so the full array b is size 64 (two elements of B)
class I has a size of 8 bytes (the size of the v-table).

Inside the function void f(I*) it has no idea that it is pointing at a B structure. 
It thinks it is pointing at an I object (or as we are interpreting it a block of I objects)
When you do i++ you add an increment size of I (8 bytes) to i,  (NOT the 32 bytes of the size of B)
so it is now not pointing at anything meaningful. (middle of a B object)

In the next iteration of the loop it tries to call a virtual function A::f() (that would virtual redirect to B::f() ) on the pointer i.  
This attempts to look up the v-table to look up the function pointer for the A::f() function.  
But the pointer i is not pointing at an instance of a class with a v-table,
so ka-boom.

It did not crash on the call to  i->print(); because the i value aka the 'this' in the member function was never validated, 
it was just printed a value of the "this" (aka the i it was called with) that is not meaningful anything meaningful.
(Undefined Behaviour, calling a member function with an invalid this pointer)

Had you tried to dereference the i using array subscripts,  i[j]->f();  
you would get the same error because array subscripts also use the same increment size of I (8 bytes.)

To visualize this draw an array of 64 bytes. (two blocks of 32, one for each B)
Have "b" point at the beginning of the array.
When you call f(b) in main, the function void f(I* i) just uses the same value of b (cast to a type of an I) as i.
The first time "i" is pointing at the head of a B object and able to access the v-table.
Now in your drawing move the i pointer forward 8 bytes (aka i++) and tell me where your pointer is pointing,
It is in the middle of an instance of a B object, not the next instance of an I object. (Boom)






On Thu, Jan 19, 2023 at 10:56 PM Julien Allali via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
Hi everyone,

I checked the following code from one of my student:

class I{
public:
   virtual ~I(){}
   virtual void print(){
     printf("hello %p\n",this);
   }
   virtual void f()=0;
};

class B : public virtual I{
   int i,j;
public:
   ~B(){}
   virtual void f(){
     printf("B %p\n",this);
   }
};


void f(I *i){
   for(int j=0;j<5;++j)
     {
       i->print();
       i->f();
       printf("i=%p ",i);
       i++; // PROBLEM IS HERE
       printf(" => %p \n",i);
     }
}

int main(){
   B b[]={B(),B()};
   printf("B: %p %p\n",b,b+1);
   f(b);
}

As you can see, I is a non instantiable type as it has one pure virtual
method. My concern is about the line "i++": I believed it will lead to
an error or at least a warning (g++ 11.3.0)... Indeed, I can not imagine
a valid case where doing arithmetic on a pointer of a type with pure
virtual function can be valid.... Shouldn't the standard forbid such
arithmetic?

Julien.
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals