Parish

Parish

Software Engineer. Interested in distributed systems and low level stuff. ( Golang, K8s, *nix Operating Systems )

11 May 2019

Golang Arrays

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 hence you will not often use them in your programs. However this is not to say that they are not 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 (homogenous) hence they require the same amount of memory to store.
  • Elements are stored in contiguous memory locations.

In Golang the size of an array must be established once and for all when storage for the array is allocated and cannot change thereafter.Therefore in go arrays are fixed.i.e an array in go must have its size specified as a constant in its declaration and this size cannot change during execution.

Arrays in Go are just values. 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
    var identifier [size]T //declaration syntax. T  represents type.
    
    var ints [5]int
  • Using short assignment
    identifier := expression //declaration syntax

    ints := [5]int{}

    //or

    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 golang. How about to initialize it? Golang 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.

  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)
      }

  }
$ 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.

References

comments powered by Disqus