Some Differences between C and C++

1: End-of-line comment

According to the ANSI definition, `end of line comment' is implemented in the syntax of C++.  The standard C comment, delimited by /* and */ can still be used in C++:
    int main()
    {
        // this is end-of-line comment
        // one comment per line

        /*
            this is standard-C comment, over more
            than one line
        */
        return (0);
    }

The end-of-line comment was already implemented as an extension to C in some C compilers, such as the Microsoft C Compiler V5.
 

2: NULL-pointers vs. 0-pointers

In C++ all zero values are coded as 0. In C, where pointers are concerned, NULL is often used. This difference is purely stylistic, though one that is widely adopted. In C++ there's no need anymore to use NULL. Indeed, according to the descriptions of the pointer-returning operator new 0 rather than NULL is returned when memory allocation fails.
 

3: Strict type checking

C++ uses very strict type checking. A prototype must be known for each function which is called, and the call must match the prototype. The program
    int main()
    {
        printf("Hello World\n");
        return (0);
    }
does often compile under C, though with a warning that printf() is not a known function. Many C++ compilers will fail to produce code in such a situation (When GNU's g++ compiler encounters an unknown function, it assumes that an `ordinary' C function is meant. It does complain however.). The error is of course the missing #include<stdio.h> directive.
 

4: A new syntax for casts

Traditionally, C offers the following cast construction:

(typename)expression

in which typename is the name of a valid type, and expression an expression. But in C++, these casts are now all called old-style casts, and they are deprecated. Instead, four new-style casts were introduced:

5: The 'static_cast'-operator

The static_cast<type>(expression) operator is used to convert one type to an acceptable other type. E.g., double to int. An example of such a cast is, assuming intVar is of type int:

intVar = static_cast<int>(12.45);

Another nice example of code in which it is a good idea to use the static_cast<>()-operator is in situations where the arithmetic assignment operators are used in mixed-type situations. E.g., consider the following expression (assume doubleVar is a variable of type double:

intVar += doubleVar;

Here, the evaluated expression actually is:

intVar = static_cast<int>(static_cast<double>(intVar) + doubleVar);

6: The 'const_cast'-operator

The const_cast<type>(expression) operator is used to do away with the const-ness of a (pointer) type. Assume that a function string_op(char *s) is available, which performs some operation on its char *s parameter. Furthermore, assume that it's known that the function does not actually alter the string it receives as its argument. How can we use the function with a string like char const hello[] = "Hello world"?

Passing hello to fun() produces the warning

passing `const char *' as argument 1 of `fun(char *)' discards const

which can be prevented using the call

fun(const_cast<char *>(hello));

7: The 'reinterpret_cast'-operator

The reinterpret_cast<type>(expression) operator is used to reinterpret byte patterns. For example, the individual bytes making up a double value can easily be reached using a reinterpret_cast<>(). Assume doubleVar is a variable of type double, then the individual bytes can be reached using

reinterpret_cast<char *>(&doubleVar)

This particular example also suggests the danger of the cast: it looks as though a standard C-string is produced, but there is not normally a trailing 0-byte. It's just a way to reach the individual bytes of the memory holding a double value.

More in general: using the cast-operators is a dangerous habit, as it suppresses the normal type-checking mechanism of the compiler. It is suggested to prevent casts if at all possible. If circumstances arise in which casts have to be used, document the reasons for their use well in your code, to make double sure that the cast is not the underlying cause for a program to misbehave.
 

8: The void argument list

A function prototype with an empty argument list, such as
    extern void func();
means in C that the argument list of the declared function is not prototyped: the compiler will not be able to warn against improper argument usage. When declaring a function in C which has no arguments, the keyword
void is used, as in:
    extern void func(void);
Because C++ maintains strict type checking, an empty argument list is interpreted as the absence of any parameter. The keyword void can then be left out. In C++ the above two declarations are equivalent.
 

9: The #define __cplusplus

Each C++ compiler which conforms to the ANSI standard defines the symbol __cplusplus: it is as if each source file were prefixed with the preprocessor directive #define __cplusplus.
 

10: The usage of standard C functions

Normal C functions, e.g., which are compiled and collected in a run-time library, can also be used in C++ programs. Such functions however must be declared as C functions. As an example, the following code fragment declares a function xmalloc() which is a C function:
    extern "C" void *xmalloc(unsigned size);
This declaration is analogous to a declaration in C, except that the prototype is prefixed with extern "C".

It is also possible to place preprocessor directives at the location of the declarations. E.g., a C header file myheader.h which declares C functions can be included in a C++ source file as follows:

    extern "C"
    {
    #   include <myheader.h>
    }
The above presented methods can be used without problem, but are not very current. A more frequently used method to declare external C functions is presented below.
 

11: The definition of local variables

In C local variables can only be defined at the top of a function or at the beginning of a nested block. In C++ local variables can be created at any position in the code, even between statements.

Furthermore local variables can be defined in some statements, just prior to their usage. A typical example is the for statement:

    #include <stdio.h>
    
    int main()
    {
        for (register int i = 0; i < 20; i++)
            printf("%d\n", i);
        return (0);
    }
In this code fragment the variable i is created inside the for statement. According to the ANSI-standard, the variable does not exist prior to the for-statement and not beyond the for-statement. With some compilers, the variable continues to exist after the execution of the for-statement, but a warning like
warning: name lookup of `i' changed for new ANSI `for' scoping using obsolete binding at `i'
will be issued when the variable is used outside of the for-loop. The implication seems clear: define a variable just before the for-statement if it's to be used beyond that statement, otherwise the variable can be defined at the for-statement itself.

Defining local variables when they're needed requires a little getting used to. However, eventually it tends to produce more readable code than defining variables at the beginning of compound statements. We suggest the following rules of thumb for defining local variables:

12: Function Overloading

In C++ it is possible to define several functions with the same name, performing different actions. The functions must only differ in their argument lists. An example is given below:
 
    #include <stdio.h>

    void show(int val)
    {
        printf("Integer: %d\n", val);
    }

    void show(double val)
    {
        printf("Double: %lf\n", val);
    }

    void show(char *val)
    {
        printf("String: %s\n", val);
    }

    int main()
    {
        show(12);
        show(3.1415);
        show("Hello World\n!");

        return (0);
    }
In the above fragment three functions show() are defined, which only differ in their argument lists: int, double and char *. The functions have the same name. The definition of several functions with the same name is called `function overloading'.
 

13: Default function arguments

In C++ it is possible to provide `default arguments' when defining a function. These arguments are supplied by the compiler when not specified by the programmer. An example is shown below:
 
    #include <stdio.h>

    void showstring(char *str = "Hello World!\n")
    {
        printf(str);
    }

    int main()
    {
        showstring("Here's an explicit argument.\n");
        showstring();           // in fact this says:
                                // showstring("Hello World!\n");
        return (0);                             
    }
The possibility to omit arguments in situations where default arguments are defined is just a nice touch: the compiler will supply the missing argument when not specified. The code of the program becomes by no means shorter or more efficient. Functions may be defined with more than one default argument:
    void two_ints(int a = 1, int b = 4)
    {
        ...
    }

    int main()
    {
        two_ints();            // arguments:  1, 4
        two_ints(20);          // arguments: 20, 4
        two_ints(20, 5);       // arguments: 20, 5
        return (0);
    }
When the function two_ints() is called, the compiler supplies one or two arguments when necessary. A statement as two_ints(,6) is however not allowed: when arguments are omitted they must be on the right-hand side.

Default arguments must be known to the compiler when the code is generated where the arguments may have to be supplied. Often this means that the default arguments are present in a header file:
            extern void two_ints(int a = 1, int b = 4);
 

14: The keyword typedef

The keyword typedef is in C++ allowed, but no longer necessary when it is used as a prefix in union, struct or enum definitions. This is illustrated in the following example:
   struct somestruct
    {
        int            a;
        double    d;
        char         string[80];
    };

When a struct, union or other compound type is defined, the tag of this type can be used as type name (this is somestruct in the above example):
    somestruct    what;
    what.d = 3.1415;

15: Functions as part of a struct

 A definition of a struct point is given in the code fragment below. In this structure, two int data fields and one function draw() are declared.
    struct point            // definition of a screen
    {                       // dot:
        int
            x,              // coordinates
            y;              // x/y
        void
            draw(void);    // drawing function
    };
A similar structure could be part of a painting program and could, e.g., represent a pixel in the drawing. Concerning this struct it should be noted that: The point structure could be used as follows:
    point                   // two points on
        a,                  // screen
        b;

    a.x = 0;                // define first dot
    a.y = 10;               // and draw it
    a.draw();

    b = a;                  // copy a to b
    b.y = 20;               // redefine y-coord
    b.draw();              // and draw it
The function which is part of the structure is selected in a similar manner in which data fields are selected; i.e., using the field selector operator (.). When pointers to structs are used, -> can be used.