[ Home | Event Information | Objectives | Committee | The Rationale | Language spec | Q&A]

The Embedded C++ Programming Guide Lines

Version WP-GU-003,Copyright(C) 6,Jan 1998 by the Embedded C++ Technical Committee

Embedded C++ Programming Guide

A.    Migrating from C language to C++ language

A.1   Character constant


          In C, a character constant has type int.  In C++, it has
          type char.


          i = sizeof('a');

          In C, this stores sizeof(int) (which is likely to be
          greater than 1) into i.  In C++, it stores sizeof(char)
          (which is always 1) into i.


          When migrating code from C to C++, rewrite expressions
          that depend on the size of a character constant to remove
          the dependency.

A.2   Object declaration at file scope


          In C++, a declaration of an object at file scope without a
          storage class specifier is a definition of that object
          with external linkage.  If the definition does not have an
          initializer, the object has initial value 0.  (As in C, an
          object declared in a C++ program must be defined exactly

          In C, a declaration of an object at file scope without a
          storage class specifier and without an initializer is a
          tentative definition, which may appear more than once in a
          translation unit.


          int a;          /* (1) */
          int a = 10;     /* (2) */

          In C, (1) is a tentative definition.  Since (2) is
          unequivocally a definition, C treats (1) as a mere

          In C++, both (1) and (2) are definitions.  In the
          presence of (1), (2) is a duplicate definition, and
          therefore an error.


          In C++, a declaration of an object at file scope is just a
          declaration (and not also a definition) if and only if it
          has an explicit extern specifier and no initializer.

          Each object declared at file scope must be defined exactly
          once.  All but one declaration must have both an explicit
          extern specifier and no initializer.

A.3   const type qualifier


          In C, a const-qualified object at file scope without an
          explicit storage class specifier has external linkage.  In
          C++, it has internal linkage.


          +- file1 --------------------+
          |      extern const int n;   |
          +- file2 --------------------+
          |      const int n = 10;     |

          In C, object n of file2 has external linkage, so it can
          satisfy the reference to n (also with external linkage) in
          file1.  In C++, object n in file2 has internal linkage,
          and will not satisfy the reference to n in file1.


          Const-qualified objects with external linkage must have an
          explicit extern specifier.

A.4   Conversion to void *


          In C, there is a standard conversion from void * to T *
          (for any object type T).  In C++, there is no such
          conversion.  Such conversions require a cast in C++.

          The following Standard C library functions return void *:

              calloc, malloc, realloc, bsearch, memcpy, memmove,
              memchr, memset

          C++ requires an explicit cast when assigning the return
          value of such a function to a pointer to non-void type.


          int* p;
          p = malloc(10 * sizeof(int));

          In C++, the assignment to p requires an explicit cast, as

          p = (int *)malloc(10 * sizeof(int));


          In C++, use operator new instead of calloc, malloc, or
          realloc.  (See item A.12).

          Ignore the return value of a memcpy, memmove, and memset.
          (They just return their first argument converted to void

          For all other functions that return void * (standard or
          user-defined), use an explicit cast when converting the
          return value to another pointer type.

A.5   Enumeration type


          In C, enumerations are integral types.  A program can
          convert from an enumeration type to an integral type, and
          back, without a cast.  A C program can apply ++ and -- to
          an enumeration object.

          In C++, each enumeration is a distinct type.  There are
          standard conversions from enumeration type to integral
          types, but not from integral types to enumeration types.
          A C++ program cannot apply built-in ++ and --, nor any
          compound assignment (such as +=) to an enumeration object.


          enum RGB { red, green, blue } rgb;

          ++rgb is an error in C++ if it uses the built-in ++
          operator.  It means the same as

          rgb = rgb + 1;

          This is also an error unless you write it with a cast:

          rgb = RGB(rgb + 1);

          The best alternative is to implement an operator++ for
          type RGB, as in

          RBG &operator++(RGB &x)
              return x = RGB(x + 1);


          When converting a C program to C++, provide typesafe
          implementations for operator++ and operator-- as needed
          for enumeration types.

A.6   Type definition in cast, parameter declaration, or sizeof


          In C, types can be defined in a cast expression, parameter
          declaration or sizeof expression.  In C++ they cannot.


          void func(struct TAG { int a; } st)

          Here, TAG is defined in the parameter declaration.


          Define a type used in a parameter declaration in the scope
          enclosing the function declaration, or some larger
          enclosing scope.

          Define a type used in a cast expression or sizeof
          expression in the scope enclosing the expression, or some
          larger enclosing scope.

A.7   Transfer of control past the definition of a local object


          In C, a goto or switch statement may transfer control
          beyond the definition of an object at block scope,
          possibly bypassing initialization.  In C++, it may not.


          goto LABEL;
              int v = 0;

          This is valid in C, assuming the code following LABEL:
          does not depend on v being initialized to 0.  It is always
          an error in C++.


          Do not use goto or switch statements to bypass
          initialization of a local object.

A.8   Character array initialization


          A C program can initialize an array of characters using a
          string literal that defines one more character (counting
          the terminating '\0') than the array can hold.
          A C++ program cannot.


          char s[3] = "abc";

          The size of the array is three, though the size of the
          string literal is 4.  This is valid in C, but not C++.


          Do not initialize an array of characters using a string
          literal with more characters (including the '\0') than the
          array. Therefore, it is necessary to specify the correct size of 
          a string literal (char s[4] = "abc";). 
          However, because the result of the expectation always can be
          obtained even if the size of the string literal is changed,
          the method of not describing the size (char s[] = "abc";) is 

A.9   Prototype declaration


          A C++ program requires that you declare a function
          prototype before calling the function. In C, a call to
          an undeclared function is permissible.  Moreover, a C++
          program interprets the function declarator 'f()' as
          equivalent to 'f(void)' -- a function with no arguments.
          In C, the same declaration leaves the number and types
          of the parameters unspecified.


          extern void func();

          The call to function 'sub' is an error because there is no
          prototype declaration.  The call to function 'func' is
          also an error because its declaration says it has no


          Always declare the prototype before calling a function.
          To emphasize that function 'f' is called with no
          arguments, write its declarator as 'f(void)'.

A.10  Keywords added in C++


          The following C++ keywords are not keywords in C:

          asm             bool            catch           class
          const_cast      delete          dynamic_cast    explicit
          false           friend          inline          mutable
          namespace       new             operator        private
          protected       public          reinterpret_cast
          static_cast     template        this            throw
          true            try             typeid          typename
          using           virtual         wchar_t


          int class, new, old;

          This declaration is valid in C, but not in C++.


          Do not use a C++ keyword as an identifier.

A.11  Scope of nested types

          In C, the name of a type defined inside a struct or union
          is actually in same scope as the name of the enclosing
          struct or union.  In C++, the name of the nested type is
          within the scope of the enclosing struct or union.


          struct S {
              int a;
              struct T {
                  int t;
              } b;
              int c;
              enum E { V1, V2 } e;

          struct T x;
          enum E y;

          The declarations for x and y are valid in C, but not in
          C++.  In C++, the names T and E are not in scope outside
          the definition of struct S.


          Do not define the name of a type as nested unless all uses
          of that name are also in the scope of the enclosing

A.12  Dynamic memory management


          There is no guarantee that new and delete apply the same
          memory management policy to the same memory as do malloc
          and free.  Therefore, a program cannot delete memory
          unless that memory was previously acquired by new, and it
          cannot free memory unless that memory was acquired by
          malloc (or calloc or realloc).


          int (*p)[10];
          p = (int (*)[10])malloc(sizeof(*p));
          delete p;

          The delete expression has undefined behavior.


          In C++, avoid malloc, calloc, realloc and free; use only
          new and delete.

A.13  '/*' after '/'


          Writing the C-style comment '/* */' immediately after
          the token '/' is interpreted as the C++-style comment
          '//' instead.


          i = j //* comment */ k ;

          The sequence '//' is interpreted as a comment delimiter.
          The expression is interpreted not as 'i = j / k;' but as
          'i = j'.


          Avoiding writing a C-style comment '/**/' immediately
          after the token '/'.

B.    Guidelines for Code Size

B.1   Object initialization


          There are various ways to specify the initialization of an
          object.  Some initializations generate unnecessary
          temporary objects, and result in larger code size.

          For example:

          T x(i)        // (1)

          T x = i;      // (2)

          T x = T(i)    // (3)

          T x;          // (4)
          x = i;        //

          (1) This applies a constructor directly to object x
              without using a temporary object, as if by calling:

              x.T(i);             // apply constructor to x

          (2) In some implementations, this applies a constructor
              directly to x as above.  In others, it constructs a
              temporary object, and initializes x from the
              temporary, as if by calling:

              temp.T(i);        // apply constructor to temp
              x.T(temp);        // apply copy constructor to x
              temp.~T();        // apply destructor to temp

          (3) This is the same as (2).

          (4) This initializes x using T's default constructor, then
              later assigns a new value to x using an assignment
              operator.  The assignment operator may release
              resources that x is using and acquire new resources.

              x.T();           // apply default constructor to x
              x.operator=(i);  // apply assignment operator to x


          Use declarations of form (1) above in preference to the
          other forms.

B.2   Inline specifier


          Inline expansion reduces the overhead of function entry
          and exit, but it may increase code size.

          Member functions in class definitions are expanded inline
          by default.


          Use inline specifier for only small functions.  A member
          function for which inline expansion is not appropriate
          should be defined outside the class definition, so that it
          is not inline expanded.

B.3   Temporary objects for return values


          Calling a Function that returns an object by value may
          create and destroy a temporary object, thus increasing by
          code size and execution time.


          class Matrix {
              int a, b;
              Matrix &operator+=(const Matrix &);
                  Matrix operator+(const Matrix &, const Matrix &);

          Matrix operator +(const Matrix &, const Matrix &)

          void func()
              Matrix a,b;
              a = a + b;        // (1)
              a += b;           // (2)

          (1) calls operator+, which returns a Matrix by value.  In
          some implementations this creates (and later destroys) a
          temporary Matrix object.

          (2) calls operator+=, which generates no temporary Matrix


          For objects of class types, use compound assignment
          operators (such as += in preference to + and =) to avoid
          creating and destroying temporary objects unnecessarily.

B.4   Operators new and delete


          Implement class-specific operators new and delete as
          needed to improve the speed and memory utilization of
          dynamic memory management.

B.5   Initialization of global objects


          The order of initialization of global objects is
          implementation-dependent.  However, the order of
          initialization in a single translation unit is guaranteed
          to be the order of declaration.


          file1           file2             file3

          int a = f();    int b = f();      int f(void)
                                                static int a = 0;
                                                return a++;

          The program may initialize 'a' with 0 and 'b' with 1, or
          vice versa, depending on the order the implementation
          chooses to initialize them.

          It is possible to make the initialization order well
          defined by moving the declaration of 'b' in file2 to
          file1, as in:

          file1           file2             file3

          int a = f();                      int f(void)
          int b = f();                      {
                                                static int a = 0;
                                                return a++;

          The order of initialization is a, then b.


          Avoid coding which depends on the initialization order of
          global objects across translation units.

C.    Guidelines for speed

C.1   new and delete for array of class objects


          Declaration of an array of class objects calls its
          element's constructor for each element.  The program calls
          the destructor for each element when the array goes out of
          scope.  The processing time of the constructor/destructor
          may be unexpectedly long.  This can be a problem for
          real-time processing.


          Avoid creating and destroying a large array of class
          objects during time-critical processing.

C.2   Object declaration in loops


          When a class variable is declared in a loop, its
          constructor and destructor are called in each iteration.
          The overhead of construction and destruction may slow the


          for (i = 0; i < 1000; i++)
              FOO a;


          Avoid declaring a class variable inside a loop.  Declare
          it outside the loop instead.

D.    Guidelines for ROMable code

D.1   const objects in ROM


          In general, a const-qualified object can reside in ROM if

          --  has static storage duration,

          -- is initialized by a constant expression, and

          --  has a POD (plain old data) type.

          A POD type is any of:

          --  a scalar (arithmetic, enumeration or pointer) type

          --  a class, struct, or union all of whose data members
              are public and of POD types, and with no user-defined
              constructor or destructor, no base classes, and no
              virtual functions

          --  an array with elements of POD type


          static const char lang[] = "EC++";

          class A {
              int a;

          const A x;

          'lang' may be placed in ROM; 'x' may not.


          Declare objects to be placed in ROM with POD types and
          with constant initializers.

NOTE: The form of presentation used here, and several of the specific
guidelines, were inspired by the excellent book by Thomas Plum and
Dan Saks, 'C++ Programming Guidelines' (Plum Hall Inc., 1991).