C++ and C: How These Languages are Diverging

C++ started life as “C with Classes back in the 1980s, though it was quickly renamed “C++.” For much of the past thirty-odd years, C++ has been regarded as a superset of C; any C program could be compiled by any C++ compiler. 

But the two languages have been slowly drifting apart. C++ didn’t change much until 2011, when a program of three yearly updates began. On the other hand, C had new versions in 1999 (C99) and 2011 (C11); there was also C18, with bug fixes and technical clarifications. 

It takes time for new compiler versions to support these changes, but C++ compiler changes are now well ahead of the game, with a considerable amount of C++ 20 already in GCC, Clang, MS VC++ and others (as this chart shows). 

Dynamic Arrays

C++ has had std::vect for a number of years; that’s how you implement dynamic arrays. There’s also std::array, but that is a fixed size at compile time—it’s just a wrapper around C-type arrays. C, like C++, had fixed-size arrays right from the start; the only way to get dynamically allocated arrays was by doing tricks with getmem and using pointers. 

Since C11, C has had variable-length arrays whose size is determined at runtime. Here’s an example:

#include <stdio.h>

void AllocateArray(int size) {
  int data[size];
  printf("Size of data=%d\n",(int)sizeof(data));
}

int main(){
  AllocateArray(100);
  AllocateArray(500);
  AllocateArray(750);
}

When compiled with GCC, the open-source C compiler, this outputs the following:

Size of n=400
Size of n=2000
Size of n=3000

This shows that 100 ints occupies 400 bytes (as you’d expect). Note I said GCC, because when I compiled this with Visual C++, it rejected it; the declaration of the array size at runtime is invalid, same as it is with older C, because size is not a constant. 

Initializing Structs

I wrote an “Asteroids” game in C and used a small struct to hold game-level data:

struct level {
	int nums[4]; // how many of each size of asteroid
	int aliens; // how many aliens
	float factor; // from 1.0 to 1.5 - multiply asteroid speed by this
};

struct level levels[50];

So in C++, you might initialize the first two levels with this declaration:

level lvls[2] = {
	{ {0, 0, 0, 1}, 5,1.3f },
	{ {0, 0, 0, 2}, 5,1.35f }
};

C, though, has had a better initializer since C99:

struct level levels[2] = {
{ .factor = (float)1,.aliens = 1,.nums = { 0,0,3,3 } }, // Level 1
{ .factor = (float)1,.aliens = 0,.nums = { 0,1,3,3 } } } // Level 2

This is tagged initialization, and does not require the order of the data to match the order of fields in the struct, as the C++ initialization does.

You could use a C++ initializer list or a constructor to initialize the struct, as a struct and a class are the same (just with different levels of default access), but that would be even clunkier. You want to have data initialized implicitly, not needing code to do it.

The Restrict Keyword

This is an interesting case, because although it’s only a C99 feature, some C++ compilers actually allow it unofficially. This type of thing is not unusual: For many years, for example, some C compilers allowed the single line comment // even though it wasn’t a C feature.

The keyword restrict applies to pointers, and tells the compiler that, for the scope of the pointer, the target of the pointer will only be referenced by the pointer or copies of it. This lets the compiler perform some optimizations because the pointer isn’t being aliased, and checks for what can be left out. If you do access the referenced data elsewhere, then the results are undefined. 

Auto Keyword

Both languages have had auto for a long time; it referred to a storage class. Specifically, it meant variables declared in a function. It’s one of those things that nobody ever uses, so developers decided to reuse it in C++ 11 onwards as a way of declaring a variable and letting the compiler determine the type from the initial value.

If you know C#, then auto is used exactly as var in C#:

void test() {
	auto count = 0;
	auto fred = "My name";
...

Here the compiler determines ‘count’ is an int and ‘fred’ is a char *. 

You can’t have auto count; by itself, it must have an initial value that provides the type. And if you want ‘fred’ to be a std::string instead of char *, add an ‘s’ to the end of the string literal, like this:

auto fred = "My name"s;

This happened because the compiler picked the simplest type that matches, so it replaces auto with char *, not std::string.

Conclusion: C++ and C, Separating Slowly

For things such as initializing data, it is possible to link C-compiled objects into a C++ object, though it seems a bit of a messy way to do it.

I compiled most examples with VC++ 2019/GCC/G++. One thing that I’d not realised is that the Visual C++ compiler only supports the minimum subset of C99 needed, not the full C99, so no restrict (for example). 

I believe the reason for this is that you can now install Clang into Visual C++, so there’s no need for Microsoft to do further work on their C compiler; you can also install GCC or any other C++ compiler. 

Does it matter that C is now probably only 99 percent a subset of C++ instead of 100 percent? Probably not, but let’s see how this evolution progresses over the next few years.

2 Responses to “C++ and C: How These Languages are Diverging”

  1. Wow … lots of errors here.

    > For much of the past thirty-odd years, C++ has been regarded as a superset of C; any C program could be compiled by any C++ compiler.

    This was never true. C and C++ have always shared a common subset, but there has always been C code that was invalid in C++. Or, in some rare circumstances, valid but produces different results.

    > C++ has had std::vect for a number of years;

    I think you mean std::vector.

    > Since C11, C has had variable-length arrays whose size is determined at runtime.

    VLAs were added in C99 … and made *optional* in C11. That’s why Visual Studio rejected your code: they never bothered implementing it with C99, and now it’s optional so they don’t have to. C is moving away from this feature, so in someways it’s actually an example of how the two languages are converging if anything. Oops.

    Though you should have also talked about flexible array members, which are also a way of implementing std::vector-like dynamic arrays in C.

    > Initializing Structs

    What you’re referring to here is designated initializers, a feature that will be coming in C++20. So … another bad example here. This is a great example of how the languages share features, instead of diverging.

    > I compiled most examples with VC++ 2019/GCC/G++. One thing that I’d not realised is that the Visual C++ compiler only supports the minimum subset of C99 needed, not the full C99, so no restrict (for example).

    > I believe the reason for this is that you can now install Clang into Visual C++, so there’s no need for Microsoft to do further work on their C compiler; you can also install GCC or any other C++ compiler.

    MSVC doesn’t even include the minimum subset of C99, which would include restrict. This was a problem long before Clang ever existed — the real reason is Microsoft hasn’t traditionally been concerned with their C compiler, instead putting their development effort into C++. The only C99 upgrades they introduced were those that were brought in incidentally through the C++ compiler. I think this has changed somewhat in recent years, but it’s very telling that they have __restrict, but not restrict.

    > Does it matter that C is now probably only 99 percent a subset of C++ instead of 100 percent? Probably not, but let’s see how this evolution progresses over the next few years.

    Indeed — if you pay attention to the C2x proposals (the next version of C to be released in the the next few years), there are several that are essentially direct copies of features first introduced in C++, or otherwise designed to harmonize language features. There’s even one that was proposed to both standards bodies simultaneously, to make sure both languages got a compatible error handling mechanism. There has even been talk of adding type deduction (auto) to C.

    C and C++ will always be different languages — their design goals are just too different to reconcile. But features will continue to be shared between them as well. The C standard committee’s charter has had this ever since C99:

    > 10. Minimize incompatibilities with C++. The committee recognizes the need for a clear and defensible plan with regard to how it intends to address the compatibility issue with C++. The committee endorses the principle of maintaining the largest common subset clearly and from the outset. Such a principle should satisfy the requirement to maximize overlap of the languages while maintaining a distinction between them and allowing them to evolve separately.

    C and C++ are not diverging. Nor are they converging: they are on a parallel development path, sharing features all along the way.