23

Many people have said that C++ is a completely different language than C, but Bjarne himself has said that C++ is a language that is extended from C hence that is where the ++ comes from. So why does everybody keep saying that C and C++ are completely different languages? In what way is C different from C++ other than the extended features in C++?

1
  • 3
    Due to how they are used. You can certainly write C in C++... but you shouldn't. Commented Oct 24, 2011 at 18:56

9 Answers 9

21

During the 1980's, when C++ development was just beginning, C++ was almost a proper superset of C. That is how it all started.
However, over time, both C and C++ evolved and have diverged from one another, even though compatibility between the languages is has always been considered important.

Additionally, the technical differences between C and C++ have made the typical idioms in those languages and what is considered 'good practice' diverge even more.

This is the driving factor behind people saying things like "there is no such language as C/C++" or "C and C++ are two different languages". Although it is possible to write programs that are acceptable to both a C and a C++ compiler, the code is generally considered to be neither an example of good C code nor an example of good C++ code.

2
  • 1
    I don't think the first paragraph is correct. I believe C always had implicit casting from void * but C++ never did. (Did not downvote) Commented Apr 10, 2011 at 16:50
  • 6
    @mathepic: Define "always". C took void* type from C++. In K&R C, malloc was returning char* Commented Oct 24, 2011 at 18:53
23

Stroustrup himself answers that in his FAQ:

C++ is a direct descendant of C that retains almost all of C as a subset. C++ provides stronger type checking than C and directly supports a wider range of programming styles than C. C++ is "a better C" in the sense that it supports the styles of programming done using C with better type checking and more notational support (without loss of efficiency). In the same sense, ANSI C is a better C than K&R C. In addition, C++ supports data abstraction, object-oriented programming, and generic programming.

It's support for object-oriented programming and generic programming that make C++ "completely different" to C. You can almost write pure C and then compile it with a C++ compiler (as long as you take care of the stricter type checking). But then you're still writing C - you're not writing C++.

If you're writing C++, then you're making use of it's object-oriented and template features and that's nothing like what you would see in C.

1
  • 1
    "that's nothing like what you would see in C." This phrase sounds funny! "see in C". C in C! This is a kind of poetry.
    – Galaxy
    Commented Jun 21, 2018 at 22:11
12

Put simply, what is considered idiomatic in C is definitely not idiomatic in C++.

C and C++ are very different languages in practice, because of the way people use them. C aims at minimalism, where C++ is a very complex language, with a lot of features.

There are also some practical differences: C can be easily called from pretty much any language, and often defines the ABI of a platform, whereas C++ is quite hard to use from other libraries. Most languages have a FFI or interface in C, even languages implemented in C++ (java, for example).

1
  • 4
    It's not just that C-style code is non-idiomatic in C++. C-style coding is actually problematic in C++, because of the lack of exception safety.
    – dan04
    Commented Jan 22, 2011 at 13:38
5

Apart from the obvious fact that C++ supports object-oriented programming, I think you have your answer here: http://en.wikipedia.org/wiki/Compatibility_of_C_and_C++

That article contains code examples showing stuff that is ok in C but not in C++. For instance:

int *j = malloc(sizeof(int) * 5); /* Implicit conversion from void* to int* */

Porting a C program to C++ is often straightforward and consists mostly of fixing compilation errors (adding casts, new keywords etc).

2
  • 5
    Porting a program like that does not give you a C++ program. It gives you C that can be compiled on C++ compiler. That does not make it C++ (I would still call the resulting code C (not even C with classes)). Commented Oct 24, 2011 at 17:46
  • @MartinYork Call it bad C, point out the semantics might be different, and I concurr. The result is obviously C though. Commented Dec 20, 2018 at 18:18
4

The "extended features", you make it sound like in C++ they added like, variadic macros or something and that's it. The "extended features" in C++ are a complete overhaul of the language and totally supersede the best C practices because the new C++ features are so much better than the original C features that the original C features are completely and totally redundant in the vast majority cases. Suggesting that C++ merely extends C is suggesting that a modern battle tank extends a butterknife for the purposes of waging war.

2
  • Can you enumerate an example of one of the more useful features that C++ has that C doesn't? Commented Oct 10, 2011 at 20:46
  • 3
    @DarkTemplar: How about easy-mode resource management with RAII? Or nice generic data structures using templates? Just to begin with.
    – DeadMG
    Commented Oct 11, 2011 at 10:33
2

C++ adds not only new features, but new concepts and new idioms to C. Even though C++ and C are closely related, the fact remains that in order to write effectively in a language, you must think in the style of that language. Even the best C code cannot take advantage of the different strengths and idioms of C++, and so is more likely than not actually rather bad C++ code.

1

The difference is that in C you think procedurally and in C++ you think in an object orientated way. The languages are quite similar but the approach is very different.

2
  • Doesn't C also have structs? Aren't C files themselves separate modules(basically, objects)? Not sure what the exact difference between procedural and object-oriented is... Commented Oct 10, 2011 at 20:39
  • 1
    Dark you have illustrated my point. It's not a limitation of the language that allows you to write procedurally or in an object-orientated way it is an ethos or way of thinking.
    – Ant
    Commented Dec 14, 2011 at 9:29
0

While C++ can be a super set of C in syntactic terms - i.e. any construct of C program can be compiled by C++ compiler.

However, you almost never write a C++ programs the way you would have done with C program. The list can be endless or may be someone should just do more research to put it as exhaustive reports. However, i am putting few pointers that make the key differences.

The point of the current post is that C++ has following features that a good C++ programmers must use as programming best practices even though C equivalent are possible to compile.

How should it be done in C++ over C

  1. Classes & Inheritance. That's most important differences that allows systematic object orientation that makes programming expression very powerful. I guess - this point needs no better explanation. If you are in C++ - almost always, you are better of making use of classes.

  2. Privatization - Classes, and even structures have what is private members. This makes encapsulation of a class possible. The equivalent in C is by way of typecasting the object as void * to application so that application doesn't have access to internal variables. However, in C++ you can have elements with public as well as private classes.

  3. Pass by reference. C++ allows modification based on reference, for which passing pointers is required. Pass-by-reference keeps code very clean and more so safer against pointer hazards. You an as well pass C style pointer and that works - but if you are in C++ you are better-off as long as

  4. new & delete vs. malloc and free. The new() and delete() statements not only allocates and de-allocate memory, but also allow code to execute as part of destruct-er to be called in a chain. If you are using C++ - it is actually BAD to use malloc and free.

  5. IO types and operator overloading Operator overloading make code readable or more intuitive if done well. Same for << and >> io operators. The C way of doing this would be to use function pointers - but it is messy and only for advance programmers.

  6. Using "string". The char * from C works everywhere. So C and C++ is pretty much same. However, if you are in C++ - it is always much better (and safer) to use String classes which saves you from the dangers of arrays over running which are almost every things.

Features I would still not be fan of in C++ 1. Templates - While i don't use heavy templates in many codes - it can turn out to be very powerful for libraries. There is almost no equivalent of it in C. But on a normal day - specially if you are doing mathematically missing.

  1. Smart pointers - Yes, they are very smart! And like most smart things - they start out good and become messy later! I don't quite like to use

Things that i like about C and miss in C++

  1. Polymorphic algorithms by using function pointers. In C, when you are running complex algorithms - some times you can use set of function pointers. This makes true polymorphism in a powerful way. When you are in C++ you CAN use function pointers - but that is bad. You should be only using methods - else be prepared to get messy. The only form of polymorphism in C++ classes is function and operator overloading but that is quite limiting.

  2. Simple threads. When you create threads were pthreads - it is quite simple and manageable. It becomes when you need to create threads which are supposed to be "private" to the classes (so that they have access to private members). There is boost type of frameworks - but nothing in basic C++.

Dipan.

1
  • "When you are in C++ you CAN use function pointers - but that is bad." Nonsense. Function pointers are available in C++ and if using them is the way to implement the desired functionality then use them. That's what they are there for. Actually this entire paragraph about polymorphism in C and C++ is misleading. C++ offers much better facilities for polymorphism than C with much better compile time error checking. Commented Jul 31, 2020 at 2:36
0

Certain language features, even if they are additions, can change the whole way the language practically needs to be used. As one example consider this case:

lock_mutex(&mutex);

// call some functions
...

unlock_mutex(&mutex);

If that above code involved calling functions implemented in C++, we could be in for a world of trouble, as any one of those function calls may throw and we'll never unlock the mutex in those exceptional paths.

Destructors are no longer in the realm of convenience to help programmers avoid forgetting to release/free resources at that point. RAII becomes a practical requirement because it's not humanly feasible to anticipate every single line of code that can throw in non-trivial examples (not to mention that those lines may not throw now but may later with changes). Take another example:

void f(const Foo* f1)
{
    Foo f2;
    memcpy(&f2, f1, sizeof f2);
    ...
}

Such code, while generally innocuous in C, is like hellfire reigning havoc in C++, because the memcpy bulldozes over the bits and bytes of these objects and bypasses things like copy constructors. Such functions like memset, realloc, memcpy, etc, while daily tools among C developers used to looking at things in a rather homogeneous way of bits and bytes in memory, are not harmonious with the more complex and richer type system of C++. C++ encourages a much more abstract view of user-defined types.

So these types of things no longer allow for C++, by anyone looking to use it correctly, to look at it as a mere "superset" of C. These languages require a very different mindset, discipline, and way of thinking to use most effectively.

I'm not in the camp that sees C++ as outright better in every way, and actually most of my favorite third-party libs are C libraries for some reason. I don't know why exactly but C libs tend to be more minimalist in nature (perhaps because the absence of such a rich type system makes the developers more focused on providing the minimal functionality required without building some large and layered set of abstractions), though I do often end up just putting C++ wrappers around them to simplify and tailor their usage for my purposes, but that minimalist nature is preferable to me even when doing that. I really love minimalism as an attractive feature of a library for those who take the extra time to seek such qualities, and perhaps C tends to encourage that, if only by the virtue of the fact that such large and layered and abstract code would be a real PITA to develop in C.

I do favor C++ far more often than not, but I'm actually required to use C APIs rather often for the widest binary compatibility (and for FFIs), although I often implement them in C++ in spite of using C for the headers. But sometimes when you go really low-level, like to the level of a memory allocator or very low-level data structure (and I'm sure there are further examples among those who do embedded programming), it can sometimes be helpful to be able to assume that the types and data you're working with are absent certain features like vtables, costructors, and destructors, so that we can treat them as bits and bytes to shuffle around, copy, free, reallocate. For very particularly low-level problems, it can sometimes be helpful to work with a much simpler type system that C provides, and of course it tends to build faster and so forth.

A Clarification

One interesting comment here I wanted to respond to a bit more in depth (I find the comments here are so strict on character limit):

memcpy(&f2, f1, sizeof f2); is also "hellfire reigning havoc" in C if Foo has any owning pointers, or is even worse, as you also lack the tools to deal with that.

That's a fair point but everything I'm focusing around is predominantly with a focus on C++'s type system and also with respect to RAII. One of the reasons such x-rayish byte-copying memcpy or qsort types of functions pose less of a practical danger in C is that the destruction of f1 and f2 above are explicit (if they even need non-trivial destruction), whereas when destructors move into the picture, they become implicit and automated (often with great value to the developers). That's not to even mention hidden state like vptrs and so forth which such functions would bulldoze right over. If f1 owns pointers and f2 shallow copies them in some temporary context, then it poses no problem if we do not try to explicitly free those owning pointers a second time. With C++ that is something that compiler will automatically want to do.

And that becomes more of a bigger if typically in C, "If Foo has owning pointers", because the explicitness required with the resource management will often make that something typically more difficult to overlook whereas in C++, we can make a UDT no longer trivially constructible/destructible by merely making it store any member variable which isn't trivially constructible/destructible (in a way that's generally very helpful, again, but not if we're tempted to use functions like memcpy or realloc).

My main point is not to try to argue any benefit of this explicitness (I would say if there are any, they are almost always weighed down by the cons of the increased probability of human error that comes with it), but merely to say that functions like memcpy and memmove and qsort and memset and realloc and so forth have no place in a language with UDTs as rich in features and capabilities as C++. While they exist regardless, I think it wouldn't be too contested to say that the vast, vast majority of C++ developers would be wise to avoid such functions like the plague, whereas these are very daily sorts of functions in C, and I'd argue that they pose less problems in C for the simple reason that its type system is a lot more basic and, perhaps "dumber". X-raying C types and treating them as bits and bytes is error-prone. Doing that in C++ is arguably just outright erroneous because such functions are fighting against very fundamental features of the language and what it encourages of the type system.

That is actually the biggest appeal to me of C, however, specifically with how it relates to language interoperability. It would be much, much more difficult to make something like C#'s FFI understand the full-blown type system and language features of C++ down to constructors, destructors, exceptions, virtual functions, function/method overloading, operator overloading, all the various types of inheritance, etc. With C it's a relatively dumber language that has become rather standard as far as APIs in ways that many different languages can import directly through FFIs, or indirectly through some C API exporting functions in a desired form (ex: Java Native Interface). And that's where I'm mostly left having no choice but to use C, since that language interoperability is a practical requirement in our case (though often I'm just writing C interfaces with C++ implementations behind them).

But you know, I'm a pragmatist (or at least I strive to be). If C was this most foul and godawful, error-prone, nasty language some of my C++ enthusiast peers claimed it to be (and I'd count myself a C++ enthusiast except that somehow it has not lead to a hatred of C on my part; to the contrary it had the opposite effect on me of making me appreciate both languages better in their own respects and differences), then I'd expect that to show up in the real world in the form of some of the buggiest and leakiest and unreliable products and libraries being written in C. And I don't find that. I like Linux, I like Apache, Lua, zlib, I find OpenGL tolerable for its long legacy against such shifting hardware requirements, Gimp, libpng, Cairo, etc. At least whatever hurdles the language poses does not seem to pose impasses as far as writing some cool libraries and products in competent hands, and that's really all I'm interested in. So I've never been the type so interested in the most passionate language wars, except to make a pragmatic appeal and say, "Hey, there's cool stuff out there! Let's learn how they made it and maybe there are cool lessons, not so specific to the idiomatic nature of the language, that we can bring back to whatever language(s) we're using." :-D

7
  • 2
    memcpy(&f2, f1, sizeof f2); is also "hellfire reigning havoc" in C if Foo has any owning pointers, or is even worse, as you also lack the tools to deal with that. So people writing C don't do things like that as much
    – Caleth
    Commented Dec 20, 2018 at 18:23
  • @Caleth Some of what simplifies that even with owning pointers is the explicit freeing of the owning pointers, so it effectively turns into a shallow copy in such cases with only one place still freeing the original memory, for example (in some cases, depending on the design). Whereas with the involvement of destructors, both Foo instances may now want to destroy the dynamic array, or vector, or something to this effect. I often find for some peculiar cases that it is helpful to be able to write certain data structures to lean on the fact that destruction is explicit rather than implicit.
    – user321630
    Commented Dec 20, 2018 at 18:29
  • @Caleth It is admittedly a laziness thing on my part since if Foo does have any owning pointers, we could give it a proper copy/move ctor, dtor, use value semantics, etc, and have much richer and safer code. But occasionally I find myself reaching for C in cases where I want to generalize a data structure or allocator at the homogeneous level of bits and bytes, with certain assumptions I can make on the types which tend to, in their narrow use cases, be simplified a bit by such assumptions I feel a bit more confident to make in C.
    – user321630
    Commented Dec 20, 2018 at 18:34
  • @Caleth In my case though, there is occasionally some cornerstone of the architecture where I find no use to look at things as any more than bits and bytes in memory to arrange and access (and typically those cases do not involve anything but PODs). Those are the few peculiar cases left where I still prefer C. If you imagine memory allocator, there isn't really anything more to work with than bits and bytes, void pointers, things of this sort, with all the focus on aligning and pooling and handing out bits and bytes, and in those very peculiar cases I find C offers fewer obstacles than C++.
    – user321630
    Commented Dec 20, 2018 at 18:43
  • The other case where I sometimes reach for C are cases where the code might actually become more generalized and reusable by being less abstract and focusing on primitives, like API void filter_image(byte* pixels, int w, int h); as a simple example as opposed to like API void filter_image(ImageInterface& img); (which would couple our code to such image interface, narrowing its applicability). In such cases I sometimes just implement such functions in C since there's little to be gained in C++, and it reduces the probability that such code would need future changes.
    – user321630
    Commented Dec 20, 2018 at 19:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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