I find it a bit dubious to claim you're doing OOP in C, considering that C does not have objects. And I find it twice as dubious because you can translate any FP/OOP code to procedural C; at this rate, you can start claiming that your x86 binary is OOP.
I often hear that "FILE is an object, it's exactly as if C++ had class FILE {...}, the class is just syntactic sugar, the result of the compilation is identical".
This is false, as it's not "just syntactic sugar". A C++ class binds a function to a specific struct, and the function can "belong" to no other struct. When doing OOP you have to choose - function F belongs to either object A or to object B. It can't belong to both. Which is why OOP has endlessly silly discussions about whether class Car should Turn() the class Wheel, or should the class Wheel Turn() class Car. Or should we refactor it into class SteeringWheel that will Turn() both Car and Wheel.
This is not a thing in C. Linus Torvalds can add a function F that operates on the internals of both FILE and DIR at the same time, for example, and suddenly it's less clear who "owns" F and which one is the object.
Which is why, personally, I don't think doing dynamic dispatch with void* in C can be qualified as OOP, it's just a pattern that's been there ever since the language was introduced. It's only after you add specific OOP features to the language that it devolves into an OOP language.