Incompatibilities Between C & C++ (David R
Incompatibilities Between C & C++ (David R
Incompatibilities Between C & C++ (David R
Contents
• Introduction
• C++ versus C
• Changes to C99 versus C++98
o Aggregate Initializers
o Comments
o Conditional expression declarations
o Digraph punctuation tokens
o Implicit function declarations
o Implicit variable declarations
o Intermixed declarations and statements
• C99 versus C++98
o Alternate punctuation token spellings
o Array parameter qualifiers
o Boolean type
o Character literals
o clog identifier
o Comma operator results
o Complex floating-point type
o Compound literals
o const linkage
o Designated initializers
o Duplicate typedefs
o Dynamic sizeof evaluation
o Empty parameter lists
o Empty preprocessor function macro arguments
o Enumeration constants
o Enumeration declarations with trailing comma
o Enumeration types
o Flexible array members
o Function name mangling
o Function pointers
o Hexadecimal floating-point literals
o IEC 60559 arithmetic support
o Inline functions
o Integer types headers
o Library function prototypes
o Library header files
o long long integer type
o Nested structure tags
o Non-prototype function declarations
o Old-style casts
o One definition rule
o _Pragma keyword
o Predefined identifiers
o Reserved keywords in C99
o Reserved keywords in C++
o restrict keyword
o Returning void
o static linkage
o String initializers
o String literals are const
o Structures declared in function prototypes
o Type-generic math functions
o Typedefs versus type tags
o Variable-argument function declarators
o Variable-argument preprocessor function macros
o Variable-length arrays
o Void pointer assignments
o Wide character type
• References
• Acknowledgments
• Revision History
• Bottom
Introduction
The C programming language began to be standardized some time around 1985 by the
ANSI X3J9 committee. Several years of effort went by, and in 1989 ANSI approved
the new standard. An ISO committee ratified it a year later in 1990 after adding an
amendment dealing with internationalization issues. The 1989 C standard is known
officially as ANSI/ISO 9899-1989, Programming Languages - C, and this
document refers to the 1989 C standard as C89. The 1990 ISO revision of the
standard is known officially as ISO/IEC 9899-1990, Programming Languages - C,
which is referred to in this document as "C90".
The next version of the C standard was ratified by ISO in 1999. Officially know as
ISO/IEC 9899-1999, Programming Languages - C, it is referred to in this document
as "C99".
The C++ programming language was based on the C programming language as it
existed shortly after the ANSI C standardization effort had begun. Around 1995 an
ISO committee was formed to standardize C++, and the new standard was ratified in
1998, which is officially known as ISO/IEC 14882-1998, Programming Languages
- C++. It is referred to in this document as "C++98" or simply as "C++".
Though the two languages share a common heritage, and though the designers
involved in the standardization processes for each language tried to keep them as
compatible as possible, some incompatibilities unavoidably arose. Once the
programmer is aware of these potential problem spots, they are easy, for the most part,
to avoid when writing C code.
When we say that C is incompatible with C++ with respect to a specific language
feature, we mean that a C program that employs that feature either is not valid C++
code and thus will not compile as a C++ program, or that it will compile as a C++
program but will exhibit different behavior than the same program compiled as a C
program. In other words, an incompatible C feature is valid as C code but not as C++
code. All incompatibilities of this kind are addressed in this document. Avoiding these
kinds of incompatibilities allows the programmer to write correct C code that is
intended to interact with, or be compiled as, C++ code.
Another form of incompatible feature is one that is valid when used in a C++
program but is invalid in a C program. We call this an incompatible C++ feature.
Huge portions of the C++ language fall into this category (e.g., classes, templates,
exceptions, references, member functions, anonymous unions, etc.), so very few of
these kinds of incompatibilities are addressed in this document.
Yet another form of incompatible feature occurs when a C++ program uses a feature
that has the same name as a C90 feature but which has a different usage or meaning in
C. This document covers these kinds of incompatibilities.
This document lists only the incompatibilities between C99 and C++98.
(Incompatibilities between C90 and C++ have been documented elsewhere; see
Appendix B of Stroustrup [STR], for example.)
New additions to the C99 standard library are also not addressed in this document
unless they specifically introduce C++ incompatibilities.
C++ versus C
As discussed in the Introduction, no attempt is made in this document to cover
incompatible C++ features, i.e., features of the C++ language or library that are not
supported in C. Huge portions of C++ and its library fall into this category. A partial
list of these features includes:
anonymous unions
classes
constructors and destructors
exceptions and try/catch blocks
external function linkages (e.g., extern "C")
function overloading
member functions
namespaces
new and delete operators and functions
operator overloading
reference types
standard template library (STL)
template classes
template functions
• Aggregate Initializers
C90 requires automatic and register variables of aggregate type (struct, array,
or union) to have initializers containing only constant expressions. (Many
compilers do not adhere to this restriction, however.)
For example:
[C99: §6.7.8]
[C++98: §3.7.2, 8.5, 8.5.1]
• Comments
i = (x//*y*/z++
, w);
[C99: §6.8.5]
[C++98: §3.3.2, 6.4, 6.5]
<: [
:> ]
<% {
%> }
%: #
%:%: ##
%:include <stdio.h>
%:ifndef BUFSIZE
%:define BUFSIZE 512
%:endif
[C99: §6.4.6]
[C++98: §2.5, 2.12]
C90 allows a function to be implicitly declared at the point of its first use
(call), assigning it a return type of int by default. For example:
void foo(void)
{
bar(); /* Implicit declaration: extern int bar() */
}
[C99: §6.5.2.2]
[C++98: §5.2.2]
C99 does not allow this omission, and neither does C++.
The following code is valid in C90, but invalid in C99 and C++:
struct info
{
const char * name;
const sz; /* Implicit int, error */
};
return (i + j);
}
[C99: §6.7, 6.7.2]
[C++98: §7, 7.1.5]
C90 syntax specifies that all the declarations within a block must appear
before the first statement in the block.
C++ does not have this restriction, allowing statements and declarations to
appear in any order within a block.
void prefind(void)
{
int i;
s = arr[i];
prepend(s);
}
[C99: §6.8.2]
[C++98: §6, 6.3, 6.7]
Note that features that are specific to C++ and which are not legal C (e.g., class
member function declarations) are not included in this section; only language features
that are common to both C and C++ are discussed. Most of the features are valid as C
but invalid as C++.
C90 does not have these built-in keywords, but it does provide a standard
<iso646.h> header file that contains definitions for the same words as
macros, behaving almost like built-in keywords.
#ifndef __cplusplus
#include <iso646.h>
#endif
[C99: §7.9]
[C++98: §2.5, 2.11]
C99 provides new declaration syntax for function parameters of array types,
allowing type qualifiers (the cv-qualifiers const and volatile, and
restrict) to be included within the first set of brackets of an array declarator.
The qualifier modifies the type of the array parameter itself. For example, the
following declarations are semantically identical:
C99 also allows the static specifier to be placed within the brackets of an
array declaration immediately preceding the expression specifying the size of
the array. The presence of such a specifer indicates that the array is composed
of at least the number of contiguous elements indicated by the size expression.
(Presumably this is a hint to the compiler for optimizing access to elements of
the array.) For example:
• Boolean type
C99 supports the _Bool keyword, which declares a two-valued integer type
(capable of representing the values true and false). It also provides a
standard <stdbool.h> header that contains definitions for the following
macros:
C++ provides bool, false, and true as reserved keywords and implements
bool as a true built-in boolean type.
C programs that do not include the <stdbool.h> header are free to use these
keywords as identifiers and macro names, which may cause compatibility
problems when such code is compiled as C++. For example:
(It is likely that an empty <stdbool.h> header will be provided by most C++
implementations as an extension.)
• Character literals
In C, character literals such as 'a' have type int, and thus sizeof('a') is
equal to sizeof(int).
In C++, character literals have type char, and thus sizeof('a') is equal to
sizeof(char).
This difference can lead to inconsistent behavior in some code that is compiled
as both C and C++.
[C99: §6.4.4.4]
[C++98: §2.13.2]
• clog identifier
// C++ code
#include <iostream>
using std::clog;
void foo(void)
{
clog << clog(2.718281828) << endl;
// Possible conflict
}
Including both the <iostream> and the <cmath> headers in C++ code places
both clog names into the std:: namespace, one being a variable and the other
being a function, which should not cause any conflicts.
// C++ code
#include <iostream>
#include <cmath>
void foo(void)
{
std::clog << std::clog(2.718281828) << endl;
// No conflict; different
types
}
void bar(void)
{
complex double (* fp)(complex double);
fp = &std::clog; // Unambiguous
}
It would appear that the safest approach to this potential conflict would be to
avoid using both forms of clog within the same source file.
[C99: §7.3.7.2]
[C++98: §27.3.1]
The comma operator in C always results in an r-value even if its right operand
is an l-value, while in C++ the comma operator will result in an l-value if its
right operand is an l-value. This means that certain expressions are valid in
C++ but not in C:
int i;
int j;
C99 provides built-in complex and imaginary floating point types, which are
declared using the _Complex and _Imaginary keywords.
There are exactly three complex types and three imaginary types in C99:
_Complex float
_Complex double
_Complex long double
_Imaginary long double
_Imaginary double
_Imaginary long double
C code that does not include this header is free to use these words as
identifiers and macro names. This was an intentional part of the design of the
_Complex and _Imaginary keywords, since this allows existing code that
employs the new words to continue working as it did before under C89.
Implicit widening conversions between the complex and imaginary types are
provided, which parallel the implicit widening conversions between the non-
complex floating point types.
// C99 code
#include <complex.h>
C++ supports more complex types than C99, in theory, since complex is a
template class.
// C++ code
#include <complex>
complex<float> square(complex<float> a)
{
return (a * a);
}
complex<int> square(complex<int> a)
{
return (a * a);
}
It is possible to define typedefs that will work in both C99 and C++, albeit
with some limitations:
#ifdef __cplusplus
#include <complex>
#else
#include <complex.h>
#endif
Including these definitions allows for portable code that will compile as both
C and C++ code, such as:
complex_double square_cd(complex_double a)
{
return (a * a);
}
• Compound literals
C99 allows literals having types other than primitive types (e.g., user-defined
structure or array types) to be specified in constant expressions; these are
called compound literals. For example:
struct info
{
char name[8+1];
int type;
};
void predef(void)
{
add((struct info){ "e", 0 }); // A struct literal
move((float[2]){ +0.5, -2.7 }); // An array literal
}
C++ does provides a similar capability through the use of non-default class
constructors, but which is not quite as flexible as the C feature:
void predef2()
{
add(info("e", 0)); // Call constructor
info::info()
}
• const linkage
C++ specifies that a const object with file scope has internal linkage by
default, meaning that the object's name is not visible outside the source file in
which it is declared. A const object must be declared with an explicit extern
specifier in order to be visible to other source modules.
• Designated initializers
C99 introduces the feature of designated initializers, which allows specific
members of structures, unions, or arrays to be initialized explicitly by name or
subscript. For example:
struct info
{
char name[8+1];
int sz;
int typ;
};
[C99: §6.7.8]
[C++98: §8.5.1, 12.1]
• Duplicate typedefs
C does not allow a given typedef to appear more than once in the same scope.
C++ handles typedefs and type names differently than C, and allows redundant
occurrences of a given typedef within the same scope.
This means that typedefs that might be included more than once in a program
(e.g., common typedefs that occur in multiple header files) should be guarded
by preprocessing directives if such source code is meant to be compiled as
both C and C++. For example:
//========================================
// one.h
#ifndef MYINT_T
#define MYINT_T
typedef int MyInt;
#endif
...
//========================================
// two.h
#ifndef MYINT_T
#define MYINT_T
typedef int MyInt;
#endif
...
Thus code can include multiple header files without causing an error in C:
MyInt my_counter = 0;
if (sz <= 0)
return sizeof(sz); // Evaluated at compile time
else
return sizeof(arr); // Evaluated at runtime
}
C++ does not support VLAs, so C code that applies the sizeof operator to
VLA operands will cause problems when compiled as C++.
// C code
extern int foo(); // Unspecified parameters
extern int bar(void); // No parameters
void baz()
{
foo(0); // Valid C, invalid C++
foo(1, 2); // Valid C, invalid C++
C++, on the other hand, makes no distinction between the two declarations
and considers them both to mean a function taking no arguments.
// C++ code
For code that is intended to be compiled as either C or C++, the best solution
to this problem is to always declare functions taking no parameters with an
explicit void prototype. For example:
Empty function prototypes are a deprecated feature in C99 (as they were in
C89).
[C99: §6.7.5.3]
[C++98: §8.3.5, C.1.6.8.3.5]
#define ADD3(a,b,c) (+ a + b + c + 0)
ADD3(1, 2, 3) => (+ 1 + 2 + 3 + 0)
ADD3(1, 2, ) => (+ 1 + 2 + + 0)
ADD3(1, , 3) => (+ 1 + + 3 + 0)
ADD3(1,,) => (+ 1 + + + 0)
ADD3(,,) => (+ + + + 0)
• Enumeration constants
C++ enumeration constants have the same type as their enumeration type,
which means that they have the same size and alignment as their underlying
integer type. This means that the values of sizeof(RED) and sizeof(int) are
not necessarily the same for any given enumeration constant RED. Enumeration
constants also have a wider range of possible underlying types in C++ than in
C: signed int, unsigned int, signed long, and unsigned long. As such,
they also have a wider range of valid initialization values.
This may cause incompatibilities for C code compiled as C++, if the C++
compiler chooses to implement an enumeration type as a different size than it
would be in C, or if the program relies on the results of expressions such as
sizeof(RED).
enum ControlBits
{
CB_LOAD = 0x0001,
CB_STORE = 0x0002,
...
CB_TRACE = LONG_MAX+1, // (Undefined behavior)
CB_ALL = ULONG_MAX
};
C99 allows a trailing comma to follow the last enumeration constant initializer
within an enumeration type declaration, similar to structure member
initialization lists. For example:
[C99: §6.7.2.2]
[C++98: §7.2]
• Enumeration types
C specifies that each enumerated type is a unique type, distinct from all other
enumerated types within the same program. The implementation is free to use
a different underlying primitive integer type for each enumerated type. This
means that sizeof(enum A) and sizeof(enum B) are not necessarily the
same. This also means, given that RED is an enumeration constant of type
enum Color, that sizeof(RED) and sizeof(enum Color) are not necessarily
the same (since all enumeration constants are of type signed int).
All enumeration constants, though, convert to values of type signed int when
they appear in expressions. Since enumeration constants cannot portably be
wider than int, it might appear that int is the widest enumeration type;
however, implementations are free to support wider enumeration integer types.
Such extended types may be different than the types used by a C++ compiler,
however.
// C code
C++ also specifies that all enumerated types are unique and distinct types, but
it goes further than C to enforce this. In particular, a function name can be
overloaded to take an argument of different enumerated types. While objects
of enumerated types implicitly convert to integer values, integer values require
an explicit cast to be converted into enumerated types. Implicitly converted
enumeration values are converted to their underlying integer type, which is not
necessarily signed int. For example:
// C++ code
// C++ code
void bar(Color c)
{
foo(c); // Enum types might be different sizes
}
This is also known as the struct hack. This specifies a conforming way to
declare a structure containing a set of fixed-sized members followed by a
flexible array member that can hold an unspecified number of elements. Such
a structure is typically allocated by calling malloc(), passing it the number of
bytes beyond the fixed portion of the structure to add to the allocation size.
For example:
struct Hack
{
int count; // Fixed member(s)
int fam[]; // Flexible array member
};
p->count = sz;
for (int i = 0; i < sz; i++)
p->fam[i] = i;
return p;
}
[C99: §6.7.2.1]
[C++98: §8.3.4]
This differs from the way functions names are mapped into symbolic object
names in C, which allows for certain cases of type punning (between signed
and unsigned integer types) and non-prototyped extern functions. Therefore C
programs compiled as C++ will produce different symbolic names, unless the
functions are explicitly declared as having extern "C" linkage. For example:
#ifdef __cplusplus
extern "C"
#endif
int bar(int i); // Same symbolic name in both C and C++
C does not reserve such names, so a C program is free to use such names in
any manner. For example:
• Function pointers
extern "C"
{
extern int api_read(int f, char *b);
extern int api_write(int f, const char *b);
}
extern "C"
{
#include "api.h"
}
But simply declaring functions with extern "C" linkage is not enough to
ensure that C++ functions can call C functions properly. Specifically, pointers
to extern "C" functions and pointers to extern "C++" functions are not
compatible. When compiled as C++ code, function pointer declarations are
implicitly defined as having extern "C++" linkage, so they cannot be assigned
addresses of extern "C" functions. (Function pointers can thus be a source of
problems when dealing with C API libraries and C callback functions.)
void foo(int a)
{
int (*pf)(int i); // C++ function pointer
To make the combination of function pointers and extern "C" functions work
correctly in C++, function pointers that are assigned addresses of C functions
must be changed to have extern "C" linkage.
extern "C"
{
typedef int (*Pcf)(int); // C function pointer
}
void bar(int a)
{
int (*pf)(int i); // C++ function pointer
float pi = 0x3.243F6A88p+03;
C99 also provides additional format specifiers for the printf() and scanf()
family of standard library functions:
printf("%9.3a", f);
printf("%12.6lA", d);
C++ does not make any special provisions for implementations that explicitly
support the IEC 60559 floating-point specification.
[C99: §6.10.8, F, G]
[C++98: §16.8]
• Inline functions
Both C99 and C++ allow functions to be defined as inline, which is a hint to
the compiler that invocations of such functions can be replaced with inline
code expansions rather than actual function calls. Inline functions are not
supposed to be a source of incompatibilities between C99 and C++ in practice,
but there is a small difference in the semantics of the two languages.
C++ requires all of the definitions for a given inline function to be composed
of exactly the same token sequence.
Thus the following two example source files, which define two slightly
different versions of the same inline function, constitute acceptable C99 code
but invalid C++ code:
//========================================
// one.c
int foo(int j)
{
return twice(j);
}
//========================================
// two.c
int bar(int b)
{
return twice(b);
}
[C99: §6.7.4]
[C++98: §7.1.2]
C99 provides the header file <stdint.h>, which contains declarations and
macro definitions for standard integer types. For example:
The C++ standard library header files amend some of the standard C library
function declarations so as to be more type-safe when used in C++. For
example, the standard C library function declaration:
// <string.h>
extern char * strchr(const char *s, int c);
// <cstring>
extern const char * strchr(const char *s, int c);
extern char * strchr(char *s, int c);
This kind of code results in an attempt to assign a const pointer returned from
a function to a non-const variable. A simple cast corrects the code, making it
valid as both C++ and C code, as in:
C99 adds a few header files that are not included as part of the standard C++
library, though:
<complex.h>
<fenv.h>
<inttypes.h>
<stdbool.h>
<stdint.h>
<tgmath.h>
Even though C++ provides the C89 standard C library headers as part of its
library, it deems their use as deprecated. Instead, it encourages programmers
to prefer the equivalent set of C++ header files which provide the same
functionality as the C header files:
Deprecating the use of the C header files thus makes the following valid
C++98 program possibly invalid under a future revision of standard C++:
int main(void)
{
printf("Hello, world\n");
return 0;
}
The program can be modified by removing the use of deprecated features in
order to make it portable to future implementations of standard C++:
#ifdef __cplusplus
#include <cstdio> // C++ only
using std::printf;
#else
#include <stdio.h> // C only
#endif
int main(void)
{
printf("Hello, world\n");
return 0;
}
[C99: §7.1.2]
[C++98: §17.4.1.2, D.5]
C99 provides signed long long and unsigned long long integer types to its
repertoire of primitive types, which are binary integer types at least 64 bits
wide.
C99 also has enhanced lexical rules to allow for integer constants of these
types. For example:
C99 also provides several new macros in <limits.h>, new format specifiers
for the printf() and scanf() family of standard library functions, and
additional standard library functions that support these types. For example:
struct Outer
{
struct Inner // Nested structure declaration
{
int a;
float f;
} in;
// C++ code
struct Outer
{
struct Inner in;
enum E state;
};
[C99: §6.2.1, 6.2.3, 6.7.2.1, 6.7.2.3]
[C++98: §9.9, C.1.2.3.3]
• Old-style casts
const_cast
dynamic_cast
reinterpret_cast
static_cast
While the following C code is also valid C++98 code, it may not be
considered valid code in a future revision of the C++ standard:
char * p;
const char * s = (const char *) p;
#ifdef __cplusplus
#define const_cast(t,e) const_cast<t>(e)
#define dynamic_cast(t,e) dynamic_cast<t>(e)
#define reinterpret_cast(t,e) reinterpret_cast<t>(e)
#define static_cast(t,e) static_cast<t>(e)
#else
#define const_cast(t,e) ((t)(e))
#define dynamic_cast(t,e) ((t)(e))
#define reinterpret_cast(t,e) ((t)(e))
#define static_cast(t,e) ((t)(e))
#endif
All four casts are included above even though dynamic_cast is not really
useful in C code. Perhaps a better definition for dynamic_cast in C would be:
These kinds of typecasts cannot be used in code that is compiled as both C and
C++.
C++ does not allow this. Only one definition of any given variable is allowed
within a program.
C also allows, or at least does not require a diagnostic for, different source
files containing conflicting definitions. For example:
//========================================
// one.c
//========================================
// two.c
C++ deems this invalid, requiring both definitions to consist of the same
sequence of tokens.
C allows definitions of the same function or object in different source files to
be composed of different token sequences, provided that they are semantically
identical.
The C++ rules are more strict, requiring the multiple definitions to be
composed of identical token sequences. Thus the following code, which
contains multiple definitions that are semantically equivalent but syntactically
(token-wise) different, is valid in C but invalid in C++:
//========================================
// file1.c
//========================================
// file2.c - Valid C, but invalid C++
• _Pragma keyword
C99 provides the _Pragma keyword, which operates in a similar fashion to the
#pragma preprocessor directive. For example, these two constructs are
equivalent:
• Predefined identifiers
C99 provides a predefined identifier, __func__, which acts like a string literal
containing the name of the enclosing function. For example:
int incr(int a)
{
fprintf(dbgf, "%s(%d)\n", __func__, a);
return ++a;
}
C99 has a few reserved keywords that are not recognized by C++:
restrict
_Bool
_Complex
_Imaginary
_Pragma
This will cause problems when C code containing these tokens is compiled as
C++. For example:
C++ also specifically reserves the asm keyword, which may or may not be
reserved in C implementations.
C code is free to use these keywords as identifiers and macro names. This will
cause problems when C code containing these tokens is compiled as C++. For
example:
[C99: §6.4.1]
[C++98: §2.11]
• restrict keyword
C99 supports the restrict keyword, which allows for certain optimizations
involving pointers. For example:
#ifdef __cplusplus
#define restrict /* nothing */
#endif
• Returning void
For example:
// C++ code
template <typename T>
T bar(someType expr)
{
...
return (T)expr; // Valid even if T is void
}
[C99: §6.8.6.4]
[C++98: §3.9.1, 6.6.3]
• static linkage
Both C and C++ allow objects and functions to have static file linkage, also
known as internal linkage. C++, however, deems this as deprecated practice,
preferring the use of unnamed namespaces instead. (C++ objects and
functions declared within unnamed namespaces have external linkage unless
they are explicitly declared static. C++ deems the use of static specifiers
on objects or function declarations within namespace scope as deprecated.)
While it is not a problem for C code compiled under C++98 rules, it may
become a problem in a future revision of the C++ language. For example, the
following fragment uses the deprecated static feature:
// C++ code
namespace /*unnamed*/
{
static int bufsize = 1024;
static int counter = 0;
#ifdef __cplusplus
#define STATIC static
#endif
#ifdef __cplusplus
namespace /*unnamed*/
{
#endif
#ifdef __cplusplus
}
#endif
• String initializers
C++ also allows character arrays to be initialized with string constants, but
always includes the terminating null character in the initialization. Thus the
last initializer (name4) in the example above is invalid in C++.
[C99: §6.7.8]
[C++98: §8.5, 8.5.2]
• String literals are const
In C, string literals have type char[n], but are not modifiable (i.e., attempting
to modify the contents of a string literal is undefined behavior).
In C++, string literals have type const char[n] and are also not modifiable.
void foo(void)
{
frob("abc"); // Valid in both C and C++,
// since literal converts to char *
}
This language feature does not present an incompatibility between C99 and
C++98. However, the implicit conversion has been deprecated in C++
(presumably to be replaced by a single implicit conversion to type const char
*), which means that a future revision of C++ may no longer accept the code
above as valid code.
C++ does not allow either of these, since the scope of the structure declared in
this fashion does not extend outside the function declaration or definition,
making it impossible to define objects of that structure type which could be
passed as arguments to the function or to assign function return values into
objects of that type.
// Macro definitions
#define sin __tg_sin // Built-in compiler symbol
#define cos __tg_cos // Built-in compiler symbol
#define tan __tg_tan // Built-in compiler symbol
etc...
[C99: §7.22]
[C++98: §13, 13.1, 13.3.1, 13.3.2, 13.3.3]
• Typedefs versus type tags
struct type
{
type memb; // int
struct type * next; // struct pointer
};
type = i + t + sizeof(type);
s.memb = type;
}
This difference in the treatment of typedefs can also lead to code that is valid
as both C and C++, but which has different semantic behavior. For example:
int sz = 80;
int size(void)
{
struct sz
{ ... };
C++ also allows variable function argument lists, but provides two syntactical
forms for this feature.
[C99: §6.7.5]
[C++98: §8.3.5]
C99 supports preprocessor function macros that may take a variable number of
arguments. Such macros are defined with a trailing '...' token in their
parameter lists, and may use the __VA_ARGS__ reserved identifier in their
replacement text.
For example:
#define DEBUGF(f,...) \
(fprintf(dbgf, "%s(): ", f), fprintf(dbgf,
__VA_ARGS__))
#define DEBUGL(...) \
fprintf(dbgf, __VA_ARGS__)
C99 also provides new declaration syntax for function parameters of VLA
types, allowing a variable identifier or a '*' to occur within the brackets of an
array function parameter declaration in place of a constant integer size
expression. The following example illustrates the syntax involved in passing
VLAs to a function:
void add_seq(int n)
{
float x[n]; // VLA
float s;
...
s = sum_square(n, x) + sum_cube(n, x);
...
}
VLA function parameter declarations using a '*' can only appear in function
declarations (with prototypes) and not in function definitions. Note that this
capability also affects the way sizeof expressions are evaluated.
C++ does not allow assigning a pointer to void directly to an object of any
other pointer type without an explicit cast. This is considered a breach of type
safety, so an explicit cast is required. Thus the following code is valid C but
invalid C++:
p = malloc(sizeof(struct object));
// Direct assignment without a
cast,
// valid C, invalid C++
return p;
}
(Both languages allow values of any pointer type to be assigned to objects of
type pointer to void without requiring an explicit cast.
void * vp;
Type * tp;
(Note that there are situations in C++ where pointers are implicitly converted
to type pointer to void, such as when comparing a pointer of type pointer to
void to another pointer of a different type, but such situations are considered
type safe since no pointer objects are modified in the process.)
C++ also provides a wchar_t type, but it is a reserved keyword just like int.
No header file is required to enable its definition.
This means that C code that does not include any of the standard header files
listed above is free to use wchar_t as an identifier or macro name; such code
will not compile as C++ code.
wchar_t readwc(void)
{
...
}
The recommended practice is therefore to use the wchar_t type only for its
special meaning, and only after including <stddef.h>, <stdlib.h>, or
<wchar.h>.
Acknowledgments
My thanks to the the people who gave helpful comments on early drafts of this
document, especially to the following individuals who emailed me suggestions and
corrections or posted comments on the comp.std.c and comp.std.c++ newsgroups:
Revision History
1.0, 2001-08-05
Completed revision.
0.12, 2000-11-13
Minor corrections made.
Better HTML anchor names.
0.11, 2000-09-20
Sixth public review revision.
Added ISO section reference numbers to most of the sections.
0.10, 2000-07-30
Sixth public review revision.
0.09, 2000-02-17
Fifth public review revision, still incomplete.
0.08, 1999-10-31
Fourth public review revision, still incomplete.
Minor corrections made.
Changed "C9X" to "C99" after the ratification of ISO C-1999.
0.07, 1999-10-13
Third public review revision, still incomplete.
0.06, 1999-10-05
Second public review revision, still incomplete.
0.05, 1999-09-14
First public review revision, still incomplete.
0.00, 1999-08-24
First attempt, incomplete.
References and links to this document may be created without permission from the
author. Portions of this document may be used or quoted without permission from the
author provided that appropriate credit is given to the author. This document may be
printed and distributed without permission from the author on the condition that the
authorship and copyright notices remain present and unaltered.