In a procedural program, the code is king and the data is subordinate. In other words, you have programs which act on data and they're not usually tightly bound.
In the OO world, objects are the primary thing of interest. An object consists of data and the code that is allowed to act on that data, and they are very tightly bound. It is the concept of encapsulation, the hiding of information.
An example, let's say you have a number and you want to double it. A procedural way of doing this is:
n = n * 2
The code here quite explicitly multiplies n by 2 and stores the result back into n.
The OO way of doing this is to send a "message" to the number object telling it to double itself:
n.double();
The advantage of this is called polymorphism. What happens when you decide you want to be able to double a string like "bob". In the procedural world, you'd have to provide more code to do the doubling but you'd also have to call that code differently.
With OO, you create a string object which can also take the 'double' message. The code to double a string belongs to the string object so it knows it has to act differently to the number object. If it decided that "bob" * 2 was "bobbob", the code would look something like:
class number: class string:
int n char array s
procedure double: procedure double:
n = n * 2 s = string_join(s,s)
Then you could call x.double() no matter what actual type x was (number or string) and it would know which code to run - this greatly simplifies your code. You can double integer, strings, matrices, complex numbers, reals, window sizes on your monitor and all sorts of different things.
And you're right, a C library can be made to look a little bit like objects. The classic example is stdio.h
- you don't ever care what a FILE*
actually points to, just the fact that it will behave in a certain way. The FILE*
, fopen()
, fclose()
and other functions are an class of sorts representing the I/O capabilities of C.