edit: 2016-7-9

This article is written when I was in an introductory CS course. Inaccurate statements are corrected with this edit.


Since pointers are about to be covered in my computer science class, I would make a post about it, both for my own review, and for your reference. I will not try to make a comprehensive introduction to pointer, but I will make this post comparison based, so you can see how concepts are related by yourself.

Because pointer is a very important and complex feature in C/C++, my post may be incomplete or inaccurate, please feel free to point it out. I researched on the following reference before writing my post

Pointers

Pointer is simply something that points to a variable. In computer memory, this is the memory address of the variable.

int *ptr;
int int_var = 1;
ptr = &int_var;    // assign the address of int_var to ptr
computer memory
                    +--------+                +-----------+
                    |        |   points to    |           |
                    |   ptr ----------------> |  int_var  |
                    |        |                |           |
                    +--------+                +-----------+

memory address:       0xABCD                      0xBCDE
value:                0xBCDE                      1 (int)

A pointer is a variable whose value is the address of the variable it points to. We can get the address of a variable with the addressof operator &, and get the value of the variable it points to with the dereferencing operator \ . Dereferencing an uninitialized pointer causes undefined behavior.

int *ptr;
int int_var = 1;
ptr = &int_var;    // assign the address of int_var to ptr
std::cout << "the value of the variable the pointer points to: "
          << *ptr  // dereference ptr: get the value of the variable ptr points to
          << std::endl;
std::cout << "the value of the pointer, an address: "
          << ptr   // get the value of ptr
          << std::endl;
std::cout << "the address of the pointer stored in memory: "
          << &ptr  // get the address of ptr
          << std::endl;

int *uninit_ptr;
std::cout << "Dereferencing an uninitialized pointer"
          << "will crash the program"
          << *uninit_ptr  // dereference the pointer
          << std::endl;

In a 64-bit architecture, the size of a pointer is 8 bytes, and for a 32-bit architecture, it is 4 bytes. The size of the pointer is irrelevant to the size of the variable it points to. You can get the size of the pointer with sizeof operator. Parenthesis around operand is only required for a type name.

int *int_ptr;
double *double_ptr;
int int_ptr_size = sizeof int_ptr,
    double_ptr_size = sizeof double_ptr;  // should be size_t, but I will use int for simplicity

std::cout << "size of int_ptr: " << int_ptr_size << std::endl
std::cout << "size of double_ptr: " << double_ptr_size << std::endl;

std::cout << "the size of a pointer that points to an int and " <<
             "the size of a pointer that points to a double are " <<
             ((int_ptr_size == double_ptr_size) ? "the same!" : "different!" )
          << std::endl;

The sizeof operator returns the size in bytes of the variable in memory. For example, sizeof(int) would return 4 and sizeof(double) would return 8 on most modern computers. I will talk more about sizeof operator later in the distinction between pointers and arrays.

Pointer Arithmetic

#include <cassert>
int a[10];
int *p = a + 5;
int *a_4_ptr = a + 4, *a_6_ptr = a + 6;

assert(p - 1 == a_4_ptr && p + 1 == a_6_ptr);
consecutive chunks of memory

    +------+------+------+
    | a[4] | a[5] | a[6] |
    +------+------+------+
    ↑      ↑      ↑
    p-1    p      p+1

When you increment a pointer by 1, the value stored inside the pointer is incremented by the size of the type that the pointer points to, and in this case, sizeof(int).

#include <cassert>
int a[10];
int *a_4_ptr = a + 4, *a_6_ptr = a + 6;

assert(a_6_ptr - a_4_ptr == 2);
consecutive chunks of memory

    +------+------+------+
    | a[4] | a[5] | a[6] |
    +------+------+------+
    ↑             ↑
    a_4_ptr       a_6_ptr

pointer subtraction produce the difference in elements between the elements

The difference between a_4_ptr and a_6_ptr is 2 * sizeof(int), but the subtraction between pointers pointing to the same type produce the difference in elements between the elements.

* please refer to the book Chapter 5 Pointers and Arrays of The C Programming Language by Dennis Ritchie and Brian Kernighan, for a more detailed explanation

Pointers and Arrays

int a[5];
consecutive chunks of memory

    +------+------+------+------+------+
    | a[0] | a[1] | a[2] | a[3] | a[4] |
    +------+------+------+------+------+
    ↑
    a

array a is actually the address of the first element
of the array.
#include <cassert>
assert(a[0] == * a);
assert(&a[0] == a);

Initializing a character array with a string literal is equivalent to initializing with a list.

char charArray[] = "hello";
char charArray[] = {'h', 'e', 'l', 'l', 'o', '\0'};

Notice the array end with the character '\0', which is the terminator for C strings.

When we try to get the length of the string with strlen function from cstring library, it will return the length of the character array before the first '\0' character. But when we try to get the length of the character array with sizeof charArray / sizeof(char), it will return the size of the array represented in memory, which includes the '\0' character.

#include <cstring>
std::cout << strlen(charArray) << std::endl;
// prints 5
std::cout << sizeof charArray / sizeof(char) << std::endl;
// prints 6

Array works like pointers in that you can do pointer arithmetic and dereference. But you cannot reassign arrays.

const int arraySize = 5;
int array[arraySize] = {1, 2, 3, 4, 5};
for (int i = 0; i < arraySize; i++) {
    std::cout << * (array+i)  // equivalent to array[i]
              << std::endl;
}

// won't work. DOES NOT COMPILE
for (int i = 0; i < arraySize; i++) {
    std::cout << * (array++)
              << std::endl;
}

// This works, because arrayPtr is a pointer
int *arrayPtr = array;
for (int i = 0; i < arraySize; i++) {
    std::cout << * (arrayPtr++)
              << std::endl;
}

The sizeof operator works differently for pointers and arrays. Notice the difference of character pointer, character array and string literal.

const char *charPtr = "hello";
const char charArr[] = "hello";
std::cout << strlen(charPtr) << std::endl;
// returns 5, the length of the string

std::cout << sizeof charPtr << std::endl;
// returns the size of the pointer in bytes

std::cout << sizeof charArr << std::endl;
// returns 6, the size of character array in memory

std::cout << sizeof "hello" << std::endl;
// returns 6, the size of character array in memory

While it's okay to use array as the parameter of a function, it does not work as intended. Arrays decays into pointers, losing information about array dimension. Essentially a pointer is copied as the pointer to the first element of the array. The following function will not work, as it will always return the size of pointer divided by the size of int.

int getArraySize(int array[]) {
    return sizeof array / sizeof *array;
    // will always return the size of pointer divided by the size of int
}