12. Pointers and References

Rule 42
Do not compare a pointer to NULL or assign NULL to a pointer; use 0 instead. [An intensive debate about this has been raging in the news group comp.lang.c++. Future changes in this recommendation may occur.]
Rec. 48
Pointers to pointers should whenever possible be avoided.
Rec. 49
Use a typedef to simplify program syntax when declaring function pointers.

According to the ANSI-C standard, NULL is defined either as (void*)0 or as 0. If this definition remains in ANSI-C++, problems may arise. If NULL is defined to have the type void*, it cannot be assigned an arbitrary pointer without an explicit type conversion. For this reason, we recommend comparisons with 0 at least until the ANSI-C++ committee has made a decision.

Pointers to pointers normally ought not be used. Instead, a class should be declared, which has a member variable of the pointer type. This improves the readability of the code and encourages data abstraction. By improving the readability of code, the probability of failure is reduced. One exception to this rule is represented by functions which provide interfaces to other languages (such as C). These are likely to only allow pre-defined data types to be used as arguments in the interface, in which case pointers to pointers are needed. Another example is the second argument to the main function, which must have the type char*[]. [This is equivalent to char**.]

A function which changes the value of a pointer that is provided as an argument, should declare the argument as having the type reference to pointer (e.g. char*&). See Rule 42!

typedef is a good way of making code more easily maintainable and portable. See chapter 18.1, Port.Rec.1. Another reason to use typedef is that the readability of the code is improved. If pointers to functions are used, the resulting code can be almost unreadable. By making a type declaration for the function type, this is avoided.

Function pointers can be used as ordinary functions; they do not need to be dereferenced. [See Example 46.]

Exception to Rule 42
No exceptions.

Example 45: Different comparisons of pointers

   char* sp = new char[100];
   if ( !sp )        cout << "New failed!" << endl;   // No!
   if ( sp == 0 )    cout << "New failed!" << endl;   // Best
   if ( sp == NULL ) cout << "New failed!" << endl;   // ERROR sometimes !!!

Example 46: Pointers to pointers are often unnecessary

[This example is, in part, taken from The C++ Programming Language, Second Edition.]
   #include <iostream.h>
   
   void print_mij(int** m, int dim1, int dim2)
   {
      for (int i = 0; i < dim1; i++)
      {
         for (int j = 0; j < dim2; j++ )
            cout << " " << ((int*)m)[i*dim2+j];
         cout << endl;
      }
   }
   
   // Could be written as:
   
   class Int_Matrix {
      public:
         Int_Matrix(int dim1, int dim2);
         int value(int,int) const;
         int dim1() const;
         int dim2() const;
         // ..
   };
   
   void print_Mij(Int_Matrix m)
   {
      for (int i = 0; i < m.dim1(); i++)
      {
         for (int j = 0; j < m.dim2(); j++ )
            cout << " " << m.value(i,j);
         cout << endl;
      }
   }

Example 47: Complicated declarations

   // func1 is a function: int -> (function : const char* -> int)
   // i.e. a function having one argument of type int and returning
   // a pointer to a function having one argument of type const char*
   // and returning an int.
   
   int (*func1(int))(const char*);
   
   // func1 of the same type as func2
   
   typedef int FTYPE(const char*);
   FTYPE* func2(int);
   
   int (*(*func1p)(int))(const char*) = func2;
   
   // Realistic example from signal.h
   
   void (*signal(int,void (*)(int)))(int);

Example 48: Syntax simplification of function pointers using a typedef

   #include <math.h>
   
   // Ordinary messy way of declaring pointers to functions:
   // double ( *mathFunc ) ( double ) = sqrt;
   
   // With a typedef, life is filled with happiness (chinese proverb):
   typedef double MathFuncType( double );
   MathFuncType* mathFunc = sqrt;
   
   void
   main()
   {
      // You can invoke the funktion in an easy or complicated way
      double returnValue1 = mathFunc( 23.0 );     // Easy way
      double returnValue2 = ( *mathFunc )( 23.0 ); // No! Correct, but complicated
   }