3

I am trying to understand how pointers work in Go. On a general level, I have little experience with pointers as I mostly use javascript.

I wrote this dummy program:

func swap(a, b *int) {
    fmt.Println("3", &a, &b)
    *a, *b = *b, *a

    fmt.Println("4", a, b)
}
func main() {
    x := 15
    y := 2

    fmt.Println("1", x, y)
    fmt.Println("2", &x, &y)
    swap(&x, &y)
    fmt.Println("5", x, y)
}

Which prints the following results:

$ go run test.go
1 15 2
2 0x208178170 0x208178178
3 0x2081ac020 0x2081ac028
4 0x208178178 0x208178170
5 2 15

I have several questions:

  1. From what I understand &x gives the address at which x is stored. To get the actual value of x, I need to use *x. Then, I don't understand why &x is of type *int. As *x and &x are both of type *int, their differences are not clear to me at all.

  2. In my swap function, what is the difference between using *a, *b = *b, *a and using a, b = b, a? Both work but I can't explain why...

  3. Why are the addresses different when printed between step 2 and 3?

  4. Why can't I just modify the address directly assigning &b to &a for example?

Many thanks for your help

Spearfisher
  • 8,445
  • 19
  • 70
  • 124
  • 1
    The actual value of `x` is `x`. If the parameter `a` stores the address of `x`, then `*a` gives the value of `x`. – Kerrek SB Jan 07 '15 at 23:42
  • 2
    I think it's the dual usage of * to signify two different things that's confusing. I remember I was a bit baffled by it when I first learned C years ago. – Not_a_Golfer Jan 07 '15 at 23:51
  • 2
    3: you're printing the address of the pointer variables, not of the underlying integers. You'd print `a` and `b` to see those. – twotwotwo Jan 08 '15 at 00:52
  • 4: it's just how they chose to define the language: they decided variables would behave as if they always live at the same address. An advantage of that is that you don't have to worry about the many pitfalls you can have with pointers (like having your variables unexpectedly changed by other code) except when you actually elect to use pointers (or the Go types implemented using pointers, like slices and maps). – twotwotwo Jan 08 '15 at 01:39
  • 3
    2: no, `a, b = b, a` swaps the pointers accessible within your `swap` function, but it doesn't swap the values from `main`'s perspective: the last line of output becomes `5 15 2`: http://play.golang.org/p/rCXDgkZ9kG – twotwotwo Jan 08 '15 at 01:40
  • 1: covering the same territory as the first two comments: `*x` in your `main()` is invalid syntax (try it), because `x` is not of a pointer type. It just happens the character in the name of the type (`*int`) is taken from the operator you use to access a value through a pointer (`*`, also called the *dereference* operator), instead of the one you use to get a pointer to a variable (`&`). – twotwotwo Jan 08 '15 at 01:44
  • @twotwotwo, why not post your comments as an answer? – Akavall Jan 08 '15 at 01:46

2 Answers2

4

1) It's a confusion about the language that leads you to think *x and &x are the same type. As Not_a_Golfer pointed out, the uses of * in expressions and type names are different. *x in your main() is invalid syntax, because * in an expression tries to get the value pointed to by the pointer that follows, but x is not a pointer (it's an int).

I think you were thinking of the fact that when you take a pointer to x using &x, the character you add to the type name is * to form *int. I can see how it's confusing that &var gets you *typ rather than &typ. On the other hand, if they'd put & on the type name instead, that would be confusing in other situations. Some trickiness is inevitable, and like human languages, it may be easier to learn by using than discussion alone.

2) Again, turns out the assumption is inaccurate: a, b = b, a swaps the pointers that the swap function looks at, but doesn't swap the values from main's perspective and the last line of output changes to 5 15 2: http://play.golang.org/p/rCXDgkZ9kG

3) swap is printing the address of the pointer variables, not of the underlying integers. You'd print a and b to see the addresses of the integers.

4) I'm going to assume, maybe wrongly, that you were hoping you could swap the locations that arbitrary variables point to with & syntax, as in &x, &y = &y, &x, without ever declaring pointer variables. There's some ambiguity, and if that's not what you were going for, I'm not sure if this part of the answer will help you.

As with many "why can't I..." questions the easy out is "because they defined the language that way". But going to why it's that way a little bit, I think you're required to declare a variable as a pointer at some point (or another type implemented using pointers, like maps or slices) because pointers come with booby traps: you can do something over in one piece of code that changes other code's local variables in unexpected ways, for example. So wherever you see *int appear, it's telling you you might have to worry about (or, that you're able to use) things like nil pointers, concurrent access from multiple pieces of code, etc.

Go is a bit more conservative about making pointer-hood explicit than other languages are: C++, for example, has the concept of "reference parameters" (int& i) where you could do your swap(x,y) with no & or pointers appearing in main. In other words, in languages with reference parameters, you might have to look at the declaration of a function to know whether it will change its arguments. That sort of behavior was a little too surprising/implicit/tricky for the Go folks to adopt.


No getting around that all the referencing and dereferencing takes some thinking, and you might just have to work with it a while to get it; hope all this helps, though.

Community
  • 1
  • 1
twotwotwo
  • 28,310
  • 8
  • 69
  • 56
1
  1. From what I understand &x gives the address at which x is stored. To get the actual value of x, I need to use *x. Then, I don't understand why &x is of type *int. As *x and &x are both of type *int, their differences are not clear to me at all.

When *int is in function declaration, it mean pointer of int. but it's in the statement, *x is not type *int. it mean dereference of the pointer. So:

*a, *b = *b, *a

This mean, swapping values by dereference. The fuction arguments is passing by copyed. So if you want to export values to caller, you need to pass a pointer of the variable as argument.

  1. In my swap function, what is the difference between using *a, *b = *b, *a and using a, b = b, a? Both work but I can't explain why...

As I said, a, b = b, a mean swapping pointers not swapping values.

  1. Why are the addresses different when printed between step 2 and 3?

This is not a defined result. Address of variable are disposed by go's runtime.

  1. Why can't I just modify the address directly assigning &b to &a for example?

Not impossible. For example:

package main

import (
    "unsafe"
)

func main() {
    arr := []int{2, 3}

    pi := &arr[0] // take address of first element of arr

    println(*pi) // print 2

    // Add 4bytes to the pointer
    pi = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(pi)) + unsafe.Sizeof(int(0))))

    println(*pi) // print 3
}

Using unsafe package, you can update address values. But it's not endorsed.

thwd
  • 23,956
  • 8
  • 74
  • 108
mattn
  • 7,571
  • 30
  • 54