in ,

C ++ is not a superset of C, Hacker News

Mon (September)

If you’re not familiar with both languages, you might have heard people say that C is a superset of C. If you’re experienced in both languages, you’ll know that this is not true at all.

Of course, C has many features that C does not; but there are also a few features that only C has. And, perhaps most importantly, there is code that compiles in both languages ​​but does different things.

There’s a lot of information about the differences between the two languages available, but a lot of it seems scattered. I wanted to have a go at creating a concise guide for the details that are often overlooked, with excerpts from the language standards to back these up.


This is primarily aimed at people who are familiar with at least one of C or C .

When I refer to C , I mean C 11 onwards, though much of this will apply to earlier standards. I’ll be referencing the C 17 standard1.

When I refer to C, I mean C 99 onwards. I’ll be referencing the C 11 standard2.

This is the category of differences that I think is most important. Not everything that C and C appear to share is as it seems.


The keywordconsthas a different semantic meaning in C than in C.

In both languages, the following produces undefined behavior:

1constintfoo=1;2int* bar=& foo;3* bar=2;

From C 17 1.7.1 paragraph 4:

any attempt to modify a const object during its lifetime results in  undefined behavior.

From C 11 6.7.3 paragraph 6:

If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.

But the following code will only compile in C , not C:

1constsize_tbuffer_size=5;2intbuffer [buffer_size];

The size of the array needs to be known at compile time. In C , aconstvariable can be aconstant expression, meaning it can be evaluated at compile time. In C, this is not the case, and we must instead use a pre-processor macro:

1# defineBUFFER_SIZE(5)2intbuffer [BUFFER_SIZE];

The keywordconstwas created for this very purpose by Bjarne Stroustrop(3): to reduce the need for macros. It was a surprise to me to learn thatconstoriginated in what would become C , and was then adopted by C. I had assumed thatconstcame from C, and C took the same concept and extended it in order to reduce the need for macros. I understand macros are embraced by C, but it seems a shame to deliberately reduce the usefulness ofconstwhen standardizing C.

Another difference is that file-scopeconstvariables have internal linkage by default in C . This is so that you can make aconstdeclaration in a header without having multiple definition errors(4)

Function declarations with no arguments

In C , this declares a function that takes no arguments. But in C, this declares a function that could take any number of arguments of any type.

From the C 11 standard paragraphs 10 and 14:

The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.

An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

So the following would be legit C:

12intfunc(intfoo,  (int)  bar) {3returnfoo   bar;4}
12# include"func.h"34intmain() {5returnfunc(5) ,6);6}

This would result in a compiler error in C :

main.c:  5: ******

:error:nomatchingfunctionforcalltofunc   returnfunc(5,6);          ~~~ ./func.h:2:5:note:candidatefunctionnotviable:  requires0arguments,but2wereprovided

The effect of name mangling

There are some common implementation details that allow us to take this further. On my Linux machine using Clang, the following C compiles and links (though the result would of course be undefined):

12intfunc(intfoo,  (int)  bar);
1# include234intfunc(floatfoo,floatbar) {5returnprintf("% f,% f n", foo, bar);6}
12# include"func.h"34intmain() {5returnfunc(5) ,6);6}

This does not compile in C . C compilers commonly use name mangling to enable function overloading. They “mangle” the names of functions in order to encode their arguments, e.g. by appending the argument types to the function name. Generally, C compilers just store the function name as the symbol. We can see this by comparing the symbol table offunc.owhen compiled as C and C .

As C:

╰─λobjdump-tfuncofunc.o:fileformatelf 64-x 8664SYMBOLTABLE:0000000000000000(ldf*ABS*0000000000000000fooC0000000000000000(ld.text.text0000000000000000(ld.rodata.str1.1.rodata.str1.10000000000000000gF.text0000000000000 02efunc0000000000000000*UND*printf

As C :

╰─λobjdump-tfuncofunc.o:fileformatelf 64-x 8664SYMBOLTABLE:0000000000000000(ldf*ABS*0000000000000000fooC0000000000000000(ld.text.text0000000000000000(ld.rodata.str1.1.rodata.str1.10000000000000000gF.text000000000000003b_ Z4funcff0000000000000000*UND*printf

These implementation details are not part of the standards, but I’d be surprised to see an implementation that did something wildly different.


I mostly include this for fun, as I think it’s not as well known as it could be.autois used for type-inference in C , but is also a C keyword, just one that I’ve never actually seen used.

autois used to declare something with automatic storage class. It’s rarely seen because this is the default storage class for all variables declared within a block. ********

In C, this would compile, albeit likely with warnings about implicit conversion:

1intmain() {2autox="actually an int";3returnx;4}

This is because if you leave out the type specifier, as we have, the type defaults to int. Here we are implicitly converted achararray toint.

In C this wouldn’t compile, as the type ofxis inferred to beconst char *:

error:cannotinitializereturnobjectoftype'int'withanlvalueoftype'const char *'    returnx;

Despite C being a very small language, and C being huge, there are a few features that C has that C does not.

Variable length arrays

VLAs allow you to define an array of automatic storage duration with variable length. E.g.

1voidf(intn) {2intarr [n];34}

VLAs were actually made optional in the C 11 standard, which makes them not very portable.

These aren’t part of C , probably in part because the C standard library relies heavily on dynamic memory allocation to create containers likestd :: vectorthat can be used similarly. There are reasons you might not want this dynamic allocation, but then perhaps you would not be using C .

Restricted pointers

C defines a third type qualifier (in addition toconstandvolatile):restrict(5). This is only used with pointers. Making a pointer restricted is telling the compiler “I will only access the underlying object via this pointer for the scope of this pointer “. Consequently it can’t be aliased. If you break this promise you will get undefined behavior.

This exists to aid optimization. A classic example ismemmovewhere you can tell the compiler that thesrcandDSTdo not overlap.

From C 11 6.7.3 paragraph 8:

An object that is accessed through a restrict-qualified pointer has a special association with that pointer. This association, defined in below, requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. 135) The intended use of the restrict qualifier (like the register storage class) is to promote optimization, and deleting all instances of the qualifier from all preprocessing translation units composing a conforming program does not change its meaning (i.e., observable behavior)

Restricted pointers aren’t part of the C standard but are actually supported as extensions by many compilers(6).

I’m suspicious ofrestrict. It seems like playing with fire, and anecdotally it seems common to run into compiler optimization bugs when using it because it’s exercised so little7. But it’s easy to be suspicious of something I’ve never actually used.

Designated initialisers

C 99 brought in an incredibly useful way to initialize structs, and I do not understand why it has not been adopted by C .

1typedefstruct{2floatred;3floatgreen;4floatblue;5} Color;6intmain() {8Color c={.red=0.1, .green=0.5, .blue=0.9};9return0;10}

In C you would have to initialize like this:Color c={0.1, 0.5, 0.9};which is harder to read and not robust to changes in the definition ofColor. You could instead define a constructor but why should we have to do this for a simple aggregate type? I heard a rumor designated initialisers are coming in C 20. It only took 21 Years …

Brave Browser


(Read More)

What do you think?

Leave a Reply

Your email address will not be published.

GIPHY App Key not set. Please check settings

Slumping EOS May Skyrocket Over 30% as Bears Near Exhaustion – CCN Markets, Crypto Coins News

Slumping EOS May Skyrocket Over 30% as Bears Near Exhaustion – CCN Markets, Crypto Coins News

China Drone Attack on Crop-Eating ‘Monster’ Shows 98% Kill Rate, Hacker News