Lably
Skip to content

Compatibility of C and C++

Comparison of programming languages

  • 18 min read
  • 3 chapters

Related on Lably

Through Compiler, Subset and Parameter (computer programming)

However, C is not a subset of C++, and nontrivial C programs will not compile as C++ code without modification. Likewise, C++ introduces many features that are not available in C and in practice almost all code written in C++ is not conforming C code. This article, however, focuses on differences that cause conforming C code to be ill-formed C++ code, or to be conforming/well-formed in both languages but to behave differently in C and C++.

Bjarne Stroustrup, the creator of C++, has suggested that the incompatibilities between C and C++ should be reduced as much as possible in order to maximize interoperability between the two languages. Others have argued that since C and C++ are two different languages, compatibility between them is useful but not vital; according to this camp, efforts to reduce incompatibility should not hinder attempts to improve each language in isolation. The official rationale for the 1999 C standard (C99) "endorse[d] the principle of maintaining the largest common subset" between C and C++ "while maintaining a distinction between them and allowing them to evolve separately", and stated that the authors were "content to let C++ be the big and ambitious language."

Several additions of C99 are not supported in the current C++ standard or conflicted with C++ features, such as variable-length arrays, native complex number types and the restrict type qualifier. On the other hand, C has gradually worked to reduce some other incompatibilities by incorporating C++ features such as single line comments with // and mixed declarations and code in C99, and adding attributes, type deduction with the auto keyword, and a nullptr constant in C23.

Constructs valid in C but not in C++

C++ enforces stricter typing rules (no implicit violations of the static type system), and initialization requirements (compile-time enforcement that in-scope variables do not have initialization subverted) than C, and so some valid C code is invalid in C++. A rationale for these is provided in Annex C.1 of the ISO C++ standard.

  • One commonly encountered difference is C being more weakly-typed regarding pointers. Specifically, C allows a void * pointer to be assigned to any pointer type without a cast, while C++ does not; this idiom appears often in C code using malloc() memory allocation, or in the passing of context pointers to the POSIX pthreads API, and other frameworks involving callbacks. For example, the following is valid in C but not C++:
    <span id="mwWg"></span><span class="kt" id="mwWw">void</span><span class="w" id="mwXA"> </span><span class="o" id="mwXQ">*</span><span class="n" id="mwXg">ptr</span><span class="p" id="mwXw">;</span> <span class="c1" id="mwYA">// Implicit conversion from void * to int *</span> <span class="kt" id="mwYQ">int</span><span class="w" id="mwYg"> </span><span class="o" id="mwYw">*</span><span class="n" id="mwZA">i</span><span class="w" id="mwZQ"> </span><span class="o" id="mwZg">=</span><span class="w" id="mwZw"> </span><span class="n" id="mwaA">ptr</span><span class="p" id="mwaQ">;</span>

    or similarly:

    <span id="mwbA"></span><span class="kt" id="mwbQ">int</span><span class="w" id="mwbg"> </span><span class="o" id="mwbw">*</span><span class="n" id="mwcA">j</span><span class="w" id="mwcQ"> </span><span class="o" id="mwcg">=</span><span class="w" id="mwcw"> </span><span class="n" id="mwdA">malloc</span><span class="p" id="mwdQ">(</span><span class="mi" id="mwdg">5</span><span class="w" id="mwdw"> </span><span class="o" id="mweA">*</span><span class="w" id="mweQ"> </span><span class="k" id="mweg">sizeof</span><span class="w" id="mwew"> </span><span class="o" id="mwfA">*</span><span class="n" id="mwfQ">j</span><span class="p" id="mwfg">);</span><span class="w" id="mwfw"> </span> <span class="c1" id="mwgA">// Implicit conversion from void * to int *</span>

    In order to make the code compile as both C and C++, one must use an explicit cast, as follows (with some caveats in both languages):

    <span id="mwhw"></span><span class="kt" id="mwiA">void</span><span class="o" id="mwiQ">*</span><span class="w" id="mwig"> </span><span class="n" id="mwiw">ptr</span><span class="p" id="mwjA">;</span> <span class="kt" id="mwjQ">int</span><span class="o" id="mwjg">*</span><span class="w" id="mwjw"> </span><span class="n" id="mwkA">i</span><span class="w" id="mwkQ"> </span><span class="o" id="mwkg">=</span><span class="w" id="mwkw"> </span><span class="p" id="mwlA">(</span><span class="kt" id="mwlQ">int</span><span class="o" id="mwlg">*</span><span class="p" id="mwlw">)</span><span class="n" id="mwmA">ptr</span><span class="p" id="mwmQ">;</span> <span class="kt" id="mwmg">int</span><span class="o" id="mwmw">*</span><span class="w" id="mwnA"> </span><span class="n" id="mwnQ">j</span><span class="w" id="mwng"> </span><span class="o" id="mwnw">=</span><span class="w" id="mwoA"> </span><span class="p" id="mwoQ">(</span><span class="kt" id="mwog">int</span><span class="o" id="mwow">*</span><span class="p" id="mwpA">)</span><span class="n" id="mwpQ">malloc</span><span class="p" id="mwpg">(</span><span class="mi" id="mwpw">5</span><span class="w" id="mwqA"> </span><span class="o" id="mwqQ">*</span><span class="w" id="mwqg"> </span><span class="k" id="mwqw">sizeof</span><span class="w" id="mwrA"> </span><span class="o" id="mwrQ">*</span><span class="n" id="mwrg">j</span><span class="p" id="mwrw">);</span>
  • C++ has more complicated rules about pointer assignments that add qualifiers as it allows the assignment of int ** to const int *const * but not the unsafe assignment to const int ** while C allows neither of those (although compilers will usually only emit a warning).
  • C++ changes some C standard library functions to add additional overloaded functions with const type qualifiers, e.g. strchr returns char * in C, while C++ acts as if there were two overloaded functions (for char * and const char *). In C23 generic selection is used to make C's behaviour more similar to C++'s.
  • C++ is also more strict in conversions to enums: ints cannot be implicitly converted to enums as in C. Also, prior to C23, enumeration constants (enum enumerators) were always of type int in C, whereas they are distinct types in C++ (and C23 or later) and may have a size different from that of int.
  • In C++ a const variable must be initialized; in C this is not necessary.
  • C++ compilers prohibit goto or switch from crossing an initialization, as in the following C99 code:
    <span id="mwtg"></span><span class="kt" id="mwtw">void</span><span class="w" id="mwuA"> </span><span class="nf" id="mwuQ">fn</span><span class="p" id="mwug">(</span><span class="kt" id="mwuw">void</span><span class="p" id="mwvA">)</span><span class="w" id="mwvQ"> </span><span class="p" id="mwvg">{</span> <span class="w" id="mwvw"> </span><span class="k" id="mwwA">goto</span><span class="w" id="mwwQ"> </span><span class="n" id="mwwg">flack</span><span class="p" id="mwww">;</span> <span class="w" id="mwxA"> </span><span class="kt" id="mwxQ">int</span><span class="w" id="mwxg"> </span><span class="n" id="mwxw">i</span><span class="w" id="mwyA"> </span><span class="o" id="mwyQ">=</span><span class="w" id="mwyg"> </span><span class="mi" id="mwyw">1</span><span class="p" id="mwzA">;</span> <span class="nl" id="mwzQ">flack</span><span class="p" id="mwzg">:</span> <span class="w" id="mwzw"> </span><span class="n" id="mw0A">printf</span><span class="p" id="mw0Q">(</span><span class="s" id="mw0g">"Reached flack"</span><span class="p" id="mw0w">);</span> <span class="p" id="mw1A">}</span>
  • While syntactically valid, a longjmp() results in undefined behaviour in C++ if the jumped-over stack frames include objects with nontrivial destructors. The C++ implementation is free to define the behaviour such that destructors would be called. However, this would preclude some uses of longjmp() which would otherwise be valid, such as implementation of threads or coroutines switching between separate call stacks with longjmp() — when jumping from the lower to the upper call stack in global address space, destructors would be called for every object in the lower call stack. No such issue exists in C.
  • C allows for multiple tentative definitions of a single global variable in a single translation unit, which is invalid as an ODR violation in C++.
    <span id="mw2w"></span><span class="kt" id="mw3A">int</span><span class="w" id="mw3Q"> </span><span class="n" id="mw3g">n</span><span class="p" id="mw3w">;</span> <span class="kt" id="mw4A">int</span><span class="w" id="mw4Q"> </span><span class="n" id="mw4g">n</span><span class="w" id="mw4w"> </span><span class="o" id="mw5A">=</span><span class="w" id="mw5Q"> </span><span class="mi" id="mw5g">10</span><span class="p" id="mw5w">;</span>
  • In C, declaring a new type with the same name as an existing struct, union or enum is valid, but it is invalid in C++, because in C, struct, union, and enum types must be indicated as such whenever the type is referenced whereas in C++, all declarations of such types carry the typedef implicitly.
    <span id="mw6g"></span><span class="k" id="mw6w">enum</span><span class="w" id="mw7A"> </span><span class="n" id="mw7Q">Bool</span><span class="w" id="mw7g"> </span><span class="p" id="mw7w">{</span><span class="w" id="mw8A"> </span><span class="n" id="mw8Q">FALSE</span><span class="p" id="mw8g">,</span><span class="w" id="mw8w"> </span><span class="n" id="mw9A">TRUE</span><span class="w" id="mw9Q"> </span><span class="p" id="mw9g">};</span> <span class="k" id="mw9w">typedef</span><span class="w" id="mw-A"> </span><span class="kt" id="mw-Q">int</span><span class="w" id="mw-g"> </span><span class="n" id="mw-w">Bool</span><span class="p" id="mw_A">;</span> <span class="c1" id="mw_Q">// in C, Bool and enum Bool are distinct</span>
  • Non-prototype ("K&R-style") function declarations are invalid in C++; they are still valid in C until C23, although they have been deemed obsolescent since C's original standardization in 1990. (The term "obsolescent" is a defined term in the ISO C standard, meaning a feature that "may be considered for withdrawal in future revisions" of the standard.) Similarly, implicit function declarations (using functions that have not been declared) are not allowed in C++, and have been invalid in C since 1999.
  • In C (prior to C23), a function declaration without parameters, e.g. int foo();, specifies that the parameters are unspecified. Therefore, it is legal to call such a function with one or more arguments, e.g. foo(42, "hello world"). In contrast, in C++ a function prototype without arguments means that the function takes no arguments, and calling such a function with arguments is ill-formed. In C, the correct way to declare a function that takes no arguments is by using void, as in int foo(void);, which is also valid in C++; since C23, int foo(); is guaranteed to take no parameters. Empty function prototypes are a deprecated feature in C99 (as they were in C89).
  • In both C and C++, one can define nested struct types, but the scope is interpreted differently: in C++, a nested struct is defined only within the scope/namespace of the outer struct, whereas in C the inner struct is also defined outside the outer struct.
  • C allows struct, union, and enum types to be declared in function prototypes, whereas C++ does not.

C99 and C11 added several additional features to C that have not been incorporated into standard C++ as of C++20, such as complex numbers, variable length arrays (complex numbers and variable length arrays are designated as optional extensions in C11), flexible array members, the restrict keyword, array parameter qualifiers, and compound literals.

  • Complex arithmetic using the float complex and double complex primitive data types was added in the C99 standard, via the _Complex keyword and complex convenience macro. In C++, complex arithmetic can be performed using the complex number class, but the two methods are not code-compatible. (The standards since C++11 require binary compatibility, however.)
  • Variable length arrays. This feature leads to possibly non-compile time sizeof operator.
    <span id="mwATU"></span><span class="kt" id="mwATY">void</span><span class="w" id="mwATc"> </span><span class="nf" id="mwATg">foo</span><span class="p" id="mwATk">(</span><span class="kt" id="mwATo">size_t</span><span class="w" id="mwATs"> </span><span class="n" id="mwATw">x</span><span class="p" id="mwAT0">,</span><span class="w" id="mwAT4"> </span><span class="kt" id="mwAT8">int</span><span class="w" id="mwAUA"> </span><span class="n" id="mwAUE">a</span><span class="p" id="mwAUI">[</span><span class="o" id="mwAUM">*</span><span class="p" id="mwAUQ">]);</span><span class="w" id="mwAUU"> </span><span class="c1" id="mwAUY">// VLA declaration</span> <span class="kt" id="mwAUc">void</span><span class="w" id="mwAUg"> </span><span class="nf" id="mwAUk">foo</span><span class="p" id="mwAUo">(</span><span class="kt" id="mwAUs">size_t</span><span class="w" id="mwAUw"> </span><span class="n" id="mwAU0">x</span><span class="p" id="mwAU4">,</span><span class="w" id="mwAU8"> </span><span class="kt" id="mwAVA">int</span><span class="w" id="mwAVE"> </span><span class="n" id="mwAVI">a</span><span class="p" id="mwAVM">[</span><span class="n" id="mwAVQ">x</span><span class="p" id="mwAVU">])</span><span class="w" id="mwAVY"> </span><span class="p" id="mwAVc">{</span> <span class="w" id="mwAVg"> </span><span class="n" id="mwAVk">printf</span><span class="p" id="mwAVo">(</span><span class="s" id="mwAVs">"%zu</span><span class="se" id="mwAVw">\n</span><span class="s" id="mwAV0">"</span><span class="p" id="mwAV4">,</span><span class="w" id="mwAV8"> </span><span class="k" id="mwAWA">sizeof</span><span class="w" id="mwAWE"> </span><span class="n" id="mwAWI">a</span><span class="p" id="mwAWM">);</span><span class="w" id="mwAWQ"> </span><span class="c1" id="mwAWU">// same as sizeof(int *)</span> <span class="w" id="mwAWY"> </span><span class="kt" id="mwAWc">char</span><span class="w" id="mwAWg"> </span><span class="n" id="mwAWk">s</span><span class="p" id="mwAWo">[</span><span class="n" id="mwAWs">x</span><span class="w" id="mwAWw"> </span><span class="o" id="mwAW0">*</span><span class="w" id="mwAW4"> </span><span class="mi" id="mwAW8">2</span><span class="p" id="mwAXA">];</span> <span class="w" id="mwAXE"> </span><span class="n" id="mwAXI">printf</span><span class="p" id="mwAXM">(</span><span class="s" id="mwAXQ">"%zu</span><span class="se" id="mwAXU">\n</span><span class="s" id="mwAXY">"</span><span class="p" id="mwAXc">,</span><span class="w" id="mwAXg"> </span><span class="k" id="mwAXk">sizeof</span><span class="w" id="mwAXo"> </span><span class="n" id="mwAXs">s</span><span class="p" id="mwAXw">);</span><span class="w" id="mwAX0"> </span><span class="c1" id="mwAX4">// will print the value of x * 2</span> <span class="p" id="mwAX8">}</span>
  • C99 allows declaration of function macros with empty arguments. For example, macro #define ADD(A, B) (A + B) used as ADD(4, ) will be expanded into (4 + ).
  • The last member of a C99 structure type with more than one member may be a flexible array member, which takes the syntactic form of an array with unspecified length. This serves a purpose similar to variable-length arrays, but VLAs cannot appear in type definitions, and unlike VLAs, flexible array members have no defined size. ISO C++ has no such feature. Example:
    <span id="mwAYs"></span><span class="k" id="mwAYw">struct</span><span class="w" id="mwAY0"> </span><span class="nc" id="mwAY4">X</span><span class="w" id="mwAY8"> </span><span class="p" id="mwAZA">{</span> <span class="w" id="mwAZE"> </span><span class="kt" id="mwAZI">int</span><span class="w" id="mwAZM"> </span><span class="n" id="mwAZQ">m</span><span class="p" id="mwAZU">;</span> <span class="w" id="mwAZY"> </span><span class="kt" id="mwAZc">long</span><span class="w" id="mwAZg"> </span><span class="n" id="mwAZk">n</span><span class="p" id="mwAZo">;</span> <span class="w" id="mwAZs"> </span><span class="kt" id="mwAZw">char</span><span class="w" id="mwAZ0"> </span><span class="n" id="mwAZ4">bytes</span><span class="p" id="mwAZ8">[];</span> <span class="p" id="mwAaA">};</span>
  • The restrict type qualifier defined in C99 was not included in the C++03 standard, but most mainstream compilers such as the GNU Compiler Collection, Microsoft Visual C++, and Intel C++ Compiler provide similar functionality as an extension.
  • Array parameter qualifiers in functions are supported in C but not C++.
    <span id="mwAac"></span><span class="kt" id="mwAag">int</span><span class="w" id="mwAak"> </span><span class="nf" id="mwAao">foo</span><span class="p" id="mwAas">(</span><span class="kt" id="mwAaw">int</span><span class="w" id="mwAa0"> </span><span class="n" id="mwAa4">a</span><span class="p" id="mwAa8">[</span><span class="k" id="mwAbA">const</span><span class="p" id="mwAbE">]);</span><span class="w" id="mwAbI"> </span><span class="c1" id="mwAbM">// equivalent to int *const a </span> <span class="kt" id="mwAbQ">int</span><span class="w" id="mwAbU"> </span><span class="nf" id="mwAbY">bar</span><span class="p" id="mwAbc">(</span><span class="kt" id="mwAbg">char</span><span class="w" id="mwAbk"> </span><span class="n" id="mwAbo">s</span><span class="p" id="mwAbs">[</span><span class="k" id="mwAbw">static</span><span class="w" id="mwAb0"> </span><span class="mi" id="mwAb4">5</span><span class="p" id="mwAb8">]);</span><span class="w" id="mwAcA"> </span><span class="c1" id="mwAcE">// annotates that s is at least 5 chars long</span>
  • The functionality of compound literals in C is generalized to both built-in and user-defined types by the list initialization syntax of C++11, although with some syntactic and semantic differences. In C:
    <span id="mwAcQ"></span><span class="k" id="mwAcU">struct</span><span class="w" id="mwAcY"> </span><span class="nc" id="mwAcc">X</span><span class="w" id="mwAcg"> </span><span class="n" id="mwAck">a</span><span class="w" id="mwAco"> </span><span class="o" id="mwAcs">=</span><span class="w" id="mwAcw"> </span><span class="p" id="mwAc0">(</span><span class="k" id="mwAc4">struct</span><span class="w" id="mwAc8"> </span><span class="nc" id="mwAdA">X</span><span class="p" id="mwAdE">){</span><span class="mi" id="mwAdI">4</span><span class="p" id="mwAdM">,</span><span class="w" id="mwAdQ"> </span><span class="mi" id="mwAdU">6</span><span class="p" id="mwAdY">};</span><span class="w" id="mwAdc"> </span><span class="c1" id="mwAdg">// The equivalent in C++ would be X{4, 6}. The C syntactic form used in C99 is supported as an extension in the GCC and Clang C++ compilers.</span> <span class="n" id="mwAdk">foo</span><span class="p" id="mwAdo">(</span><span class="o" id="mwAds">&</span><span class="p" id="mwAdw">(</span><span class="k" id="mwAd0">struct</span><span class="w" id="mwAd4"> </span><span class="nc" id="mwAd8">X</span><span class="p" id="mwAeA">){</span><span class="mi" id="mwAeE">4</span><span class="p" id="mwAeI">,</span><span class="w" id="mwAeM"> </span><span class="mi" id="mwAeQ">6</span><span class="p" id="mwAeU">});</span><span class="w" id="mwAeY"> </span><span class="c1" id="mwAec">// The object is allocated in the stack and its address can be passed to a function. This is not supported in C++.</span> <span class="k" id="mwAeg">if</span><span class="w" id="mwAek"> </span><span class="p" id="mwAeo">(</span><span class="n" id="mwAes">memcmp</span><span class="p" id="mwAew">(</span><span class="n" id="mwAe0">d</span><span class="p" id="mwAe4">,</span><span class="w" id="mwAe8"> </span><span class="p" id="mwAfA">(</span><span class="kt" id="mwAfE">int</span><span class="p" id="mwAfI">[]){</span><span class="mi" id="mwAfM">8</span><span class="p" id="mwAfQ">,</span><span class="w" id="mwAfU"> </span><span class="mi" id="mwAfY">6</span><span class="p" id="mwAfc">,</span><span class="w" id="mwAfg"> </span><span class="mi" id="mwAfk">7</span><span class="p" id="mwAfo">,</span><span class="w" id="mwAfs"> </span><span class="mi" id="mwAfw">5</span><span class="p" id="mwAf0">,</span><span class="w" id="mwAf4"> </span><span class="mi" id="mwAf8">3</span><span class="p" id="mwAgA">,</span><span class="w" id="mwAgE"> </span><span class="mi" id="mwAgI">0</span><span class="p" id="mwAgM">,</span><span class="w" id="mwAgQ"> </span><span class="mi" id="mwAgU">9</span><span class="p" id="mwAgY">},</span><span class="w" id="mwAgc"> </span><span class="n" id="mwAgg">n</span><span class="p" id="mwAgk">)</span><span class="w" id="mwAgo"> </span><span class="o" id="mwAgs">==</span><span class="w" id="mwAgw"> </span><span class="mi" id="mwAg0">0</span><span class="p" id="mwAg4">)</span><span class="w" id="mwAg8"> </span><span class="p" id="mwAhA">{</span> <span class="w" id="mwAhE"> </span><span class="c1" id="mwAhI">// ...</span> <span class="p" id="mwAhM">}</span>

    In C++:

    <span id="mwAhY"></span><span class="k" id="mwAhc">using</span><span class="w" id="mwAhg"> </span><span class="n" id="mwAhk">Digits</span><span class="w" id="mwAho"> </span><span class="o" id="mwAhs">=</span><span class="w" id="mwAhw"> </span><span class="kt" id="mwAh0">int</span><span class="p" id="mwAh4">[];</span><span class="w" id="mwAh8"> </span> <span class="k" id="mwAiA">if</span><span class="w" id="mwAiE"> </span><span class="p" id="mwAiI">(</span><span class="n" id="mwAiM">std</span><span class="o" id="mwAiQ">::</span><span class="n" id="mwAiU">memcmp</span><span class="p" id="mwAiY">(</span><span class="n" id="mwAic">d</span><span class="p" id="mwAig">,</span><span class="w" id="mwAik"> </span><span class="n" id="mwAio">Digits</span><span class="p" id="mwAis">{</span><span class="mi" id="mwAiw">8</span><span class="p" id="mwAi0">,</span><span class="w" id="mwAi4"> </span><span class="mi" id="mwAi8">6</span><span class="p" id="mwAjA">,</span><span class="w" id="mwAjE"> </span><span class="mi" id="mwAjI">7</span><span class="p" id="mwAjM">,</span><span class="w" id="mwAjQ"> </span><span class="mi" id="mwAjU">5</span><span class="p" id="mwAjY">,</span><span class="w" id="mwAjc"> </span><span class="mi" id="mwAjg">3</span><span class="p" id="mwAjk">,</span><span class="w" id="mwAjo"> </span><span class="mi" id="mwAjs">0</span><span class="p" id="mwAjw">,</span><span class="w" id="mwAj0"> </span><span class="mi" id="mwAj4">9</span><span class="p" id="mwAj8">},</span><span class="w" id="mwAkA"> </span><span class="n" id="mwAkE">n</span><span class="p" id="mwAkI">)</span><span class="w" id="mwAkM"> </span><span class="o" id="mwAkQ">==</span><span class="w" id="mwAkU"> </span><span class="mi" id="mwAkY">0</span><span class="p" id="mwAkc">)</span><span class="w" id="mwAkg"> </span><span class="p" id="mwAkk">{</span> <span class="w" id="mwAko"> </span><span class="c1" id="mwAks">// ...</span> <span class="p" id="mwAkw">}</span>
  • Designated initializers for arrays are valid only in C:
    <span id="mwAk8"></span><span class="kt" id="mwAlA">char</span><span class="w" id="mwAlE"> </span><span class="n" id="mwAlI">s</span><span class="p" id="mwAlM">[</span><span class="mi" id="mwAlQ">20</span><span class="p" id="mwAlU">]</span><span class="w" id="mwAlY"> </span><span class="o" id="mwAlc">=</span><span class="w" id="mwAlg"> </span><span class="p" id="mwAlk">{</span><span class="w" id="mwAlo"> </span><span class="p" id="mwAls">[</span><span class="mi" id="mwAlw">0</span><span class="p" id="mwAl0">]</span><span class="w" id="mwAl4"> </span><span class="o" id="mwAl8">=</span><span class="w" id="mwAmA"> </span><span class="sc" id="mwAmE">'a'</span><span class="p" id="mwAmI">,</span><span class="w" id="mwAmM"> </span><span class="p" id="mwAmQ">[</span><span class="mi" id="mwAmU">8</span><span class="p" id="mwAmY">]</span><span class="w" id="mwAmc"> </span><span class="o" id="mwAmg">=</span><span class="w" id="mwAmk"> </span><span class="sc" id="mwAmo">'g'</span><span class="w" id="mwAms"> </span><span class="p" id="mwAmw">};</span><span class="w" id="mwAm0"> </span><span class="c1" id="mwAm4">// allowed in C, not in C++</span>
  • Functions that do not return can be annotated using a [[noreturn]] attribute in C++ whereas C uses a distinct keyword, _Noreturn. In C23, the attribute syntax is adopted while _Noreturn is deprecated.
  • C29 introduces named loops and named break statements (similar to Java). This feature is proposed for inclusion into C++29.
  • C29 introduces case ranges (case 0...9: covers all cases from 0 to 9, inclusive). This feature is proposed for inclusion into C++29.
  • C uses a _Generic selection for macros to allow for compile-time deciding of expressions, which can be seen as emulating function overloading. C++ has no need for this as C++ supports function overloading and template specialization.
  • C23 introduces the typeof operator which produces a type of either a type or an expression. In C++, the equivalent feature is decltype.
  • C++ adds numerous additional keywords to support its new features. This renders C code using those keywords for identifiers invalid in C++. For example, the following snippet is valid C code, but is rejected by a C++ compiler, since template, new and class are keywords in C++.
    <span id="mwAow"></span><span class="k" id="mwAo0">struct</span><span class="w" id="mwAo4"> </span><span class="nc" id="mwAo8">template</span><span class="w" id="mwApA"> </span><span class="p" id="mwApE">{</span> <span class="w" id="mwApI"> </span><span class="kt" id="mwApM">int</span><span class="w" id="mwApQ"> </span><span class="n" id="mwApU">new</span><span class="p" id="mwApY">;</span> <span class="w" id="mwApc"> </span><span class="k" id="mwApg">struct</span><span class="w" id="mwApk"> </span><span class="nc" id="mwApo">template</span><span class="w" id="mwAps"> </span><span class="o" id="mwApw">*</span><span class="n" id="mwAp0">class</span><span class="p" id="mwAp4">;</span> <span class="p" id="mwAp8">};</span>

Sign in to keep reading

2 more chapters are free with a Lably account.

More in Computing and communication