You might have heard this phrase by your professors a lot - "C++ is a superset of C. Anything that works in C, works in C++." If you are in India, probably that's how you have been introduced to C++. And the saying is true ... around 30 years ago.

In fact, when C++ had just come out, it was indeed a superset of C, with some exceptions. The preface of the 1st edition of The C++ Programming Language, written by Bjarne Stroustrup, the creator of C++ himself starts with -

Except for minor details, C++ is a superset of the C programming language.

This book came out in 1985. And there has been a lot of changes in C++ and C since then. They have both improved and rectified in different direction. The difference is so distinct that it's no longer true to say C++ is a superset of C. It's true that both the languages have a really tiny bit of intersection, however they are different languages. In fact, the preface to the 4th revision of The C++ Programming Language starts with "C++ feels like a new language".

Currently we have between C and C++ -

1. Things that compile as C++ but do not compile as C. This is obvious, since if you claim C++ is a superset of C, you are claiming that there are more to C++ than C.
2. Things that compile as C and C++ both and do the same thing. This is the part where the two languages intersect.
3. Things that compile as C but not as C++.
4. Things that compile as C and C++ both but produce different results.

We will focus on 3 and 4, which will show that C++ is not a superset of C.

## Things that work in C but not in C++

char *p = "abc";

This is valid C but invalid C++. Trying to compile this as C++ will give a warning -

warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]

The reason is the type of a string literal is changed from “array of char” to “array of const char”.

C++ doesn't have "tentative definitions" as in C. So that doing this at file scope -

int i;
int i;

is valid C but invalid C++. This prevents you from defining mutually referential file-local objects with static storage duration. For example -

struct X { int i; struct X* next; };

static struct X a;
static struct X b = { 0, &a };
static struct X a = { 1, &b };

is valid C but invalid C++. Here we want a to reference b and b to reference a. So we define a first and then after defining b we redefine a pointing to b. This produces an error in C++

main.cpp:14:17: error: redefinition of ‘X a’
static struct X a = { 1, &b };

In C, the main function can be called recursively and its address can be taken. This is not allowed in C++

Converting a void * without cast is allowed in C but not in C++

int x = 2;
void *p = &x;
long *k = p;

This is valid C, but invalid C++. To make it valid, you need to cast

int x = 2;
void *p = &x;
long *k = (long *) p;

In C, implicit declaration of functions is allowed. If you do not declare a function, it is assumed to take int parameters and return int. This is why you can write this -

int main()
{
printf("Hi");
return 0;
}

Although we haven't included stdio.h, we will get a warning -

main.c:12:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
main.c:12:5: warning: incompatible implicit declaration of built-in function ‘printf’
main.c:12:5: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’

But it would compile and work.

However, implicit declaration will not work in C++ and result in an error.

In C++, it is invalid to return (explicitly or implicitly) from a function which is declared to return a value without actually returning a value

int f() { return; }

This is valid C and produces a warning -

main.c:9:11: warning: ‘return’ with no value, in function returning non-void

In C++, it produces error

main.cpp: In function ‘int f()’:
main.cpp:11:11: error: return-statement with no value, in function returning ‘int’ [-fpermissive]

If you declare a function like this -

int f();

Does this mean this function takes no argument? Your professors might have told so, but it's not true. It means the number of parameters it takes is unknown. But in C++, this means exactly what you think - it takes no arguments. So that calling f(1) is valid C but invalid C++.

### Honourable mentions - features that are in C but never were in C++

So far we have only seen changes to existing features. How about some new stuff? Here are a few features that are only in C, and not in C++ -

1. variable-length arrays
2. static array indexes
3. compound literals
4. restrict type qualifier
5. designated initializers for for structs/unions and for arrays
6. Generic selection
7. etc.

## Things that work in C and C++ both but produce different results

sizeof('a') == sizeof(int)

This is 1 in C (i. e. true) but 0 in C++ (i.e. false). The reason is, in C, the type of character literal is int whereas in C++, it has been changed to char. So, in C++ sizeof('a') == sizeof(char) is true instead.

In C++, an enumeration is distinct type, whereas in C, it is an int

enum e { A };
sizeof(A) == sizeof(int)        // true in C
sizeof(A) == sizeof(e)          // true in C++
/* and sizeof(int) is not necessarily equal to sizeof(e) */

In C++, a class declaration introduces the class name into the scope where it is declared and hides any object, function or other declaration of that name in an enclosing scope.In C, an inner scope declaration of a struct tag name never hides the name of an object or function in an outer scope.

What this means, is that you have a variable in global scope and inside a function you declare a struct with the same tag name, in C++, it hides the outside variable.

int x[99];
void f() {
struct x { int a; };
sizeof(x);  /* size of the array in C */
/* size of the struct in C++ */
}