Challenge 3: A Second Shape, and What an Unset Function Pointer Does — Possible Solution ==================================================================== #include typedef struct { float radius; float (*area)(void *self); } Circle; typedef struct { float width; float height; float (*area)(void *self); } Rectangle; float circle_area(void *self) { Circle *c = (Circle *)self; return 3.14159f * c->radius * c->radius; } float rectangle_area(void *self) { Rectangle *r = (Rectangle *)self; return r->width * r->height; } int main() { Circle c = {5.0f, circle_area}; Rectangle r = {4.0f, 6.0f, rectangle_area}; printf("circle area: %f\n", c.area(&c)); printf("rectangle area: %f\n", r.area(&r)); return 0; } Output: circle area: 78.539749 rectangle area: 24.000000 Both shapes share the same CALLING PATTERN (a struct field named area, called as shape.area(&shape)), even though Circle and Rectangle are otherwise unrelated struct types with different fields -- exactly the pattern the chapter's own Circle example established, now applied to a second, independently-defined shape. What would happen if a third shape's function pointer were left unset (effectively NULL, e.g. a struct field never explicitly initialized, or explicitly set to NULL) and its area were called: per the chapter's own warn-box, calling through a NULL function pointer is undefined behavior. In practice, this typically manifests as an immediate crash (a segmentation fault), since the program attempts to jump execution to memory address 0, which is essentially never a valid, executable location -- but per the standard, this is not GUARANTEED to crash cleanly; it's undefined, meaning no specific behavior is promised at all. Critically, nothing in the C language itself would have caught this mistake before runtime -- unlike Rust's dyn Trait, where the compiler simply would not allow a trait object to exist with a missing method implementation in the first place. WHY THIS WORKS AS AN ANSWER ------------------------------ This builds a genuinely independent second shape type (not just a copy of Circle with a new name) sharing the calling convention rather than any shared struct layout, and correctly identifies calling through a NULL function pointer as undefined behavior specifically -- not simply "a crash" -- while noting the typical real-world symptom (a segfault) without overstating it as guaranteed.