Basically Object Oriented really boils down to "message passing"
In a procedural language, I call a function like this :
f(x)
And the name f is probably bound to a particular block of code at compile time. (Unless this is a procedural language with higher order functions or pointers to functions, but lets ignore that possibility for a second.) So this line of code can only mean one unambiguous thing.
In an object oriented language I pass a message to an object, perhaps like this :
o.m(x)
In this case. m is not the name of a block of code, but a "method selector" and which block of code gets called actually depends on the object o in some way. This line of code is more ambiguous or general because it can mean different things in different situations, depending on o.
In the majority of OO languages, the object o has a "class", and the class determines which block of code is called. In a couple of OO languages (most famously, Javascript) o doesn't have a class, but has methods directly attached to it at runtime, or has inherited them from a prototype.
My demarcation is that neither classes nor inheritance are necessary for a language to be OO. But this polymorphic handling of messages is essential.
Although you can fake this with function pointers in say C, that's not sufficient for C to be called an OO language, because you're going to have to implement your own infrastructure. You can do that, and a OO style is possible, but the language hasn't given it to you.