39

I'm having a little play with google's Go language, and I've run into something which is fairly basic in C but doesn't seem to be covered in the documentation I've seen so far

When I pass a pointer to a slice to a function, I presumed we'd have some way to access it as follows:

func conv(x []int, xlen int, h []int, hlen int, y *[]int)

    for i := 0; i<xlen; i++ {
        for j := 0; j<hlen; j++ {
            *y[i+j] += x[i]*h[j]
        }
    }
 }

But the Go compiler doesn't like this:

sean@spray:~/dev$ 8g broke.go
broke.go:8: invalid operation: y[i + j] (index of type *[]int)

Fair enough - it was just a guess. I have got a fairly straightforward workaround:

func conv(x []int, xlen int, h []int, hlen int, y_ *[]int) {
    y := *y_

    for i := 0; i<xlen; i++ {
        for j := 0; j<hlen; j++ {
            y[i+j] += x[i]*h[j]
        }
    }
}

But surely there's a better way. The annoying thing is that googling for info on Go isn't very useful as all sorts of C/C++/unrelated results appear for most search terms.

Sean
  • 1,698
  • 2
  • 16
  • 22
  • To ask a question about the Go language, tag it with `go`, more people will answer; Vatine did that for you. `golang` is an obsolete tag; as you can see there are 117 questions tagged as `go` and just 3 tagged, in error, as `golang`. – peterSO Mar 15 '10 at 16:56
  • 1
    Note that you don't need xlen and hlen as arguments; you can use len(x) and len(h) instead. – Arkku Mar 31 '10 at 09:42
  • 2
    just put `*y` to `()` => `(*y)[index]` and have fun, see [play ground](http://play.golang.org/p/eTg7wplH5c) – Ivan Black Nov 22 '14 at 02:58

6 Answers6

34

The Google Go docs state the following about passing arrays - they say you usually want to pass a slice (instead of a pointer?):

Updated:

As indicated by @Chickencha's comment, array slices are references which is why they are efficient for passing. Therefore likely you will want to use the slice mechanism instead of "raw" pointers.

From Google Effective Go doc http://golang.org/doc/effective_go.html#slices

Slices are reference types,


Original

It's under the heading

An Interlude about Types

[...snip...] When passing an array to a function, you almost always want to declare the formal parameter to be a slice. When you call the function, take the address of the array and Go will create (efficiently) a slice reference and pass that.

Editor's note: This is no longer the case

Using slices one can write this function (from sum.go):

09    func sum(a []int) int {   // returns an int
10        s := 0
11        for i := 0; i < len(a); i++ {
12            s += a[i]
13        }
14        return s
15    }

and invoke it like this:

19        s := sum(&[3]int{1,2,3})  // a slice of the array is passed to sum    

Maybe pass the whole array as a slice instead. Google indicates Go deals efficiently with slices. This is an alternate answer to the question but maybe it's a better way.

ollien
  • 4,418
  • 9
  • 35
  • 58
John K
  • 28,441
  • 31
  • 139
  • 229
  • I agree that this sounds like the way to go. A slice is basically a pointer to an array already and is the preferred way of passing an array by reference. – Evan Shaw Mar 15 '10 at 14:00
  • 1
    Append doesn't work without asterisk. See [playground](http://play.golang.org/p/qwWwoJqOC0) – Ivan Black Mar 01 '15 at 23:02
  • @John K, even reference works like pointer, you can not mix them up. Your sample code to invoke function "sum" will not be compiled, as you can not pass a pointer to a function which needs a slice (reference). The code to call function "sum" `s := sum(&[3]int{1,2,3}) ` can be changed to ` arr :=[3]int{1,2,3} s := sum(arr[:]) ` Full example can be found on the [playgroud](https://play.golang.org/p/3B7W2BVBfW) – Rader Feb 13 '17 at 07:11
21

Types with empty [], such as []int are actually slices, not arrays. In Go, the size of an array is part of the type, so to actually have an array you would need to have something like [16]int, and the pointer to that would be *[16]int. So, what you are actually doing already is using slices, and the pointer to a slice, *[]int, is unnecessary as slices are already passed by reference.

Also remember that you can easily pass a slice referring to the entire array with &array (as long as the element type of the slice matches that of the array). (Not anymore.)

Example:

package main
import "fmt"

func sumPointerToArray(a *[8]int) (sum int) {
    for _, value := range *a { sum += value }
    return
}
func sumSlice (a []int) (sum int) {
    for _, value := range a { sum += value }
    return
}
func main() {
    array := [...]int{ 1, 2, 3, 4, 5, 6, 7, 8 }
    slice := []int{ 1, 2, 3, 4 }
    fmt.Printf("sum arrray via pointer: %d\n", sumPointerToArray(&array))
    fmt.Printf("sum slice: %d\n", sumSlice(slice))
    slice = array[0:]
    fmt.Printf("sum array as slice: %d\n", sumSlice(slice))
}

Edit: Updated to reflect changes in Go since this was first posted.

Arkku
  • 41,011
  • 10
  • 62
  • 84
  • Oh, as an additional note, the [...] notation for the length of an array can only be applied to array literals to make the compiler count the elements for you. One cannot, for example, have a *[...]int as the formal argument of a function. – Arkku Mar 31 '10 at 09:04
  • O_o this doesn't work: cannot use &array (type *[8]int) as type []int in function argument – Doug Feb 11 '13 at 04:20
  • @Doug Interesting, it did compile when I posted it and old documentation also uses the `&array` syntax to pass an array as a slice. However, it doesn't work anymore, it seems. – Arkku Feb 11 '13 at 04:39
  • @Doug Updated to compile with the current version of Go. – Arkku Feb 11 '13 at 04:48
  • After messing around, turns out a []int still works, but you must manually convert the array into a slice when you pass the args (sumPointerToArray(array[:]) instead of just passing &array. – Doug Feb 11 '13 at 04:58
5

The semicolon and the asterisk are added and removed.

*y[i+j] += x[i]*h[j]

Is interpreted as

(*y)[i+j] += x[i] * h[j];

EDIT: Please read the comments. The answer is probably no longer valid. and I haven't touched up on go for quite some time and can't even read this anymore.

Behrooz
  • 1,696
  • 2
  • 32
  • 54
  • In Go you can skip the semicolon, and the asterisk was how I thought a pointer to an array should be used. – Sean Mar 14 '10 at 00:01
  • Drop the semicolon. From Release 2009-12-22: Since the last release there has been one large syntactic change to the language, already discussed extensively on this list: semicolons are now implied between statement-ending tokens and newline characters. See http://groups.google.com/group/golang-nuts/t/5ee32b588d10f2e9 for details. – peterSO Mar 15 '10 at 16:49
  • please please please fix the MD :) - looks like some alien glyph now haha (i wouldve changed it myself, but the edit queue is full for some reason) – AthulMuralidhar Jan 26 '22 at 09:31
2

The length is part of the array's type, you can get length of an array by the len() built-in function. So you needn't pass the xlen, hlen arguments.

In Go, you can almost always use slice when passing array to a function. In this case, you don't need pointers. Actually, you need not pass the y argument. It's the C's way to output array.

In Go style:

func conv(x, h []int) []int {
    y := make([]int, len(x)+len(h))
    for i, v := range x { 
        for j, u := range h { 
            y[i+j] = v * u 
        }   
    }   
    return y
}

Call the function:

conv(x[0:], h[0:])
Stephen Hsu
  • 5,127
  • 7
  • 31
  • 39
  • The value of i is 0 to (len(x)-1) and the value of j is 0 to (len(h)-1). Therefore, the value of (i+j) is 0 to ((len(x)-1)+(len(h)-1)). Since array indices start at zero, don't we need ((len(x)-1)+(len(h)-1)+1) array elements for y? – peterSO Mar 15 '10 at 16:38
  • For `var s []int`, the references `s`, `s[0:len(s)]`, and `s[0:]` are all the same thing, the slice `s`. So, more concisely, write `conv(x, h)`, instead of `conv(x[0:], h[0:])`. – peterSO Mar 16 '10 at 00:56
2

Here's a working Go program.

package main

import "fmt"

func conv(x, h []int) []int {
    y := make([]int, len(x)+len(h)-1)
    for i := 0; i < len(x); i++ {
        for j := 0; j < len(h); j++ {
            y[i+j] += x[i] * h[j]
        }
    }
    return y
}

func main() {
    x := []int{1, 2}
    h := []int{7, 8, 9}
    y := conv(x, h)
    fmt.Println(len(y), y)
}

To avoid wrong guesses, read the Go documentation: The Go Programming Language.

peterSO
  • 158,998
  • 31
  • 281
  • 276
0

Almost all the other answers to this question talk about using slices instead of array pointers but none answers how to solve the error, so I thought I would write this answer. The error gives us a hint that it cannot access the index of y because it is an invalid operation.

Your first approach is wrong as the Go compiler shouts at you. The problem in the first approach is that *y[i+j] is wrong syntax. This is because technically you are doing this *(y[i+j]) and you can't just do that because y in your case is a pointer to an int array. If you print y it would print the memory address of the array.

You are trying to get the i+jth index of y which does not simply exist because y is not an array. You can fix your code by adding parentheses to the statement which would indicate that you are trying to get the i+jth index of the array that y is pointing to. Use (*y)[i+j] instead of *y[i+j]. The function would look like this after making the changes:

func conv(x []int, xlen int, h []int, hlen int, y *[]int) {
    for i := 0; i<xlen; i++ {
        for j := 0; j<hlen; j++ {
            (*y)[i+j] += x[i]*h[j]
        }
    }
}
  • 1
    your code uses a pointer to a slice and not an array like your explanation implies – Pizza lord - on strike Dec 08 '21 at 10:34
  • @Pizzalord oh yeah, sorry I by mistake used the word `array` instead of `slice` because the question had by mistake used the word `array`. I'll replace the word `array` with `slice` in my answer but the code would work for both slices and arrays. – Hargunbeer Singh Dec 08 '21 at 11:28