57

This question was triggered by replie(s) to a post by Herb Sutter where he explained MS's decision to not support/make a C99 compiler but just go with the C(99) features that are in the C++(11) standard anyway.

One commenter replied:

(...) C is important and deserves at least a little bit of attention.

There is a LOT of existing code out there that is valid C but is not valid C++. That code is not likely to be rewritten (...)

Since I only program in MS C++, I really don't know "pure" C that well, i.e. I have no ready picture of what details of the C++-language I'm using are not in C(99) and I have little clues where some C99 code would not work as-is in a C++ compiler.

Note that I know about the C99 only restrict keyword which to me seems to have very narrow application and about variable-length-arrays (of which I'm not sure how widespread or important they are).

Also, I'm very interested whether there are any important semantic differences or gotchas, that is, C(99) code that will compile under C++(11) but do something differently with the C++ compiler than with the C compiler.


Quick links: External resources from the answers:

Cœur
  • 37,241
  • 25
  • 195
  • 267
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
  • 9
    Does C++11 support variable-length arrays yet? C99 does, so if not, that's a pretty good example. – Cody Gray - on strike May 05 '12 at 11:23
  • 2
    http://david.tribble.com/text/cdiffs.htm - a lot of that is still valid for C++11. @CodyGray: VLAs aren't part of C++11. – Mat May 05 '12 at 11:26
  • 1
    @CodyGray: C++11 does not support VLA, so your comment can be an answer :) – Alok Save May 05 '12 at 11:26
  • 2
    "Note that I know ... about variable-length-arrays". – Niklas B. May 05 '12 at 11:30
  • 2
    I saw that, and I felt like he couldn't possibly know what a variable-length array was if he was unable to imagine how they would be useful or important. :-) – Cody Gray - on strike May 05 '12 at 11:47
  • Well, I haven't read Herb Sutter's post, but they didn't even include the C99 parts that are in C++11, even in the new VC11, which I heard to claim to have a complete C++ standard library and yet it misses things like `` or the new floating point functions from ``. – Christian Rau May 05 '12 at 13:06

4 Answers4

32

There are a bunch of incompatibilities that have been around for ages (C90 or earlier), as well as a bunch of really nice features in C99 and C11. These are all off the top of my head.

// Valid C
int *array = malloc(sizeof(*array) * n);

// Valid C and valid C++, extra typing, it's always extra typing...
int *array = (int *) malloc(sizeof(*array) * n);

// Valid C++
int *array = new int[n];

C99 is nice and C programmers everywhere should use it

The new features in C99 are very nice for general programming. VLAs and restrict are not (in my opinion) targeted for general use, but mostly for bringing FORTRAN and numerical programmers to C (although restrict helps the autovectorizer). Since any conforming program that uses restrict will still work exactly the same way (but possibly not as fast) if you #define restrict at the top of the file, it's not a big deal. VLAs are pretty rare in the wild it seems.

Flexible array members can be nice. Note that these are NOT the same as variable-length arrays! People have been using this trick for years, but official support means less typing and it also allows us to make constants at compile time. (The old way was to have an array of size 1, but then computing the allocation size is a real bother.)

struct lenstr {
    unsigned length;
    char data[];
};
// compile time constant
const struct lenstr hello = { 12, "hello, world" };

Designated initializers. Saves a lot of typing.

struct my_struct { int a; char *b; int c; const char *d; };
struct my_struct x = {
    .a = 15,
    .d = "hello"
    // implicitly sets b = NULL and c = 0
};
int hex_digits[256] = { ['0'] = 0, ['1'] = 1, ['2'] = 2, /* etc */ ['f'] = 15 };

The inline keyword behaves differently, you can choose which translation unit gets a non-inline version of a function declared inline by adding an extern declaration to that unit.

Compound literals.

struct point { float x; float y; };
struct point xy_from_polar(float r, float angle)
{
    return (struct point) { cosf(angle) * r, sinf(angle) * r };
}

The snprintf function is probably in my top 10 most useful library functions in C. It's not only missing from C++, but the MSVC runtime only provides a function called _snprintf, which is not guaranteed to add a NUL terminator to the string. (snprintf is in C++11, but still conspicuously absent from the MSVC C runtime.)

Anonymous structures and unions (C11, but GCC extension since forever) (anonymous unions are apparently in C++03, no MSVC support in C mode):

struct my_value {
    int type;
    union {
        int as_int;
        double as_double;
    }; // no field name!
};

As you can see, many of these features just save you a lot of typing (compound literals), or make programs easier to debug (flexible array members), make it easier to avoid mistakes (designated initializers / forgetting to initialize structure fields). These aren't drastic changes.

For semantic differences, I'm sure the aliasing rules are different, but most compilers are forgiving enough these days I'm not sure how you'd construct a test case to demonstrate. The difference between C and C++ that everyone reaches for is the old sizeof('a') expression, which is always 1 for C++ but usually 4 on a 32-bit C system. But nobody cares what sizeof('a') is anyway. However, there are some guarantees in the C99 standard that codify existing practices.

Take the following code. It uses a common trick for defining union types in C without wasting extra storage. I think this is semantically valid C99 and I think this is semantically dubious C++, but I might be wrong.

#define TAG_FUNKY_TOWN 5
struct object { int tag; };
struct funky_town { int tag; char *string; int i; };
void my_function(void)
{
    struct object *p = other_function();
    if (p->tag == TAG_FUNKY_TOWN) {
        struct funky_town *ft = (struct funky_town *) p;
        puts(ft->string);
    }
}

It's a shame, though. The MSVC code generator is nice, too bad there's no C99 front-end.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 1
    C99 does not allow flexible array member initialization (it's a GNU extension). C++11 has uniform initialization, which is similar to compound literals: `return {cos(angle)*r,sin(angle)*r};`. anonymous unions are valid C++. I'm not sure that anonymous structs are even valid C though (and they seem kind of pointless to me). I think that that 'semantically dubious C++' is valid, but I might be wrong. – bames53 May 05 '12 at 14:57
  • 5
    Also C++ is nice and C programmers everywhere should use it. ;) – bames53 May 05 '12 at 15:03
  • "The inline keyword behaves differently, you can choose which translation unit gets a non-inline version of a function declared inline by adding an extern declaration to that unit.". And unfortunately you [**have** to](http://stackoverflow.com/questions/2217628/multiple-definition-of-inline-functions-when-linking-static-libs/2218034#2218034) choose :( – Johannes Schaub - litb May 05 '12 at 15:13
  • @litb: thanks to the preprocessor, it is possible to sanely make use of C99 inline semantics; like header guards, it adds a bit of boilerplate code (4 lines in the factoring I use), but that's the price of using C... – Christoph May 05 '12 at 16:19
  • 1
    Regarding the last example: it is valid C++11. When standard-layout types with a common leading sequence (here `tag`) are used in a `union`, then if one of them is set, the attributes that form the common leading sequence can be accessed through any of them. You can read about it in [class.mem] (note 17 from memory), it just stems, of course, from the fact that the compiler performs no magic when it comes to lay out types :) – Matthieu M. May 05 '12 at 16:53
  • +1 specifically for the canonical example of not having to cast `malloc`s return type! (I should have remembered that one.) – Martin Ba May 05 '12 at 17:01
  • 5
    @bames53: I don't go walking around C++ forums telling them to use C#, don't go walking around discussions about C telling people to use C++. – Dietrich Epp May 05 '12 at 22:42
  • Anonymous unions are in C++11, and I think they were in C++03; in 11 members of the union are called "variant members" of the enclosing class. Anonymous structs are not allowed, though. – Potatoswatter May 07 '12 at 08:39
  • @MatthieuM. - The last example has no `union`... And I am pretty sure that makes it _invalid_ C++ (accessing object via "wrong" pointer type). – Nemo Aug 15 '12 at 00:45
  • @Nemo: My comment was on May 5th, the post was edited on May 7th, the *last* example changed in-between. Mine had a `union`. – Matthieu M. Aug 17 '12 at 12:59
28

If you start from the common subset of C and C++, sometimes called clean C (which is not quite C90), you have to consider 3 types of incompatibilities:

  1. Additional C++ featues which make legal C illegal C++

    Examples for this are C++ keywords which can be used as identifiers in C or conversions which are implicit in C but require an explicit cast in C++.

    This is probably the main reason why Microsoft still ships a C frontend at all: otherwise, legacy code that doesn't compile as C++ would have to be rewritten.

  2. Additional C features which aren't part of C++

    The C language did not stop evolving after C++ was forked. Some examples are variable-length arrays, designated initializers and restrict. These features can be quite handy, but aren't part of any C++ standard, and some of them will probably never make it in.

  3. Features which are available in both C and C++, but have different semantics

    An example for this would be the linkage of const objects or inline functions.

A list of incompatibilities between C99 and C++98 can be found here (which has already been mentioned by Mat).

While C++11 and C11 got closer on some fronts (variadic macros are now available in C++, variable-length arrays are now an optional C language feature), the list of incompatibilities has grown as well (eg generic selections in C and the auto type-specifier in C++).

As an aside, while Microsoft has taken some heat for the decision to abandon C (which is not a recent one), as far as I know no one in the open source community has actually taken steps to do something about it: It would be quite possible to provide many features of modern C via a C-to-C++ compiler, especially if you consider that some of them are trivial to implement. This is actually possible right now using Comeau C/C++, which does support C99.

However, it's not really a pressing issue: Personally, I'm quite comfortable with using GCC and Clang on Windows, and there are proprietary alternatives to MSVC as well, eg Pelles C or Intel's compiler.

hugomg
  • 68,213
  • 24
  • 160
  • 246
Christoph
  • 164,997
  • 36
  • 182
  • 240
2

In C++, setting one member of a union and accessing the value of a different member is undefined behavior, whereas it's not undefined in C99.

There are lots of other differences listed on the wikipedia page.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • I thought this was also UB in C99 and it was only OpenCL C (which is based on C99) that losened this restriction a bit. – Christian Rau May 05 '12 at 16:17
  • The [wikipedia section}(http://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B#Constructs_valid_in_C_but_not_C.2B.2B) is indeed quite nice. I should make a habit of visiting wikipedia before asking here. :-) – Martin Ba May 07 '12 at 08:21
  • I think C++ offers "reinterpret_cast", however, which is defined behavior in cases where the pattern of bits in the original type has meaning in the destination type. C offers nothing similar. – supercat Mar 04 '16 at 14:15
  • 1
    @supercat no, most of the time `reinterpret_cast` between incompatible types violates strict aliasing rules, almost the same as in C. Even if all bits zero of an `unsigned int` have a meaning as a positive zero `float`, doing a `reinterpret_cast` on pointer to the former to pointer to the latter and using the result is UB. – Ruslan May 13 '17 at 14:54
2

I will mention "C.1 C++ and ISO C" of the C++11 standard. That document has a blow by blow of each difference and its impact on development.

emsr
  • 15,539
  • 6
  • 49
  • 62