An object is polymorphic when its specific type may vary.
The types a specific value may take, is called class.

It is trivial to copy an object if its type is known:

int x;
int y = x;
Here x is not polymorphic, so y is declared of same type (int) as x.
But if the specific type of x were unknown, then y could not be declared of any specific type.

The task: let a polymorphic object contain an instance of some specific type S derived from a type T.
The type T is known.
The type S is possibly unknown until run time.
The objective is to create an exact copy of such polymorphic object (not to create a reference, nor a pointer to).
Let further the type T have a method overridden by S.
This method is to be called on the copy to demonstrate that the specific type of the copy is indeed S.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef struct object *BaseObj;
typedef struct sclass *Class;
typedef void (*CloneFctn)(BaseObj s, BaseObj clo);
typedef const char * (*SpeakFctn)(BaseObj s);
typedef void (*DestroyFctn)(BaseObj s);
 
typedef struct sclass {
    size_t csize;		/* size of the class instance */
    const char  *cname;		/* name of the class */
    Class  parent;		/* parent class */
 
    CloneFctn clone;		/* clone function */
    SpeakFctn speak;		/* speak function */
    DestroyFctn del;		/* delete the object */
} sClass;
 
typedef struct object {
    Class class;
} SObject;
 
static 
BaseObj obj_copy( BaseObj s, Class c )
{
    BaseObj clo;
    if (c->parent) 
        clo = obj_copy( s, c->parent);
    else 
        clo = malloc( s->class->csize );
 
    if (clo)
        c->clone( s, clo );
    return clo;
}
 
static
void obj_del( BaseObj s, Class c )
{
    if (c->del)
        c->del(s);
    if (c->parent)
        obj_del( s, c->parent);
    else
        free(s);
}
 
BaseObj ObjClone( BaseObj s )
{ return obj_copy( s, s->class ); }
 
const char * ObjSpeak( BaseObj s )
{ 
    return s->class->speak(s); 
}
 
void ObjDestroy( BaseObj s )
{ if (s) obj_del( s, s->class ); }
 
/* * * * * * */
static
void baseClone( BaseObj s, BaseObj clone)
{  
    clone->class = s->class;
}
 
static
const char *baseSpeak(BaseObj s)
{
    return "Hello, I'm base object";
}
 
sClass boc = { sizeof(SObject), "BaseObj", NULL, 
    &baseClone, &baseSpeak, NULL };
Class BaseObjClass = &boc;
 
/* * * * * * */
/* Dog - a derived class */
 
typedef struct sDogPart {
    double weight;
    char color[32];
    char name[24];
} DogPart;
 
typedef struct sDog *Dog;
 
struct sDog {
    Class   class;		// parent structure
    DogPart dog;
};
 
static
void dogClone( BaseObj s, BaseObj c)
{  
    Dog src = (Dog)s;
    Dog clone = (Dog)c;
    clone->dog = src->dog;	/* no pointers so strncpys not needed */
}
 
static
const char *dogSpeak( BaseObj s)
{
    Dog d = (Dog)s;
    static char  response[90];
    sprintf(response, "woof! woof! My name is %s. I'm a %s %s", 
            d->dog.name, d->dog.color, d->class->cname);
    return response;
}
 
 
sClass dogc = { sizeof(struct sDog), "Dog", &boc,
    &dogClone, &dogSpeak, NULL };
Class DogClass = &dogc;
 
BaseObj NewDog( const char *name, const char *color, double weight )
{
    Dog dog = malloc(DogClass->csize);
    if (dog) {
        DogPart *dogp = &dog->dog;
        dog->class = DogClass;
        dogp->weight = weight;
        strncpy(dogp->name, name, 23);
        strncpy(dogp->color, color, 31);
    }
    return (BaseObj)dog;
}
 
/* * * * * * * * * */
/* Ferret - a derived class */
 
typedef struct sFerretPart {
    char color[32];
    char name[24];
    int  age;
} FerretPart;
 
typedef struct sFerret *Ferret;
 
struct sFerret {
    Class   class;		// parent structure
    FerretPart ferret;
};
 
static
void ferretClone( BaseObj s, BaseObj c)
{  
    Ferret src = (Ferret)s;
    Ferret clone = (Ferret)c;
    clone->ferret = src->ferret;  /* no pointers so strncpys not needed */
}
 
static
const char *ferretSpeak(BaseObj s)
{
    Ferret f = (Ferret)s;
    static char  response[90];
    sprintf(response, "My name is %s. I'm a %d mo. old %s wiley %s", 
            f->ferret.name, f->ferret.age, f->ferret.color,
            f->class->cname);
    return response;
}
 
sClass ferretc = { sizeof(struct sFerret), "Ferret", &boc, 
    &ferretClone, &ferretSpeak, NULL };
Class FerretClass = &ferretc;
 
BaseObj NewFerret( const char *name, const char *color, int age )
{
    Ferret ferret = malloc(FerretClass->csize);
    if (ferret) {
        FerretPart *ferretp = &(ferret->ferret);
        ferret->class = FerretClass;
        strncpy(ferretp->name, name, 23);
        strncpy(ferretp->color, color, 31);
        ferretp->age = age;
    }
    return (BaseObj)ferret;
}
 
/* * Now you really understand why Bjarne created C++ * */
 
int main()
{
    BaseObj  o1;
    BaseObj  kara = NewFerret( "Kara", "grey", 15 );
    BaseObj  bruce = NewDog("Bruce", "yellow", 85.0 );
    printf("Ok created things\n");
 
    o1 = ObjClone(kara );
    printf("Karol says %s\n", ObjSpeak(o1));
    printf("Kara says %s\n", ObjSpeak(kara));
    ObjDestroy(o1);
 
    o1 = ObjClone(bruce );
    strncpy(((Dog)o1)->dog.name, "Donald", 23);
    printf("Don says %s\n", ObjSpeak(o1));
    printf("Bruce says %s\n", ObjSpeak(bruce));
    ObjDestroy(o1);
    return 0;
}