Search

6.7 — Introduction to pointers

In lesson 1.3 -- a first look at variables, we noted that a variable is a name for a piece of memory that holds a value. When our program instantiates a variable, a free memory address is automatically assigned to the variable, and any value we assign to the variable is stored in this memory address.

For example:

When this statement is executed by the CPU, a piece of memory from RAM will be set aside. For the sake of example, let’s say that the variable x is assigned memory location 140. Whenever the program sees the variable x in an expression or statement, it knows that it should look in memory location 140 to get the value.

The nice thing about variables is that we don’t need to worry about what specific memory address is assigned. We just refer to the variable by its given identifier, and the compiler translates this name into the appropriately assigned memory address.

However, this approach has some limitations, which we’ll discuss in this and future lessons.

The address-of operator (&)

The address-of operator (&) allows us to see what memory address is assigned to a variable. This is pretty straightforward:

On the author’s machine, the above program printed:

5
0027FEA0

Note: Although the address-of operator looks just like the bitwise-and operator, you can distinguish them because the address-of operator is unary, whereas the bitwise-and operator is binary.

The dereference operator (*)

Getting the address of a variable isn’t very useful by itself.

The dereference operator (*) allows us to access the value at a particular address:

On the author’s machine, the above program printed:

5
0027FEA0
5

Note: Although the dereference operator looks just like the multiplication operator, you can distinguish them because the dereference operator is unary, whereas the multiplication operator is binary.

Pointers

With the address-of operator and dereference operators now added to our toolkits, we can now talk about pointers. A pointer is a variable that holds a memory address as its value.

Pointers are typically seen as one of the most confusing parts of the C++ language, but they’re surprisingly simple when explained properly.

Declaring a pointer

Pointer variables are declared just like normal variables, only with an asterisk between the data type and the variable name.

Syntactically, C++ will accept the asterisk next to the data type, next to the variable name, or even in the middle. Note that this asterisk is not a dereference. It is part of the pointer declaration syntax.

However, when declaring multiple pointer variables, the asterisk has to be included with each variable. It’s easy to forget to do this if you get used to attaching the asterisk to the type instead of the variable name!

For this reason, when declaring a variable, we recommend putting the asterisk next to the variable name.

Best practice: When declaring a pointer variable, put the asterisk next to the variable name.

However, when returning a pointer from a function, it’s clearer to put the asterisk next to the return type:

This makes it clear that the function is returning a value of type int* and not an int.

Best practice: When declaring a function, put the asterisk of a pointer return value next to the type.

Just like normal variables, pointers are not initialized when declared. If not initialized with a value, they will contain garbage.

One note on pointer nomenclature: “X pointer” (where X is some type) is a commonly used shorthand for “pointer to an X”. So when we say, “an integer pointer”, we really mean “a pointer to an integer”.

Assigning a value to a pointer

Since pointers only hold addresses, when we assign a value to a pointer, that value has to be an address. One of the most common things to do with pointers is have them hold the address of a different variable.

To get the address of a variable, we use the address-of operator:

Conceptually, you can think of the above snippet like this:

This is where pointers get their name from -- ptr is holding the address of variable value, so we say that ptr is “pointing to” value.

It is also easy to see using code:

On the author’s machine, this printed:

0012FF7C
0012FF7C

The type of the pointer has to match the type of the variable being pointed to:

Note that the following is also not legal:

This is because pointers can only hold addresses, and the integer literal 5 does not have a memory address. If you try this, the compiler will tell you it cannot convert an integer to an integer pointer.

C++ will also not allow you to directly assign literal memory addresses to a pointer:

The address-of operator returns a pointer

It’s worth noting that the address-of operator (&) doesn’t return the address of its operand as a literal. Instead, it returns a pointer containing the address of the operand, whose type is derived from the argument (e.g. taking the address of an int will return the address in an int pointer).

We can see this in the following example:

On Visual Studio 2013, this printed:

int *

(With gcc, this prints “pi” (pointer to integer) instead).

This pointer can then be printed or assigned as desired.

Dereferencing pointers

Once we have a pointer variable pointing at something, the other common thing to do with it is dereference the pointer to get the value of what it’s pointing at. A dereferenced pointer evaluates to the contents of the address it is pointing to.

The above program prints:

0012FF7C
5
0012FF7C
5

This is why pointers must have a type. Without a type, a pointer wouldn’t know how to interpret the contents it was pointing to when it was dereferenced. It’s also why the type of the pointer and the variable address it’s being assigned to must match. If they did not, when the pointer was dereferenced, it would misinterpret the bits as a different type.

Once assigned, a pointer value can be reassigned to another value:

When the address of variable value is assigned to ptr, the following are true:

  • ptr is the same as &value
  • *ptr is treated the same as value

Because *ptr is treated the same as value, you can assign values to it just as if it were variable value! The following program prints 7:

A warning about dereferencing invalid pointers

Pointers in C++ are inherently unsafe, and improper pointer usage is one of the best ways to crash your application.

When a pointer is dereferenced, the application attempts to go to the memory location that is stored in the pointer and retrieve the contents of memory. For security reasons, modern operating systems sandbox applications to prevent them from improperly interacting with other applications, and to protect the stability of the operating system itself. If an application tries to access a memory location not allocated to it by the operating system, the operating system may shut down the application.

The following program illustrates this, and will probably crash when you run it (go ahead, try it, you won’t harm your machine):

The size of pointers

The size of a pointer is dependent upon the architecture the executable is compiled for -- a 32-bit executable uses 32-bit memory addresses -- consequently, a pointer on a 32-bit machine is 32 bits (4 bytes). With a 64-bit executable, a pointer would be 64 bits (8 bytes). Note that this is true regardless of what is being pointed to:

As you can see, the size of the pointer is always the same. This is because a pointer is just a memory address, and the number of bits needed to access a memory address on a given machine is always constant.

What good are pointers?

At this point, pointers may seem a little silly, academic, or obtuse. Why use a pointer if we can just use the original variable?

It turns out that pointers are useful in many different cases:

1) Arrays are implemented using pointers. Pointers can be used to iterate through an array (as an alternative to array indices) (covered in lesson 6.8).
2) They are the only way you can dynamically allocate memory in C++ (covered in lesson 6.9). This is by far the most common use case for pointers.
3) They can be used to pass a large amount of data to a function in a way that doesn’t involve copying the data, which is inefficient (covered in lesson 7.4)
4) They can be used to pass a function as a parameter to another function (covered in lesson 7.8).
5) They can be used to achieve polymorphism when dealing with inheritance (covered in lesson 12.1).
6) They can be used to have one struct/class point at another struct/class, to form a chain. This is useful in some more advanced data structures, such as linked lists and trees.

So there are actually a surprising number of uses for pointers. But don’t worry if you don’t understand what most of these are yet. Now that you understand what pointers are at a basic level, we can start taking an in-depth look at the various cases in which they’re useful, which we’ll do in subsequent lessons.

Conclusion

Pointers are variables that hold a memory address. They can be dereferenced using the dereference operator (*) to retrieve the value at the address they are holding. Dereferencing a garbage pointer may crash your application.

Best practice: When declaring a pointer variable, put the asterisk next to the variable name.
Best practice: When declaring a function, put the asterisk of a pointer return value next to the type.

Quiz

1) What values does this program print? Assume a short is 2 bytes, and a 32-bit machine.

Show Solution

2) What’s wrong with this snippet of code?

Show Solution

6.7a -- Null pointers
Index
6.6 -- C-style strings

180 comments to 6.7 — Introduction to pointers

  • Nguyen

    Hi,

    Here are what I understand about the code step by step.  I think I understand everything well except the last statement.  I'm going to comment what I think is right in the last statement.  

    Please let me know what I understand about *ptr is correct.  

    Thanks, Have a great day.

  • Sagar

    Memory addrese is of type integer, right?
    And we can assign any integer value to *ptr(which is an integer pointer and we are dereferencing it), right?

    So in quiz ques 2,
    *ptr=&value;  (&value is of type integer), why does the compiler complain?

    Thanks.

    • nascardriver

      Hi Sagar!

      > Memory addrese is of type integer, right?
      No, integers can be any size and addresses can be any size. Usually integers are 4 bytes and addresses 4 or 8 bytes. If you need a data type that's guaranteed to be the same size as an address use @std::uintptr_t. It's an unsigned integral type.

      > &value is of type integer
      It's of type int*.

      • Sagar

        What's the basic difference between type int and int* ?
        int* is a pointer type I know but can't we assign address of any variable to a integer variable like
        int x ;
        int y=&x;
        And if we can, then the statement(*ptr=&value) in ques 2 should be valid

        • nascardriver

          int is a integer, int* is an address in memory at which an integer is stored.

          > but can't we assign address of any variable to a integer variable

          You might be able to store an address in an int on some systems, but that's based on luck.

  • Samira Ferdi

    Alex and nascardriver! Does pointer just support copy assignment? Why?

    I got *ptr = 7; as warning: 'ptr' is used unitialized in this function. Why?

    • nascardriver

      Hi Samira!

      You're trying to assign 7 to whatever @ptr is pointing to. But since you didn't point @ptr to anything it's uninitialized and will cause undefined behavior.

      Will call the @operator= of the object or type @ptr is pointing to.

  • DecSco

    Hey there,
    it is stated that putting the asterisk next to the variable name is preferred, i.e.

    This seems unintuitive to me. First of all, I assume that I'll write functions which return a pointer more often than declaring several pointers in one line, so that

    would be a more common use case than

    If that assumption is true, then putting the asterisk next to the data type would lead to increased consistency.
    Also, putting the asterisk next to the variable name makes it look like a dereference, which could be avoided.
    And finally, for

    the part before the whitespace indicates the data type, the part afterwards the variable name. I would argue, that

    and

    are distinct data types, rather than that the asterisk is part of the variable name.
    Of course, I can do it whichever way I want, but as no explanation is stated above, I would like to hear your reasoning.

    • Alex

      The recommendation for putting * next to the variable name and not the type is based solely on the multiple variable instantiation use case. The Google style guide says either way is fine, and recommend avoiding the multiple declaration case whenever mixed types are involved (which is a good recommendation). Practically speaking, either way is fine, and it's really your preference that dictates which you choose.

  • Ishak

    It didn't crash. it just printed out 1528349827. I'm disappointed; I was expecting fireworks.

  • VEDDEV

    Dear LearnCPP,

    May I ask if "Something *somethingPtr;" means the struct Something is initialized as a pointer in somethingPtr?

    • nascardriver

      Hi VEDDEV!

      Creates an uninitialized pointer that's supposed to be pointing to an object of type @Something. Variables should always be initialized.

      • VEDDEV

        Dear LearnCPP,

        Would you mind to elaborate further the usage of array pointers because I'm curious of getting nX, nX, and nZ via the array pointers.

        I started to learn programming by manipulating memory of games. However, I have moved on to replicate game code by understanding how the game works so I can tell the game what to do like getting Entities' XYZ coords to create a map of where enemies are.

        And, are you scraping every page of LearnCPP to be notified if there are new comments or you are a friend of Alex?

        • nascardriver

          > Would you mind to elaborate further the usage of array pointers because I'm curious of getting nX, nX, and nZ via the array pointers.

          > are you scraping every page of LearnCPP to be notified if there are new comments
          Yes

          • VEDDEV

            You elaborated it very clearly, thanks! I'll try to apply this knowledge to practical code, game memory manipulation is a damn hard topic :<

          • Rohit

            Wow I assumed you must have struck a deal with Alex to give you some privileges in the comments. Never woulda thought you manually open every page to find new comments. That's some dedication!

  • Mireska

    This doesn't work; I don't particularly want it to, but I do want to understand exactly why this breaks the program.
    I think it's similar to the 'garbage pointer' example; we get 'permission' for a pointer to a pointer, but what the pointed-to pointer points to (is there clearer wording for this? :$), we never got 'permission' to access, it's uninitialized garbage, and to protect other applications the OS shuts my program down.

    Alternatively, I thought that maybe whatever value ends up being accessed cant be implicitly cast to an int, causing the program to crash, but I'd expect the compiler to give me a warning or an error if that's the case, so my first guess seems more likely to me.

    Am I understanding this correctly, or is this a different case? Thanks in advance!

    EDIT: Messing about a bit more, it seems that dereferencing a pointer to location CDCDCDCD crashes it, which is (on my machine) the default for an uninitialized pointer. The garbage pointer in the example here points towards CCCCCCCC on my machine, also crashing on running. Are these default locations to prevent garbage output? Are they the 'protected' memory? Why is the one CDCDCDCD and the other CCCCCCCC (Probably has to do with one being a pointer and the other a pointer to a pointer?)?

    I'm not sure if this knowledge will be useful to me at all, but at this point I can't back down from trying to understand what's going on!

    • nascardriver

      Hi Mireska!

      Your program consists of many different memory pages. Those pages have different flags:
      - read (You're allowed to read from this page)
      - write (You're allowed to write to this page)
      - execute (You can execute code in this page)
      Executable pages are usually not writable. Constants are stored in read-only pages. Non-readable pages are rare. Your program only uses as many pages as it needs, all addresses outside those pages cause a crash.

      The binary produced by the code below has just 0x1140 readable addresses. That's 0.0001028% of all possible addresses in an x86 program, let alone x64. That's why it's unlikely to end up with a valid address.

      • Mireska

        How does the binary produce 0x1140 readable adresses? Or is that something unexplainable in a comment?

        *ptrptr is undefined, but consistently gives me CDCDCDCD as output. What does this mean? I suppose, by definition, it doesn't mean anything due it being undefined, but it just feels weird to me that it still works,  Trying to access, to dereference what is in location CDCDCDCD (which is not a good idea) causes the program to crash. Should I just see CDCDCDCD (and CCCCCCCC) as not memory locations at all, even though they, to my untrained eye, look like one? I suppose that makes it make sense that it crashes, as you can't go to a place that doesn't exist.

        But then I still don't quite understand the 'A warning about dereferencing invalid pointers' part of the tutorial; this says the crash could be the OS protecting other applications. Is the CCCCCCCC/CDCDCDCD then just a placeholder name for an actual, protected location given to all uninitialized pointers? I'm sorry if I'm not making much sense; I'm new to the world of 0s and 1s and the shift from real to virtual is sometimes hard to make.

        • nascardriver

          > How does the binary produce 0x1140 readable adresses?
          The compiler decided that this is the amount of memory required to run the program.

          > consistently gives me CDCDCDCD
          Some compilers initialize all memory with a specific value (In your case 0xCD, msvc uses 0xCC) when building in debug mode to make it easier to sport uninitialized variables.

          > should I just see CDCDCDCD (and CCCCCCCC) as not memory locations at all
          They are memory addresses, they're just not in use by your program so you can't access them. It's like walking through a street and looking for a house number that doesn't exist, although it could potentially exist.

          > OS protecting other applications
          Each process has it's own virtual address space that is not shared with other processes. How this is done is OS specific.

  • could I know why the function is  void foo(int *&p) but not void foo(int *p)?

    • nascardriver

      Hi Aron!

      Sample output
      0x559c2bd6ae70
      0

      In your code, if you didn't pass @p by reference there would be no way to assign a value to @p.

    • Alex

      Nascardriver's answer is good, but doesn't really nail the point succinctly.

      The *& is syntax that allows the function to potentially modify the value of the pointer (even though it doesn't).

      We're using this to trick the compiler into thinking foo() might assign a value to the pointer, so it won't complain about p being uninitialized.

      Of course, we don't actually do that, so p is still uninitialized.

      • #include <iostream>

        void foo(int *p)
        {
        }

        int main()
        {
            int *p; // Create an uninitialized pointer (that points to garbage)
            foo(p); // Trick compiler into thinking we're going to assign this a valid value
                
            std::cout << *p; // Dereference the garbage pointer

            return 0;
        }

        i run it in codeblocks(without using *& operator in foo i used only * operator) but compiler is not complaining at compile time so what is the need to trick the compiler.although it may crash at run time.

        • Akshay Gajbe

          some compiler like visual studio will through an error on compile time if you try to perform any operation on uninitialized pointer variable.

          void foo(int *p)
          {
          }
          int main()
          {
              int *p;
              foo(p);
          }

          pointer is variable which holds the address of another variable.
          but since here uninitialized pointer variable is passing as pass by value
          so it is like
          int *p;
          int *p2 = p;
          here p2 is supposed to hold the address which is stored in p but p itself is not holding any address  
          Thats why visual studio compiler will through an error. To make this code in compile state we are cheating with compiler by writing the code like this 'void foo(int *&p)', here p is passing as a pass by reference so the compiler will think that foo will modify the value so it will not complain.

          I dont have any idea about the codeblock, may be internally codeblock compiler is assigning nullptr to uninitialized pointer. Thats why the code is running fine with  void foo(int *p) in codeblock.

  • Peter Baum

    This comment is about editing comments: if you have

    in the comment and then edit the comment, those tags don't seem to work anymore.  My workaround was to copy the comment, delete the comment, and then create a new comment where I can paste the old edited comment.

    In fact, that is how I got those tags to show properly in this comment!

    • nascardriver

      Hi Peter!

      Refresh the page after editing the comment. It's just a client-side issue.

      • Peter Baum

        Aha! Yes, my mistake.  Thank you.

        • Alex

          Not your mistake. It's something I need to fix. 🙂

          The reason this happens is because when the lesson page is generated, the syntax highlighter plugin generates the code for all of the code snippets on the page. When you edit your comment dynamically (as text), the content of the comment gets updated dynamically, but the syntax highlighter doesn't get run again (it was never built to do dynamic updating). So you end up seeing the raw text rather than the snippetized version.

          I need to figure out how to make the page refresh automatically after editing a comment.

  • Peter Baum

    In the section "Assigning a value to a pointer" I think it might be helpful to go over the various combinations of ordinary variable names and the * and & operators. Consider the following code and lines 7 through 10 in particular.  I suggest an explanation of why each of those lines works or does not work.

  • Peter Baum

    In section "Assigning a value to a pointer" - in the sentence

    "One of the most common things to do with pointers is have them hold the address of a different variable."

    I did not understand why the word "different" was used.  Wouldn't it be very rare to have an application that required a pointer to point to itself?  In any case, an example of such an application might be interesting and helpful.

    • nascardriver

      Hi Peter!

      > an example of such an application
      I doubt the existence of such an application.

      • Peter Baum

        Yes, I struggled with it as well, which is why I wondered why "different" was in there at all.  The only thing I could think of was that doing so might be used to represent a null pointer that would always guaranteed that the address was accessible.

        • nascardriver

          That's what nullptr is for (Lesson 6.7a)

          • Peter Baum

            I had not read the next lesson yet, so I went there at your good suggestion.  However, I didn't see where Alex explained how nullptr was implemented. That would be both helpful and interesting.  

            I'm the kind of person who likes to know how things work.  Another example is with the descriptions about scope.  Rather than just memorizing a bunch of rules, it is easier for me to learn if I understand what the compiler and linker are actually doing.

            • nascardriver

              I am worried there's not much to research about it's implementation.

              Quote from cplusplus.com:
              "Even though nullptr_t it is not a keyword, it identifies a distinct fundamental type"

    • Alex

      > "One of the most common things to do with pointers is have them hold the address of a different variable."

      The word "different" was used to indicate that pointers are rarely (never?) self-referential. Having the pointer point to null/nullptr is also common, but not what this particular statement is referencing.

      As Nascardriver mentions, nullptr_t is a unique type that was designed to help disambiguate null pointers from integer 0. There's not much else to say about it, as the implementation details are compiler specific.

  • david

    Hi Alex,  
    is there a convention for naming pointers?
    especially those that point to dynamically allocated memory.
    what would be an appropriate prefix or suffix?

    • nascardriver

      Hi David!

      There any many naming conventions.
      I like hungarian notation (Modified to my liking) where pointers start with a lower case 'p' followed by the actual name.

      Here's a table of 'normal' hungarian notation:
      https://www.cse.iitk.ac.in/users/dsrkg/cs245/html/Guide.htm

    • Alex

      There isn't a standard prefix for these kinds of things. As Nascardriver points out, some users use a "p" prefix or "Ptr" suffix to denote that a variable is a pointer. This doesn't tell you whether the pointer is pointing to dynamically allocated memory or not though.

      Personally, I don't think adding the prefix or suffix is valuable, since the strong typing system will rarely let you interchange a pointer and non-pointer value. Better to focus on making your variable names actually good.

  • PLZ ALEX reply this .

    as per explanation of quiz question 2 in 6.7 introduction to pointers

    int value = 5;
    int *ptr = &value; // ptr points to value

    *ptr = 7; // *ptr is the same as value, which is assigned 7(HOW A '7' CAN BE ASSIGNED TO '5' AS *ptr RETRIEVES THE VALUE OF VARIABLE "value" i.e. 5)
    std::cout << value; // prints 7

    the  3rd line also should not compile as [quiz 2nd question solution]:
    the asterisk here represents a dereference, which is used to get the value that a pointer is pointing to. So this line says, “retrieve the value that ptr is pointing to (an integer here 5), and overwrite it with the   literal  7). That doesn’t make any sense -- you can’t assigna literal to an integer literal!

    • nascardriver

      Hi prince GUPTA!

    • Alex

      Nascardriver's answer is correct. Just to be clear, *ptr is the same as value. We can use value in two contexts: as an l-value (e.g. value = 5) or as an r-value to be evaluated to produce a value (std::cout << value). *ptr works identically. We can use it as an l-value (*ptr = 7) or an r-value (std::cout << *ptr).

  • D.Master

    pointers don't store values [expletive removed]

    • Alex

      From wikipedia (https://en.wikipedia.org/wiki/Pointer_(computer_programming))

      "... a pointer is a programming language object, whose value refers to (or "points to") another value stored elsewhere in the computer memory using its memory address".

      Seems pretty clear that pointers have values, those values are just addresses.

  • hey alex, this question is a bit off topic. I hope you won't mind.
    So, I am having problem with this code

    #include<iostream>
    using namespace std;
    int main()
    {
    char ch = 'a';
    cout<<&ch;
    }

    when I ran it, it gave  "a )"
    what does this mean?"ch" does not take a valid address in memory?

    • Alex

      std::cout treats objects of type char* as C-style strings, and will print characters until it hits a null terminator.

      You've essentially told the compiler to print the character 'a' as a string -- so you get the 'a' as the first character, and then everything else beyond that is junk in memory.

    • D.Master

      This question on address of a char has been a great aid to me in my learning of c++. Std::strings can be iniatialised  with char pointers. Pointers even find use in other situation to mean a set of elements. Eg: sf::Text *text is an array of sf::Text elements. Because pointer is not smart you need to specify a size type for the number of elements. In the case of char pointers, the last element is a NULL value of 0

Leave a Comment

Put all code inside code tags: [code]your code here[/code]