The 5-minute Guide to C Pointers

Pointers, Addresses and Dereferencing

What is a pointer? What is a memory address? What does dereferencing a pointer mean?

A pointer is a variable that holds the location of, literally the address-of, a variable stored in computer memory. An address is the actual address-of a variable in the computer memory. Dereferencing a pointer means getting the value stored in the memory at the address which the pointer “points” to.

The * is the value-at-address operator, also called the indirection operator. It is used both when declaring a pointer and when dereferencing a pointer. I will show why it is helpful to read it as the value-at-address operator in the code below. When we talk about getting the value-at-addess of the pointer we are talking about getting the value stored in the memory at the address which the pointer “points” to.

The & is the address-of operator and is used to reference the memory address of a variable. When we declare a variable, three things happen. One, computer memory is set aside for the variable. Two, The variable name is linked to that location in memory. And three, the value of the variable is placed into the memory that was set aside. By using the & operator in front of a variable name we can retrieve the memory address-of that variable. It is best to read this operator as address-of and we will show why below.

Take the following code which shows some common notations for the value-at-address (*) and adress-of (&) operators.

If you take line 2 it reads a “declare a variable that points to the value-at-addess of an int type”. In other words declare a variable that holds a memory address where the value-at that memory address is a int. Line 4 we declare an int variable and assign it the literal value 1. Line 6 reads assign to the ptr pointer variable the address-of the variable val. This isn’t the value of the val variable (which is 1), this is the address-of the val variable in computer memory. Line 8 reads assign to the deref variable the value-at-address stored in the ptr pointer variable. We are getting the int value at the address stored in the ptr pointer, which in this case is 1. And finally we print out the deref variable on line 9 which prints 1.

Reading the operators as value-at-address (*) and address-of (&) helps to keep track of what is happening in any given line of code. I have found that sounding out the operators address-of and value-at-address helps to build a clearer mental model of what the code is doing. Soon it becomes second nature.

Think about pointers, addresses and values like envelopes, mailing addresses and houses. A pointer is like an envelope, on it we can write a mailing address. An address is like the mailing address, it is the location. The dereferenced value is like the house at the address, it is the value. A location represents the house but isn’t the house itself. We could erase the address on the envelope and write a different one if we wanted but that doesn’t change the house.

Using Pointers and Using Addresses

One question that comes up is where do you use a pointer versus where do you use an address? Let’s answer this with some code.

Run that code and we get:

dereference *ptr = 1
dereference address of val *(&val) = 1

We create an int pointer, an int variable val, and assign the address-of the val variable to out pointer. We print out the dereferenced value of our pointer. And finally we print out the dereferenced value of the address-of val. This notation may look a little strange but remember to sound out the operators and it says to get the value-at-address for the address of val.

The point of this piece of code is to show that an address can be used wherever a pointer is used. An address can even be treated as a pointer and deferenced directly, with the proper parentheses notation.

Void Pointers, NULL pointers and Unintialized Pointers

A pointer can be declared to be a void type. A pointer can be set to NULL or 0. And a pointer variable that has been declared but not yet assigned a value is considered uninitialized.

Run the above code and you should get something like the output below although with different memory addresses.

iptr=0x7fff94b89c6c, vptr=0x7fff94b89c6c
*backptr=1
uninit=0x7fff94b89d50, nullptr=(nil)

On line 1 we leave our int pointer uninitialized. All pointers, like other variables, are uninitialized until they are assigned a value. Uninitialized pointers hold random memory addresses. Pointers can be assigned the literal value 0, an address-of a variable, or the value of another pointer. Line 2 we assign 0 to our pointer. I use the terms NULL pointer and pointer assigned a literal value of 0 interchangeably. Line 3 we declare void pointer type. Then an int value and a couple of int pointers on lines 4-6.

Lines 9-11 we assign a reference to one of our int pointers and then we assign that int pointer to our void pointer. Void pointers can hold any pointer type. They are mostly used for generic purposes such as data structures. Notice on line 11 we print out the locations of the both the int pointer and the void pointer. They now point to the same memory address. All pointers just hold memory addresses. The types they are declared are used only when dereferencing pointer values.

On lines 15-16 we assign our void pointer back to our backptr int pointer. Then we dereference and print the value pointed to by castptr, which is 1.

Line 19 is interesting. Here we print out the values of our uninitialized and NULL pointers. Notice that our uninitialized pointer has a memory location. This is a garbage location. Any variable that is declared but not initialized will have a garbage memory location. Pointers are no exception. There is no telling what is at that location in memory. This is the reason why you should never try to dereference an uninitialized pointer. Best case you get garbage back and you have a fun time trying to debug your program. Worst case the program crashes badly.

A 0 value is the only literal value that can be assigned to a pointer without generating a warning or using an explicit cast. The NULL macro from stdlib.h can also be used to assign a literal value of 0 to the pointer. Assigning NULL or 0 is useful as a placeholder until you are ready to use the pointer. The prevents the computer from assigning a random memory location. NULL pointers should never be dereferenced though as doing so will cause undefined behavior. On many systems it will caue a segmentation fault.

Pointers and Arrays

Arrays in C are contiguous blocks of memory that hold multiple objects of a specified type. Contrast that with a pointer that holds a single memory location. Arrays and pointers are not the same thing and are not interchangeable. That being said an array variable does point to the memory address of the first element of the array.

An array variable is constant. You can’t assign a pointer to an array variable, even if the pointer variable actually points to the same or a different array. You also cannot assign one array variable to another. You can assign an array variable to a pointer though and that is where things get confusing. When assigning the array to the pointer we are actually assigning the address of the first element in the array to the pointer.

On line 1 we initialize the array of ints. On line two we intiailize the int pointer and assign the array variable to it. Since the array variable actually is the memory address of the first element in the array, we have assigned the memory address of the first element in the array to the pointer. This is the same as doing int *ptr = &myarray[0], explicitly stating the address-of the first element in the array. Notice the pointer has to be the same type as the elements of the array, unless the pointer is a void pointer.

Pointers and Structs

Just like an array a pointer to a struct holds the memory address of the first element in the struct. And like arrays pointers to structs must be declared to point to the struct type or be void type.

On lines 1-6 we declare the struct person, a variable to hold a person struct, and a pointer to a person struct. Line 8 we assign a literal int to the age member. Line 9-10 we declare a char pointer to a literal char array and then assign that to the struct name member. Line 11 we assign a reference to the first person struct to our struct pointer variable.

Line 13 we print out the age and name of our struct instance. Notice the two different notations, the . and the ->. With the age field we are accessing the struct instance directly and so we use the . notation. With the name field we are using our pointer to the struct instance and so we use the -> notation. This would be the same as doing (*ptr).name where we first derefence the pointer and then access the name field.

Pointers to Pointers

A pointer holds the address-of a variable. That variable can be another pointer. Take the following code:

When run you should get output similar to this but with different memory addresses.

&ptr=0x7fff390fa6f8, &val=0x7fff390fa70c
ptr2ptr=0x7fff390fa6f8, *ptr2ptr=0x7fff390fa70c, **ptr2ptr=1

Lines 1-2 are declaring and int variable val and an int pointer variable ptr. Line 5 is new. Here we are saying that we have a variable ptr2ptr that holds the value-at-address of another *int type. That int type in turn holding the value-at-address of an int type. In other words we have an address that when we dereference it we get another address. When we dereference that address we get an int value. There is no limit to the levels of indirection this can go but usually after 2-3 indirections it becomes mentally prohibitive to clearly understand what code is doing.

Line 6 we assign the ptr variable the address-of the val variable. We have seen this before. Line 7 we assign the ptr2ptr variable the address-of the ptr pointer variable. Double indirection. The ptr2ptr variable stores the address-of ptr which in turn stores the address-of val. Line 8 we print out the address-of the ptr and val variables. Line 9 we print out the value stored in ptr2ptr which is the same as &ptr. When we dereference that we get the address of val. What that is dereferenced we get the value 1. *ptr2ptr reads get the value-at-address stored in ptr2ptr which is the address-of ptr. **ptr2ptr reads get the value-at-address store in ptr which is also an address and then get the value-at-address of that address.

Conclusion

Hope this brief overview helps with some of the different types of pointers you will see. In a later post I might go into another type of advanced pointer that is used, the function pointer.

Questions and comments are always welcome.

Update 1: Thanks to phao, mauchter and jorgem from hacker news for improvement suggestions. Made some changes to make some explanations more clear.
Update 2: Fixed spelling errors. Fixed unclear wording. Tried to make NULL pointer explanations more clear. Thanks to everybody for the comments and suggestions.
Update 3: Added a pointers to pointers section. Made descriptions of operators more clear.
Update 4: Thanks to JoachimS and Dinah for catching more spelling errors. I need to take a class in proofreading. Thanks to Radu for helping to clarify the value-at-address operator.
Update 5: Added a section about using pointers versus using addresses.



40 Responses to “The 5-minute Guide to C Pointers”

  1. dirtycoder says:

    Thank you very much for your post. You have written it in a very simple and understandable way. Pointers were in my opinion the hardest subject of C until I read your guide.

  2. isaac says:

    Spelling: dereference -> “Then we deference our int pointer variable which assigns the value at the memory address stored in the int pointer to our deref variable.”

  3. Jon says:

    s/decaled/declared/

  4. Jon says:

    “Line 2 we declare a NULL pointer” is confusing, because the *type* of the pointer is not NULL, but the value. You’ve used the prefix to mean type in other parts (e.g. ‘void pointer’)

  5. Many thanks for explaining in such simple language.
    Pointers had always been the pain area for me, but your article has changed that totally for me.
    Cheers :)

  6. Yasith says:

    Great article. Looking forward to reading the next one.

  7. sulfide says:

    Hi nice tutorial, one suggestion though is that many people learning c pointers are unsure some of the merits of why they should use them when they encounter these beginner tutorials; maybe an example in this guide that shows something like the following (excuse me my c is rusty)

    #include

    struct person {
    int age;
    char *name;
    };

    void print1(struct person p) {
    printf(“age: %d\nname: %s\nsize: “,p.age, p.name);
    printf(“%zd\n”,sizeof(p));
    }

    void print2(struct person *p) {
    printf(“age: %d\nname: %s\nsize: “,p->age, p->name);
    printf(“%zd\n”, sizeof(p));
    }

    int main() {
    struct person p = {10,”moo”};
    struct person* ptr_p = &p;

    print1(p);
    printf(“\n\n”);
    print2(ptr_p);

    return 0;
    }

    • Colin Whitehouse says:

      You should also show using a reference too:

      void print3(struct person& p) {
      printf(“age: %d\nname: %s\nsize: “,p.age, p.name);
      printf(“%zd\n”,sizeof(p));
      }

      int main() {
      struct person p = {10,”moo”};
      print3(p);
      }

      should also mention that print1() cause copying of the structure to a temporary (pass by value).

      • Colin Whitehouse says:

        To clarify, that would be C++.

      • Dennis Kubes says:

        One thing I wish I could make more clear is the concept of address of in terms of dereferencing.

        C++ has an actual reference type. C is more informal and has address of but not a reference type. I wanted to tie that dereferencing in C is getting the address of a reference. I wish I had a better analogy for this.

  8. […] I came across another post doing the same thing. For what it’s worth, I think it’s well-written and I’m […]

  9. Jim Dibb says:

    This is not true:
    “The only literal value a pointer can be assigned is 0.”
    Pointers can be assigned any value. The very common use case for doing this is when HW registers are mapped into memory addresses (which are fixed values.)

    • Dennis Kubes says:

      Correct you can actually assign other literal values to a pointer.

      int *pt = 1; // this will generate a warning
      int *castptr = (int *)1; // no warning, explicit cast.

      I should have said 0 is the only literal that can be used without generating a warning or using an explict cast. I have updated the post to be more clear.

  10. fdsa says:

    But what about declaring an array of functions that use themselves as arguments and return values?

  11. JoachimS says:

    Aloha!

    Great post!
    A small typo: “Lines 9-11 we assign a reference to on of our int pointers”. “on” should probably be “one”.

  12. […] in C can be tough for those new to the language, so Dennis Kubes wrote “The 5-minute Guide to C Pointers,” and put it on his blog. Only four code snippets are needed for Kubes to illustrate his […]

  13. Radu says:

    “Dereferencing a pointer means getting the value-at-address stored in the pointer.” If I didn’t know what pointers were the above sentence would have totally mislead me.
    I think it would be better to say something like :
    “Dereferencing a pointer means getting the value stored in the memory at the address which the pointer ‘points’ at. ”
    I think saying it like this makes it clearer and actually gives the reader a clue why the pointer is called like it is. Also , in my opinion, makes it easy to remember and forms some kind of mental map to relate to when you try to remember what a pointer is all about. It points to something :)

    • Dennis Kubes says:

      You’re right. I have not explained the (*) value-at-address operator at that point and it does make it more confusing. I will clarify.

      One thing that helped me with pointers was sounding out the operators to understand what was happening. I tend to think of myself as a visual person but in the case of pointers, sounding it out address-of and value-at-address actually helped me to build a clearer mental model.

  14. Dinah says:

    Small typo: “Reading the operators as value-at-address (*) and adress-of (&) helps to keep track of what is happening in any given line of code.”

    adress-of –> address-of

  15. […] It is helpful to think of everything in C in terms of computer memory. Let’s think of computer memory as an array of bytes where each address in memory holds 1 byte. If our computer has 4K of memory for example, it would have 4096 elements in the memory array. When we talk about pointers storing addresses, we are talking about a pointer storing an index to an element in the memory array. Dereferencing a pointer would be getting the value at that index in the array. All of this is of course a lie. How operating systems handle memory is much more complex than this, but the analogy provides an easy way to think about memory in C. Confused about pointers, addresses and dereferencing? Take a look at this 5-Minute Guide to Pointers. […]

  16. Flo says:

    Ok, it’s only 5min, but no use of ptr++?

  17. […] the same output. If you are having trouble understanding the previous code, take a look at the 5-Minute Guide to Pointers for a deeper explanation of pointers, addresses, and […]

  18. […] http://denniskubes.com/2012/08/16/the-5-minute-guide-to-c-pointers/ This entry was posted in Developer. Bookmark the permalink. ← Making money with Android apps […]

  19. […] La guía de cinco minutos a los punteros en C, para el programador que todos llevamos dentro; está en inglés pero es una de las mejores explicaciones que he visto y eso que no soy programador (DennisKubes) […]

  20. […] The 5 Minutes Guide to Pointers in C — fuer Leute wie mich, die damals etwas laenger brauchten. Dieser Eintrag wurde veröffentlicht in Allgemein. Bookmark the Permanent-Link. Kommentieren oder ein Trackback hinterlassen: Trackback-URL. « “Nach diesem Urteil sollten wir uns über die NSU nicht mehr wundern” Kleine Kamera, grosse Bilder » […]

  21. […] The 5 Minutes Guide to Pointers in C — fuer Leute wie mich, die damals etwas laenger brauchten. Dieser Eintrag wurde veröffentlicht in Allgemein. Bookmark the Permanent-Link. Kommentieren oder ein Trackback hinterlassen: Trackback-URL. « Was alles gehen wuerde, wenn man wollte […]

  22. […] 英文原文: Dennis Kubes,编译:伯乐在线 – 唐尤华 […]

  23. […] 英文原文: Dennis Kubes,编译:伯乐在线 – 唐尤华 […]

  24. […] This is a good post talking about C pointers and addresses issues. See the original post. […]

  25. Elie says:

    What about this code:
    void swap(int *pa, int *pb)
    {
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
    }

    int main()
    {
    int x, y;
    x = 3; y = 7;
    printf(“x is: %d and y is: %d.\n”, x, y);
    swap(&x, &y);
    printf(“x is: %d and y is: %d.\n”, x, y); // x == 7, y == 3
    }

    If I read * as value-at-address, then why should this code work?
    Could you write a section about functions and pointers.

  26. Stephen Breton says:

    Thank you! Focusing on the translations, like what * and & mean in English was what I needed to finally understand pointers.

  27. METIN says:

    Hi,

    sorry but even I’m beginner in C, I find you following statements confusing and also errhmm…wrong:

    “An array variable is constant. You can’t assign a pointer to an array variable, even if the pointer variable actually points to the same or a different array. You also cannot assign one array variable to another. You can assign an array variable to a pointer though and that is where things get confusing”

    An array variable is of course not constant, it’s variable, not?
    And the second statement, Why should one assign a Pointer to Array, it’s normally the other way around.
    One assigns the Array to Pointer of the same type, and pointer holds the address of the first variable in the array.

    And the next statement: “You also cannot assign one array variable to another” Why? I think one can assign one array variable to another if they are of same type and size.

    Well…..I’m still reading :)

    • Dennis Kubes says:

      > An array variable is of course not constant, it’s variable, not?
      An array variable is constant in C. If you declare two arrays of the same type and length and try to assign one to another you will get an invalid array assignment compile error. For example, this won’t compile:

      #include
      int main(int argc, char **argv)
      {
      int testAr[3] = {0,1,2};
      int testAr2[3] = {3,4,5};
      testAr = testAr2;
      }

      > Why should one assign a Pointer to Array, it’s normally the other way around.
      You shouldn’t / can’t assign a pointer to an array. In fact you can assign anything to an array variable once it has been declared. You can have a pointer variable to an array and that pointer can change but a declared array variable cannot.

      > One assigns the Array to Pointer of the same type, and pointer holds the address of the first variable
      You can use an array in locations, functions parameters for instance, that use a pointer of the same type as the array. This is because an array variable holds the address of the first array element. Arrays passed into functions are always passed as pointers in C. Copy by value in this case means the pointer is copied into the function, not the entire array.

      Hope this helps.

      • METIN says:

        Hmmm…..I think you’re right…..one should be quite tender & sensible looking at pointers/arrays….with calm thinking and bit more reading I now know more….Many Thanks!

  28. Yeonho Yoon says:

    Great article! It was very clearly written.

    I think there’s a typo in example at ‘Void Pointers, NULL pointers and Unintialized Pointers’,
    line 20 and 22 reads
    // printf("*nullptr=%d\n", nullptr);
    perhaps it should be

    // printf("*uninit=%d\n", *uninit);
    // printf("*nullptr=%d\n", *nullptr);

  29. […] remember the exact moment in 1990 when I fully understood what a pointer was and a pointer to pointer (a powerful abstraction in C and C++ programming). I remember whom I […]

Leave a Reply