Thursday, June 2, 2011

Memory, variables in memory, and pointers


Programming involves machine instructions working on data that is available in memory. So when you want to, say, add two numbers, then both those numbers have to be available in memory. And for that to happen, you should allocate some memory space for that. In Go, you can do that easily for most types with the initialization operator.

Full code
package main

import "fmt"

func main() {
    i := 5
    var j int
    fmt.Println("i is: ", i)
    fmt.Println("j is: ", j)
}

i is: 5
j is: 0

Here Go automatically allocates some memory for the variable i - the size of the memory allocated here is that required by a single integer. Since we have also said i := 5, the integer value 5 is assigned to that memory space after the space is allocated. For variable j, no assignment has been stated. However, Go assigns a "zero-value" by default to most data types. For numeric fields, this is the value 0.

Let’s represent it this way:



So i now holds the value 5. And j now holds the default value 0.

Default values of primitive types

Let’s do a short example to find the ‘zero-value’ or default value for some other known data types.

Full code
package main

import "fmt"

func main() {
    var i int
    fmt.Println("default int is: ", i)
    var s string
    fmt.Println("default string is: ", s)
    var f float64
    fmt.Println("default float64 is: ", f)
    var arInt [3]int
    fmt.Println("default int array is: ", arInt)
    var c complex64
    fmt.Println("default complex64 is: ", c)
}

default int is: 0
default string is:
default float64 is: 0
default int array is: [0 0 0]
default complex64 is: (0+0i)

Addresses and memory location

When there is a value stored in memory, it is stored in a physical location. This location is called its address. Many programming languages, including Go, allows you to access the data in a location by specifying its location in memory.

Full code
package main

import "fmt"

func main() {
    i := 5
    fmt.Println("i is: ", i)
    fmt.Println("address of i is: ", &i)
}

i is: 5
address of i is: 0xf840000040

Note that you obtain the address of a variable by using the & symbol before the variable name. Let’s see some more examples.

package main

import "fmt"

func main() {
    var i int
    fmt.Println("address of i is: ", &i)
    var s string
    fmt.Println("address of s is: ", &s)
    var f float64
    fmt.Println("address of f is: ", &f)
    var c complex64
    fmt.Println("address of c is: ", &c)
}

address of i is: 0xf840000040
address of s is: 0xf8400013e0
address of f is: 0xf8400000f8
address of c is: 0xf8400000f0

The actual value of the address will differ from machine to machine and even on different executions of the same program as each machine could have a different memory layout and and also the location where it is allocated could be different.

You could ask the question, ‘so would my program behave differently on different machines since the addressing changes?’ It is true that the addresses will change, however, normal programs do not use the numeric value of the address for anything. Instead, what they usually use is the value referenced by the address. You can get the value at an address by using the * symbol before the address. Let’s see some examples where we dereference and address to get its value.

Full code
package main

import "fmt"

func main() {
    var i int
    fmt.Println("value of i is: ", i)
    fmt.Println("address of i is: ", &i)
    fmt.Println("value at address ", &i, " is: ", *(&i)) //value at (address of i)
    fmt.Println()
    var s string
    fmt.Println("value of s is: ", s)
    fmt.Println("address of s is: ", &s)
    fmt.Println("value at address ", &s, " is: ", *&s) ////value at address of i
    fmt.Println()
    var f float64
    fmt.Println("value of f is: ", f)
    fmt.Println("address of f is: ", &f)
    fmt.Println("value at address ", &f, " is: ", *&f)
    fmt.Println()
    var c complex64
    fmt.Println("value of c is: ", c)
    ptr := &c //address of c.  
    fmt.Println("address of c is: ", ptr)
    fmt.Println("value at address ", ptr, " is: ", *ptr) //value at the address
}

value of i is: 0
address of i is: 0xf840000040
value at address 0xf840000040 is: 0

value of s is:
address of s is: 0xf8400013b0
value at address 0xf8400013b0 is:

value of f is: 0
address of f is: 0xf8400000e8
value at address 0xf8400000e8 is: 0

value of c is: (0+0i)
address of c is: 0xf8400000b8
value at address 0xf8400000b8 is: (0+0i)

When a variable holds an address, it is called a pointer. So in the example ptr := &c, ptr is a pointer and it holds the address the of c. Putting it differently, ptr is a pointer to the variable c. You could also say that ptr is a reference to the variable c. All of these are valid, but they tend to be used in slightly different contexts.

To illustrate, if we had i := 5; ptr := &i, we could roughly illustrate it as shown below. In using it, both i and *ptr refers to the integer value 5.



You can only take the pointer to a variable and not to a literal value or a constant as the following examples show.

package main

func main() {
    const i = 5
    ptr := &i //error: cannot take the address of i

    ptr2 := &10 //error: cannot take the address of 10
}

The need for addresses/pointers/references

Why do we need the complexity of addresses, pointers and references? Why can we not just use the actual value?

One of the reasons for working with addresses is a matter of efficiency; we shall see more when we discuss pass-by-reference and pass-by-value. As a metaphor, imagine a page on Wikipedia, let’s say one about Paris: http://en.wikipedia.org/wiki/Paris. If you wanted to send that entire information to somebody, one way would be to copy the entire page into a document and send it to him, say via email or as a print out. An easier and much faster alternative would be to just send the link to the page, which is a unique url/reference to it. In this case, there are no redundant copies and both of you can read the latest page about Paris. If you sent the entire page, the former method, then it is similar to ‘pass-by-value’ - the entire value is being passed. If you sent only the link, the latter method, then it is similar to ‘pass-by-reference’ which is passing the address.

Both are useful depending on what is required in a situation. When you pass by reference, there is only a single copy of the target, and therefore any changes made by one person can be seen by all. When you pass by value, there are separate copies and what is changed by one person does not affect the original.

12 comments:

  1. Thank you, that the perfect expression of the subject, as always.

    ReplyDelete
  2. thanks this was really helpful

    ReplyDelete
  3. Excellent refresher on the topic. I'm sure I'll be back in a tired state at some point. :)

    ReplyDelete
  4. Fantastic write up. Some of the best explanations.

    ReplyDelete
  5. This article is simple and very util for understanding GoLang

    ReplyDelete
  6. thanks a lot. great article.

    ReplyDelete
  7. That's the first time that I really understand what is a pointer. THANK YOU!

    ReplyDelete
  8. Thanks for this post! It seems I start understanding what actually is a pointer (at last). But it would be great if someone gave a couple examples on when and how we should use pointers. And one more question: I saw somewhere in the Go code a pointer to a pointer, i.e., **a. Could someone please explain when, how and why should we use these? Thanks a lot!

    ReplyDelete
    Replies
    1. If you were to drop down to C, you would learn that an array is essentially syntactic sugar for a pointer. Since a string with this line of thought is a pointer, if you are to have an arrays of strings you are essentially working with a pointer to a pointer. But since pointer arithmetic is absent from Go, probably for your data structures

      Delete
  9. Hello Author,

    Awesome article... Very helpful for beginner

    ReplyDelete
  10. Perfect! Thanks.

    ReplyDelete
  11. Great write-up. Thanks for sharing!

    ReplyDelete

If you think others also will find these tutorials useful, kindly "+1" it above and mention the link in your own blogs, responses, and entries on the net so that others also may reach here. Thank you.