Golang Arrays

Evanson Mwangi
5 min readFeb 22, 2021
Maria Letta Free Gophers Pack

We rarely deal directly with arrays in Go: instead, we tend to work with them by using slices.

In Go, an array is a fixed length, ordered collection of values of the same type stored in contiguous memory locations. The number of elements is the array’s length and it is never negative. The length of the array must always evaluate to a non-negative constant that can be represented as a value of the int data type.

In Go unlike in other imperative languages the size of the array is part of its type, which in part limits its expressive power . However they are still important for you to understand them.

var ints [5]int
fmt.Printf("%T\n", ints) //[5]int is the type

The values stored in an array are called elements. These elements are accessed by use of indices (plural for index). An integer value is used to indicate the ordinal value of the element in the array.
For example, if n is an array with 5 elements, then n[5] is the element of n with ordinal value 6. Indexing of arrays in Golang much like in other imperative languages starts at 0, thus n[5] is the 6th value in n.

Arrays are important because they allow many values to be stored in a single data structure while providing for very fast access to each value.This is made possible by the fact that:

  • All values in an array are of the same type (homogeneous) hence they require the same amount of memory to store.
  • Elements are stored in contiguous memory locations. (By using contiguous memory allocation means you can access any element anywhere in the array’s memory location while still in the same memory location)

In Go the size of an array is established during memory allocation for the array and cannot change thereafter. Therefore in Go arrays are fixed hence must have its size specified as a constant in its declaration and this size is static and cannot change at runtime.

Arrays in Go are just values, this is to mean that the concrete value of each of the array’s element types are held in memory. An array variable denotes an entire array hence when you pass an array as an argument to a function, a copy of the original’s contents is passed in. Therefore modifying its contents does not modify the contents of the original. To do that you must pass a pointer to the array variable.

func mod(ints *[5]int){
//do some modification here
}
//declare an zero value array of ints.
var numbers [5]int
//Then we call mod like this
mod(&numbers)

How to declare an array

Go has several ways of declaring arrays. You can use one of the two ways shown below to declare an array in Go.

  • Using var keyword
// declaration syntax. T  represents type.
var identifier [size]T

var ints [5]int
  • Using short assignment
identifier := expression //declaration syntax
ints := [5]int{}
ints := [...]int{} // the compiler will compute the length at runtime.

How to initialize an array

Now you know how to declare an array variable in Go. How about to initialize it? Go is nothing like other programming languages where the default value of uninitialized variables is Null, nil, None or even Undefined. Instead in Go variables are initialized to the zero value of their declared type. That is to mean if your variable holds an int then it will be initialized to 0 and thus ready to use. You wont get some of the issues you get with using uninitialized variables as is in Java and the rest. :-)

To initialize the array to your predefined values you could use one of two ways.

var ints [5]int = [5]int{1,2,3,4,5}

or

ints :=[5]int{1,2,3,4,5}

Accessing elements in Arrays

To access the individual elements in an array Go provides convenient ways by use of the for and for range loops.

For example given the following array.

num := [5]int{1,2,3,4,5}
for i := 0; i < len(num); i++ {
fmt.Printf("%v\n",num[i])
}

or

num := [5]int{1,2,3,4,5}
for _,v := range num{
fmt.Printf("%v\n",v)
}

When an array of a given size, say N and of a given type T, the compiler allocates enough memory to hold all N pieces of data. If M bytes of memory is required for each piece of data of the type T, then a total of N*M bytes of contiguous memory are allocated to that array variable.

The data for the first element is stored in the first M bytes, the data for the second element is stored in the next M bytes and so on and so forth.The Go compiler remembers the address of the first byte of an array only. In fact the address of the first byte is considered the memory address for the entire array. Thus knowing the address of the first byte the compiler can easily find the memory address for any other element of the array using the below formula.

b + ( i * m )

where

b is the base address ( address of the first byte of the array ) , i is the position of the element. i.e the ordinal value , m is the size of an array element of type T

The summation ( i + m ) is called the offset.

For example given an array A, to retrieve the data stored in A[n], the computer goes to the address of the array ( b ) then increments it by the offset (n * M) and retrieves the next M bytes of data.

// array.go
package main

import (
"fmt"
"unsafe"
)

func main() {
ints := [5]int{1, 4, 5, 8, 9}
fmt.Printf("Address of the int variable %p\n", &ints)
fmt.Printf("Index\tValue\tAddress\n")
for i := 0; i < len(ints); i++ {
pint := unsafe.Pointer(&ints[i])
fmt.Printf("%d\t\t\t%v\t\t\t%v\n", i, ints[i], pint)
}
}
// terminal output
$ go run array.go
Address of the ints variable 0xc0000b4060
Index Value Address
0 1 0xc0000b4060
1 4 0xc0000b4068
2 5 0xc0000b4070
3 8 0xc0000b4078
4 9 0xc0000b4080

The above code snippet declares and initializes an array literal using the short hand declaration style and afterwards iterates through each of its elements printing out their addresses. Note that the address of the ints variable is the same as of the first element in the array ( ints[0] ).

Also note that the above elements are aligned using a 8 byte alignment in the contiguous memory location. That means each element in the array occupies 8 bytes which is equivalent to 64 bits, which is the default size of an int in a 64 bit machine.( I am using a 64 bit machine ). Therefore if we were to find out the location of the 5th element (index 4 ) in the array, we would only need to know the base address of the array which is 0xc0000b4060. Then we would use the above formula to compute the address.

formulae: base_address + offset
base_address = 0xc0000b4060
offset = (i * m) = 4 * 8 = 32 (decimal)
convert it to hexadecimal 32 = 0x20
0xc0000b4060 + 0x20 = 0xc0000b4080

Note

Details about how elements are stored in contiguous locations varies somewhat among languages. Do not generalize the information above.

This blog post was originally posted here basebandit. If you liked the post or have any comments, feel free to reach out to me.

Great thanks to John Arundel for the great feedback.

References

--

--