Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Lovely Pointers

0.00/5 (No votes)
5 Jun 2003 1  
The topic of this article is pointers. I describe below some problems, bugs and technique solutions that correspond with using pointers. This article would be useful for beginners and programmers who use other programming languages and are starting to study C++ now.

Introduction

The topic of this article is pointers. I describe below some problems, bugs and technique solutions that correspond to using pointers. This article would be useful for beginners and programmers who are using other programming languages and are starting to study C and C++ now.

Bad pointers

There are a lot of programmers who think that pointers are bad constructions as the "go to" operator. Pointers are so bad that you cannot find them in Basic, Java, C#. But really it is not true. All applications that are executed under Windows or Unix use pointers! A lot of API functions receive pointers and return pointers, so if you use API, you use pointers.

For example, if I declare in a Visual Basic program, such an API function interface:

Private Declare Sub SetLocalTime Lib "kernel32" (localTime As SYSTEMTIME)

Basic instruction Call SetLocalTime(tmLocal), will send to API function, the pointer to the SYSTEMTIME structure.

Why do not many languages support pointers as a language construction? - Because pointers are dangerous. It is easy to make an error that a compiler will not find. More possible is to make an error that you will not find while debugging the program. If your program is alive, it is only because it has not been started by the right user. Good users will always find a way to crash your program.

Here is an example of a very common pointers-related error:

char * CloneName(char *pSomeName)
{
     char *pTmpName = new char[strlen(pSomeName)]; // Error! 

     strcpy(pTmpName, pSomeName); 
     return pTmpName;
}

This function must clone a string. In this example, one byte of memory will be destroyed behind the copy of the string. The right allocation instruction is new char[strlen(pSomeName) +1]. A string in C and C++ is finished by zero code. This error can crash your program immediately, once in a while or never! Everything depends on a byte behind a string.

Good pointers

A pointer is a language construction of C++. Historically, this language continues the C language tradition that was created as a good alternative to assembler language. Pointers allow a programmer manage memory allocation very efficiently. If you work accurately, everything will be OK.

What is a pointer?

A pointer is a special variable that is used for storing some memory address. So sizeof(pointer) is small and depends on operation system. For Win32 it equals 4 bytes. A pointer has a type "Pointer to some type". A pointer can be converted to integer value and integer value can be converted to a pointer. It is used widely in the Windows API functions.

Here is a title of "the main" Windows function. It sends a message to a window.

LRESULT SendMessage(
     HWND hWnd, // handle of destination window

     UINT Msg, // message to send

     WPARAM wParam, // first message parameter

     LPARAM lParam // second message parameter

);

WPARAM and LPARAM are integer types. But many messages use them as pointers. For example, I want to print text in some window with handle hWnd. I do so:

SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)"Some Text");

"Some Text" is a static text constant. It has an address in a memory and type char*. This example shows the conversion from char* to integer (LPARAM is a long integer).

A pointer is also an array. Array type is not actually the same as a pointer type. But it is very close to a pointer and easy to convert to pointer. All dynamic arrays are pointers.

Pointers and arrays

It is an article. I do not want to rewrite some C++ book. So, I will demonstrate here only one interesting example. It shows the difference between using 2D automatic array and 2D dynamic array.

#include <iostream.h> 


void OutAutoElm(int nRow, int nCol, int *pAutoArray, int nSize);
void OutDynElm(int nRow, int nCol, int **pDynArray); 

int main(int argc, char* argv[])
{
     const int nSize = 5; // Size of matrix 

   // Auto Array. Allocate memory in the stack.

     int AutoArray[nSize][nSize]; 
     int **DynArray; // Dynamic Array pointer.

     // Memory allocation for Dynamic Array in the heap.

     DynArray = new int*[nSize];
     for(int i=0; i< nSize; i++){ 
          DynArray[i] = new int[nSize]; 
     } 
   // Assign some element of AutoArray

     AutoArray[2][3] = 7; 
   // and call output function

     OutAutoElm(2, 3, (int *)AutoArray, nSize); 
     DynArray[3][4] = 9; // Assign some element of DynamicArray

     OutDynElm(3, 4, DynArray); // and call output function

     AutoArray[5][0] = 10; 
      // Error! Outside of the array. The last element is [4][4]

      // But the program executed in my system without any errors.


     // Release memory of Dynamic Array 

      for(i=0; i< nSize; i++){ 
          delete[] DynArray[i]; 
     } 
     delete[] DynArray; 
     return 0; 
}
void OutAutoElm(int nRow, int nCol, int *pAutoArray, int nSize)
{
    // What a strange expression!

    int nValue = *(pAutoArray + nRow*nSize + nCol); 
    cout << "AutoArray["<<nRow<<"]
        ["<<nCol<<"]="<< nValue 
        << endl;
}
void OutDynElm(int nRow, int nCol, int **pDynArray)
{
     int nValue = pDynArray[nRow][nCol]; // Looks Normal

     cout << "DynArray["<<nRow<<"]
     ["<<nCol<<"]="<< nValue 
     << endl;
}

A very interesting example! AutoArray[2][3] = 7 and DynArray[3][4] = 9 looks as the same instructions. But one of them is *(AutoArray + 2 * 5 + 3) = 7, the other is *(*(DynArray+3)+4) = 9; See Pic.1.

Dangers

There are some common errors when using pointers. Most of them are very dangerous because they can be executed in your system without runtime errors. And nobody knows when and where they will crash the system.

  1. Using a pointer without memory allocation.

    Example:

         char *Str;
         cin >> Str;
  2. Array overflow. See an example in the previous paragraphs.
  3. Sending a value to the function that waits a pointer.

    Example (popular beginner error):

         int nSomeInt = 0;
         scanf("%d", nSomeInt); // send the value

    scanf is defined as int scanf(const char *, ...). The compiler cannot test variables' types. So you will not receive an error or warning message. Right solution is:

         int nSomeInt = 0;
         scanf("%d", &nSomeInt); // send the pointer
  4. Pointer release errors. A common error is a memory leak. We use new statement without delete statement. Some other errors are shown below:

    Example 1:

         int *pArray = new int[10];
         ... 
         delete pArray; // must be delete[] pArray

    Example 2:

         int a = 0;
         int*p = &a;
         delete p; // Nothing for release! 
    
                 //Use delete only when 
    
                 //instruction "new" was used!

    Example 3:

         int *a = new int;
         int *b = a;
         delete a;
         delete b; // Error. 
    
                 //The memory was cleared 
    
                 //by previous delete.
  5. Type conversion errors. We can convert a pointer to a wrong type and use it.

    Example:

         class A{};
         class B{
         public: 
              B():M(5){}
              int M;
         };
         int main(int argc, char* argv[])
         {
              A* pA = new A;
              B* pB = new B;
              cout << ((B*)pA)->M << endl; //Error! There is no M in A!
    
         }
  6. Strange allocation. It is not my fantasy. I met the same code!
         void SomeFun(int a);
         ....
         int main(){
              SomeFun(*(new int) ); 
           // Temporary variable with memory allocation.
    
                // Deleting memory is impossible.
    
         }

Here I described only plain errors that are common for C and C++ languages. When we use classes, inheritance, multiply inheritance, templates and other OOP constructions, we have much more opportunities for making mistakes with pointers. Be optimistic!

Recommendations

There are some rules that could prevent you from many errors.

  1. Use pointers only if you really need it. The common situations of using pointers are: creating dynamic arrays, creating an object in one function and deleting it in another function, receiving a pointer from a library function. Do not use a pointer if you can use an automatic variable or a reference instead.
  2. If you do not allocate memory while a pointer is defined, set it to NULL. Null pointers pretty much suit for debugging than non-initialized pointers.

    Example:

         int *pSome = NULL;
  3. Always test pointers returning by functions to NULL.

    Example:

         if ( ( pFile = fopen("SomeFile","r") ) == NULL){
              cerr << " SomeFile Open Error!" << end;
         }
  4. Always test incoming pointers using assert macros. It works only in the debugging mode and ignores in the release mode.

    Example:

         void SomeFun(SomeType *pSomePointer){
              ASSERT(pSomePointer);
              . . .
         }
  5. Never use C-style strings and C-style arrays. Always use library classes instead.

    STL using example:

         #include <string>
    
         #include <vector>
    
         #include <iostream>
    
         using namespace std;
         int main(int argc, char* argv[])
         {
          // Some string object, use instead char *
    
              string sName1 = "Jeanne"; 
          // Some array of strings. Use instead char** 
    
              vector<string> sNames; 
              sNames.push_back(sName1);
              sNames.push_back("Ann");
              sNames.push_back("George");
              for(int i=0; i < sNames.size(); i++){
                   cout << sNames[i] << endl;
              }
              return 0;
         }

    As you see, there are no pointers, new and delete operations in this example. Same MFC classes are called CString and CArray.

  6. If you use a standard string or container class and need the pointer to its data. you can easily get it. All classes have correspondent methods or operators.

    MFC Examples:

         CString sSomeStr; 
         (LPCTSTR) sSomeStr; // char * pointer to the string buffer
    
         CArray <int,int> SomeArray;
         SomeArray.GetData( ); // int * pointer to the array buffer
    
         STL examples:
         string sSomeStr;
         sSomeStr.c_str(); // char * pointer to the string buffer
    
         vector <int> SomeArray;
         &SomeArray[0]; // int * pointer to the array buffer

    Remember about dangers! Use such conversions only if you really need it. For example, if you need to send pointer to a library function. It can be Win API function or others.

  7. When you need send to some big object to a function, use the reference instead of the pointer. At least you cannot change the memory address of reference.

  8. Use new type conversion operator static_cast instead of the old style conversion.

    Example:

         A* pA = new A;
         B* pB = new B;
         cout << ((B*)pA)->M << endl; // Compiler said "OK!"
    
         cout << static_cast<B*>(pA)->M << endl; // Compile said "Error!"
  9. Use constant modify where it is possible

    Example:

         int a = 5;
         const int* p1 = &a; // You cannot change pointed value
    
         int* const p2 = &a; // You cannot change pointer
    
         const int* const p2 = &a; // You cannot change anything
  10. Remember that every "new SomeType" operator needs "delete PointerSomeType" operator, and every "new SomeType[]" operator needs "delete[] PointerSomeType" operator.
  11. Remember that if your program works correctly, it does not mean that it has no pointer errors inside. Errors can appear on another computer in another time. Be accurate!

Announcement

I described only plane problems of using pointers in this article. I plan to continue this topic in the next article that would be called "Pointers and Classes".

Links

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here