112

It seems that C has its own quasi-objects such as 'structs' that can be considered as objects (in the high-level way that we would normally think).

And also, C files themselves are basically separate "modules", right? Then aren't modules kind of like 'objects' too? I'm confused as to why C, which seems so similar to C++, is considered a low-level "procedural" language where as C++ is high-level "object-oriented"

*edit: (clarification) why and where, is the line drawn, for what an 'object' is, and isn't?

6
  • 47
    All - why the down votes? It's a basic question but not a bad one. Oct 10, 2011 at 20:59
  • 11
    You can use OO principles effectively in C (and people who write good C code typically do), but the language is not built around making it easy, as many more recent languages are.
    – asveikau
    Oct 10, 2011 at 22:47
  • 5
    C simply has a different, simpler, and (to be honest) better defined (at least among the open-source community) approach to data abstraction. C++ tends to be an abstraction powerhouse and allows for many great things, but comes with a cost of having to understand how to [not] use them properly, which is, more often than not, quite missing in common programs. See my full answer for more details. Oct 10, 2011 at 23:55
  • 1
    Shortly: Structs can't have methods. (Pointer to a function doesn't quite cut it).
    – SF.
    Oct 11, 2011 at 7:01
  • 1
    It is possible to do object-based programming in C, with some difficulty. But that doesn't make it object oriented.
    – Wedge
    Oct 11, 2011 at 9:24

13 Answers 13

115

It seems that C has its own quasi-objects such as 'structs' that can be considered as objects

Let's together you and I read through the Wikipedia page on object oriented programming and check off the features of C-style structs that correspond to what is traditionally considered to be object-oriented style:

(OOP) is a programming paradigm using "objects" – data structures consisting of data fields and methods together with their interactions

Do C structs consist of fields and methods together with their interactions? No.

Programming techniques may include features such as data abstraction, encapsulation, messaging, modularity, polymorphism, and inheritance.

Do C structs do any of these things in a "first class" way? No. The language works against you every step of the way.

the object-oriented approach encourages the programmer to place data where it is not directly accessible by the rest of the program

Do C structs do this? No.

An object-oriented program will usually contain different types of objects, each type corresponding to a particular kind of complex data to be managed or perhaps to a real-world object or concept

Do C structs do this? Yes.

Objects can be thought of as wrapping their data within a set of functions designed to ensure that the data are used appropriately

No.

each object is capable of receiving messages, processing data, and sending messages to other objects

Can a struct itself send and receive messages? No. Can it process data? No.

OOP data structures tend to "carry their own operators around with them"

Does this happen in C? No.

Dynamic dispatch ... Encapsulation ... Subtype polymorphism ... Object inheritance ... Open recursion ... Classes of objects ... Instances of classes ... Methods which act on the attached objects ... Message passing ... Abstraction

Are any of these features of C structs? No.

Precisely which characteristics of structs do you think are "object oriented"? Because I can't find any other than the fact that structs define types.

Now, of course you can make structs that have fields that are pointers to functions. You can make structs have fields that are pointers to arrays of function pointers, corresponding to virtual method tables. And so on. You can of course emulate C++ in C. But that is a very non-idiomatic way to program in C; you'd be better off just using C++.

And also, C files themselves are basically separate "modules", right? Then aren't modules kind of like 'objects' too?

Again, what characteristics of modules are you thinking of that makes them act like objects? Do modules support abstraction, encapsulation, messaging, modularity, polymorphism, and inheritance?

Abstraction and encapsulation are pretty weak. Obviously modules are modular; that's why they're called modules. Messaging? Only in the sense that a method call is a message and modules can contain methods. Polymorphism? Nope. Inheritance? Nope. Modules are pretty weak candidates for "objects".

20
  • 16
    I disagree that "emulating C++" (your term) isn't idiomatic C. One counter example would be how an OS kernel typically implements file operations - take Linux as an example, where you have structs full of function pointers. They may be somewhat "domain-specific" idioms (restricted to writing drivers on *nix, say), but they are recognizable and do the job cleanly enough for some purposes. There are also some examples of user mode libraries that do some notion of OO well enough; take Gtk+ and the libraries it depends on. Call it hacky and you may be right, but these are not terrible to work with
    – asveikau
    Oct 10, 2011 at 23:16
  • 6
    @asveikau: Doesn't the idea that a practice is unusual (even if not unheard of) and "hacky" mean that it is, by definition, not idiomatic? Oct 11, 2011 at 1:50
  • 24
    @asveikau: Sure, there are things you can do to make C programs more OO in style. But as you say, it requires discipline to do so; the language features themselves do not naturally lead you towards encapsulation, polymorphism, class inheritance, and so on. Rather, the developer can impose that pattern on the language. That doesn't mean that structs are logically the same thing as objects. Heck, you can program in an OO style in Lisp too, but that doesn't mean that cons cells are objects. Oct 11, 2011 at 5:48
  • 6
    @NemanjaTrifunovic: Read that sentence again: "You can program in an OO style in Lisp too, but that doesn't mean that cons cells are objects." My point is not that you cannot program OO style in Lisp, as you can. My point is that doing so does not automatically make the fundamental building blocks of Lisp "objects". Similarly, you can program in an OO style in C, but that doesn't mean that structs (or modules) are objects. Oct 11, 2011 at 13:07
  • 10
    @BillK: Structs cannot contain code in C. Structs can contain function pointers, which are data. Function pointers aren't code. Code is a textual artifact created by a programmer. Oct 11, 2011 at 21:36
55

The key word is "oriented", not "object". Even C++ code that uses objects but uses them like structs is not object oriented.

C and C++ can both do OOP (aside from no access control in C), but the syntax for doing it in C is inconvenient (to say the least), while the syntax in C++ makes it very inviting. C is oriented to procedural, while C++ is oriented to objects, despite nearly identical core capabilities in that regard.

Code that uses objects to implement designs that can only be done with objects (usually meaning taking advantage of polymorphism) is object oriented code. Code that uses objects as little more than bags of data, even using inheritance in an object oriented language, is really just procedural code that is more complicated than it needs to be. Code in C that uses function pointers that are changed around at runtime with structs full of data is kinda doing polymorphism, and could be said to be "object oriented", even in a procedurally oriented language.

8
  • 5
    +1 For pointing our C can be OO. It's not convenient, but it can be done.
    – snakehiss
    Oct 11, 2011 at 4:04
  • It's easer to create objects in VBA, but I wouldn't consider it object 'oriented' either.
    – JeffO
    Oct 11, 2011 at 12:09
  • VBA I call "object based". It uses objects, but not polymorphically, and in the little bit of work I've done in it, I'm not sure you can even do polymorphism no matter what kind of code acrobatics you try. It's basically a procedural language with encapsulation.
    – kylben
    Oct 11, 2011 at 12:17
  • 4
    Most languages can act as an OO, functional or pretty much any style of language. the difference is the "Oriented" and I'd never heard it put that way before. I wish I could vote this up 3 times and accept it, it's the right answer.
    – Bill K
    Oct 11, 2011 at 15:34
  • 1
    @kylben It wouldn't be too much of a stretch to store SQL code in the table then extract and execute (making the table your object) It would be an interesting experiment. Actually it's a little tempting...
    – Bill K
    Oct 11, 2011 at 20:07
20

Based on the very highest level principals:

An object is an encapsulation of data and behaviour in an interlinked way such that they operate as a whole that can be instantiate multiple times and worked on as a black box if you know the external interface.

Structs contain data but no behaviour and therefore can not be considered objects.

Modules contain both behaviour and data but are not encapsulated in such a way that the two are related and certainly can't be instantiated multiple times.

And that's before you get onto inheritance and polymorphism...

5
  • How come behavior and data in modules aren't related? Aren't member functions basically the 'behavior' on the data that exist inside the module? Oct 10, 2011 at 21:05
  • 4
    The problem is that they are not encapsulated, not that they are not related. Oct 10, 2011 at 21:08
  • Actually objects are centred principally around behaviour, not data. Data is/should be a second class citizen in OOP. Structs, instead are centred around data and have no behaviour.
    – Sklivvz
    Oct 11, 2011 at 0:27
  • 1
    @Dark Templar - What Eoin said... It's in the nature of the relationship. I can see where you're coming from - you could certainly use Structs within a module in such a way that it emulated some very very basic elements of OO but much of what you'd be doing would rely on the programmer sticking to a set of self imposed rules rather than having the language enforce it. And why would you bother? You're getting none of the benefits of OO - no inheritance for instance - and you're restricting yourself. Oct 11, 2011 at 6:20
  • This is a better answer than the one marked correct. Nov 2, 2011 at 18:34
8

"structs" are data only. The usual quick and dirty test of "object orientation" is: "Is there a structure that allows code and data to be encapsulated as a single unit?". C fails that, and is hence procedural. C++ passes that test.

5
  • 6
    There are workarounds, structs can have static function pointers to member functions. They will need explicit this pointer, but in that case, data and means to process data will be encapsulated inside the structure.
    – Coder
    Oct 10, 2011 at 20:50
  • @Brian Knoblauch: but isn't a C file itself an encapsulation of data and code?? Oct 10, 2011 at 20:51
  • @DarkTemplar In a sense, yes, but unless you manage to create translation units, compile them and load them into the running process at runtime, this does you little good.
    – user7043
    Oct 10, 2011 at 20:53
  • translation units? >.< Oct 10, 2011 at 20:56
  • @Dark Templar: A translation unit is a source file plus anything #included into it, and minus anything removed by not being conditionally included (#if, #ifdef, and the like). Oct 11, 2011 at 13:45
5

C, just like C++, has the capability of providing Data Abstraction, which is one idiom of the object-oriented-programming paradigm that existed before it.

  • C structs can have data (and that's their main purpose)
  • C structs can also define function pointers as data
  • C structs can and often have a set of functions associated with them, just like methods, only the this pointer is not passed implicitly, but you must specify it explicitly as the first argument to each method designed to handle the specified struct. C++ does this automatically for you, both when you define and call class/struct methods.

OOP in C++ extends the means to abstract data. Some say it's harmful, while others consider it a good tool when used correctly.

  • C++ makes the this pointer implicit by not requiring the user to pass it into "methods of the class/struct" as long as the type can be (at least partly) identified.
  • C++ lets you restrict access to certain methods (class functions), and hence allows for more "defensive programming" or "idiot-proofing".
  • C++ encourages abstractions by providing stronger type-safety by introducing
    1. The new operator instead of malloc + cast
    2. Templates instead of void pointers
    3. Inline functions receiving typed-values instead of macros
    4. Built in Polymorphism which you don't have to implement yourself, that lets you create abstraction hierarchies, contracts and specializations.

However, you'll find many C "hackers" preaching for how C is perfectly capable of just the right amount of abstraction and how the overhead created by C++ only distracts them from solving the actual problem.

Inefficient abstracted programming models where two years down the road you notice that some abstraction wasn't very efficient, but now all your code depends on all the nice object models around it, and you cannot fix it without rewriting your app. -- Linus Torvalds

Others tend to look at it in a more balanced way, accepting both the advantages along with the disadvantages.

C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off. -- Bjarne Stroustrup

2

You need to look at the other side of the coin: C++.

In OOP, we think of an abstract object (and design the program accordingly), for example a car that can stop, accelerate, turn left or right, etc. A struct with a bundle of functions simply does not fit the concept.

With "real" objects we need to hide the members for instance, or we can also have inheritance with a real "is a" relationship and many more.

AFTER READING THE COMMENTS BELOW: Well it's right that (almost) everything can be done with C (that is always true), but at first glance I thought that what separates c from c++ is the way you think when designing a program.

The only thing that really makes the difference is the imposing of policies by the compiler. i.e pure virtual function, and such. but this answer will only relate to technical issues, but I think the the main difference (as mentioned) is the original way you think while you code, since C++ gives you a better built in syntax for doing such things, instead of doing OOP in a somewhat clumsy way in C.

3
  • Not really sure why a struct with a "bundle of functions" doesn't fit the concept? +1 for the answer though Oct 10, 2011 at 20:55
  • 1
    Aside from access control (private/protected/public) everything else can be done with structs.
    – Coder
    Oct 10, 2011 at 20:58
  • 1
    @DarkTemplar: Well, you can try to emulate OOP in C (people have actually written ebooks on that topic and do it in the real world, albeit very rarely), like you can try emulating functional programming in Java. But C provides zero help for it, hence C the language isn't object-oriented.
    – user7043
    Oct 10, 2011 at 20:58
1

You kind of said it yourself. While C has things that are kind of like objects, they are still not objects, and that is why C is not considered an OOP language.

1
  • 2
    Well, I guess the question is: why and where is the line drawn for what is an object and what isn't? Oct 10, 2011 at 20:49
1

Object Oriented refers to both an architectural pattern (or even a meta-pattern), and, the languages that have features to help implement or mandate using this pattern.

You can implement an "OO" design (Gnome desktop is perhaps the best example of OO done in pure C), I have even seen this done with COBOL!

However being able to implement an OO design dose not make the language OO. Purists would argue that Java and C++ are not truly OO as you cannot override or inherit the basic "types" such as "int" and "char", and, Java does not support multiple inheritance. But as they are the most widely used OO languages and support most of the paradigm most "real" programmers who get paid to produce working code regard them as the OO languages.

C on the other hand supports only structures (as do COBOL,Pascal and dozens of other procedural languages), you could argue that supports multiple inheritance in that you can use any function on any piece of data but most would regards this as a bug rather than a feature.

0

Let's just look at the definition of OO:

  • Messaging
  • Encapsulation
  • Late Binding

C provides none of those three. In particular, it doesn't provide Messaging, which is the most important one.

3
  • 1
    I think I'd have to disagree with that. You can most certainly have messaging in C — work with Objective C APIs from C and you find that out in depth — and late binding can be done through function pointers, but you don't get lots of syntactic sugar to hide it. (Encapsulation is actually easy in C; pointers to incomplete structure types do the job perfectly.) Oct 11, 2011 at 8:03
  • Inheritance is also considered important in OO, and C doesn't do that. The closest thing to encapsulation in C is separate files that can have internal (static) functions and data members. Oct 11, 2011 at 13:39
  • @DonalFellows, the actual message passing in Objective-C could be easily implemented as a C99 macro. The only thing that the compiler has going for it is that it generates all the necessary structures statically from a few lines of high level code. Most if not all features are available from the C API.
    – user2582
    Oct 11, 2011 at 15:22
0

There are a number of key ingredients to OO, but the big ones are that the majority of code does not know what is inside an object (they see the surface interface, not the implementation), that the state of an object is a managed unit (i.e., when the object ceases to be, so does its state), and when some code invokes an operation on an object, they do so without knowing exactly what that operation is or involves (all they do is follow a pattern to throw a “message” over the wall).

C does encapsulation just fine; code which does not see the definition of a structure can't (legitimately) peek inside it. All you need to do that is to put a definition like this in a header file:

struct FooImpl;
typedef struct FooImpl *Foo;

Of course, there will be a need for a function that builds Foos (i.e., a factory) and that should delegate some of the work to the allocated object itself (i.e., through a “constructor” method), and also to have a way of disposing of the object again (while letting it clean up via its “destructor” method) but that's details.

Method dispatch (i.e., messaging) can also be done through the convention that the first element of the structure is actually a pointer to a structure full of function pointers, and that each of those function pointers must take a Foo as its first argument. Dispatch then becomes a matter of looking up the function and calling it with the right argument rewrite, which isn't that hard to do with a macro and a little cunning. (That table of functions is the core of what a class really is in a language like C++.)

Furthermore, that gives you late binding too: all the dispatch code knows is that it is invoking a particular offset into a table pointed at by the object. That only needs to be set during the allocation and initialization of the object. It's possible to go with even more complex dispatch schemes that buy you more runtime dynamism (at a cost of speed) but they're cherries on top of the basic mechanism.

Yet that doesn't mean that C is an OO language. The key is that C leaves you to do all the tricky work of writing the conventions and dispatch mechanism yourself (or use a third-party library). That's a lot of work. It also doesn't provide syntactic or semantic support, so implementing a full class system (with things like inheritance) would be unnecessarily painful; if you're dealing with a complex problem that is well described by an OO model, an OO language will be very useful in writing the solution. The extra complexity can be justified.

0

I think C is perfectly fine and decent for implementing object-oriented concepts, shrug. Most of the differences as I see it between the common denominator subset of languages considered object-oriented are minor and syntactical in nature from my sort of pragmatic viewpoint.

Let's start with, say, information hiding. In C we can achieve that by simply hiding the definition of a structure and working with it through opaque pointers. That effectively models the public vs. private distinction of data fields as we get with classes. And it's easy enough to do and hardly anti-idiomatic, as the standard C library relies on this heavily to achieve information hiding.

Of course you lose the ability to easily control exactly where the structure is allocated in memory by using opaque types, but that's only a noteworthy difference between, say, C and C++. C++ is definitely a superior tool when comparing its ability to program object-oriented concepts over C while still maintaining control over memory layouts, but that doesn't necessarily mean that Java or C# is superior to C in that regard, since those two make you completely lose the ability to control where objects are allocated in memory.

And we do have to use a syntax like fopen(file, ...); fclose(file); as opposed to file.open(...); file.close(); but big whoop. Who really cares? Maybe just someone who leans heavily on auto-completion in their IDE. I do admit that can be a very useful feature from a practical standpoint, but maybe not one that necessitates a discussion about whether a language is suitable for OOP.

We do lack the ability to effectively implement protected fields. I'll totally submit there. But I don't think there's a concrete rule that says, "All OO languages should have a feature to allow subclasses to access members of a base class that should still not be accessed by normal clients." Besides I rarely see use cases for protected members that aren't at least a little bit suspicious of becoming a maintenance hurdle.

And of course we have to "emulate" OO polymorphism with tables of function pointers and pointers to them for dynamic dispatch with a little more boilerplate to initialize those analogical vtables and vptrs, but a little bit of boilerplate never caused me much grief.

Inheritance is much the same way. We can easily model that through composition, and at the internal workings of compilers it boils down to the same thing. Of course we lose type safety if we want to downcast, and there I'd say if you want to be downcasting at all, to please not use C for it because the things people do in C to emulate downcasting can be horrific from a type safety standpoint, but I'd rather people don't downcast at all. Type safety is something you can easily start to miss in C as the compiler provides so much leeway to interpret things as just bits and bytes, sacrificing the ability to catch would-be errors at compile-time, but some languages considered object-oriented aren't even statically typed.

So dunno, I think it's fine. Of course I wouldn't use C to try to create a large-scale codebase that conforms to SOLID principles, but it's not necessarily due to its short-comings in the object-oriented front. A lot of the features I would miss if I attempted to use C for such a purpose would be related to language features not directly considered a prerequisite for OOP such as strong type safety, destructors which are automatically invoked when objects go out of scope, operator overloading, templates/generics, and exception-handling. It's when I'm missing those ancillary features that I reach for C++.

6
  • 1
    I'm not sure how constructors could not be considered a prerequisite for OOP. Part of the basic essence of encapsulation is the ability to ensure invariants for an object. And that requires being able to properly initialize the object within those invariants. That requires a constructor: one or more functions, such that you cannot say that the object exists yet until at least one of them has been called. Dec 19, 2017 at 1:30
  • When we have information hiding with opaque types then the only way to instantiate them and copy/clone and destroy them is through the analogical equivalent of functions serving as constructors and destructors with just as much ability to maintain those invariants. It's just not directly modeled into the language and might resemble the form of foo_create and foo_destroy and foo_clone, e.g.
    – user204677
    Dec 19, 2017 at 1:52
  • All we need to maintain invariants on struct Foo in such a case is to make sure its data members cannot be accessed and mutated by the outside world, which opaque types (declared but not defined to the outside world) give right away. It's arguably a stronger form of information hiding, on par with that of a pimpl in C++.
    – user204677
    Dec 19, 2017 at 1:54
  • Yes, I know how people implement OOP in C, thank you. My point was that your statement that constructors are "not directly considered a prerequisite for OOP" is in error. Dec 19, 2017 at 1:57
  • I see, lemme correct that... but in that case might we say that C does provide (adequate?) support for destructors and constructors?
    – user204677
    Dec 19, 2017 at 1:57
0

The distinction between an OO language and some other is not whether you can write a program a certain way (they're all turing complete, after all), but whether it comes with provisions to support this programming style.

In the end, everything in a program compiles down to machine code. By extension, that means you can write code for any programming paradigm in assembler. If you allow me to horribly simplify, C is basically a portable assembler. You can implement most programming styles in C (you may have to resort to macros for a few things, and it may not be as efficient as in assembler, but you can do it).

The difference between an object-oriented language like C++ and a procedural language like C is that you need to re-implement a lot of the provisions for object-oriented programming yourself that come built into C++.

If you use C, you need to manually define each implementation function, the virtual method dispatch tables for each class and subclass, keep them in sync or wildly typecast between base and inherited classes (or just stay aware of which methods are from the base class and call them via a super member or a chain of super.super.super).

Also, C++ knows what an object is, and can detect nonsensical code that a C compiler looking at your re-implementation of everything wouldn't be able to tell. It won't just NULL-initialize a missing function pointer at the end of the vtable and move on, it will tell you that you forgot to implement a pure virtual method, or will fill it in itself because it sees it was added to the base class.

Moreover, an OO language constitutes a standard for OO provisions. Your OO-with-C-code will be different from someone else's. If it's built into the language, you can just grab someone else's OO code and use it. If you use different styles for OO-with-C, you may have to create adapter objects.

-1

Not a bad question.

If you are going to call c an OO language you would need to called just about all procedural languages OO too. So it would make the term meaningless. c has no language support for OO. If has structs, but structs are types no classes.

Actually c doesn't have a lot of features at all compared to most languages. It's mostly use for it's speed, simplicity, popularity and support including tons of libraries.

Not the answer you're looking for? Browse other questions tagged or ask your own question.