Skip to content
Go back

Go Data Types and Memory Sizes

Go has a rich set of built-in data types that are carefully designed for performance and memory efficiency. Understanding these types and their memory footprints is crucial for writing efficient Go programs. This comprehensive guide covers all Go data types with their exact memory sizes and practical examples.

Table of contents

Open Table of contents

Data Types Overview

Go’s data types are organized into several categories, each with specific characteristics and memory requirements.

Integer Types

Data TypeSize (32-bit)Size (64-bit)Range/Description
int81 byte1 byte-128 to 127
uint81 byte1 byte0 to 255
byte1 byte1 byteAlias for uint8
int162 bytes2 bytes-32,768 to 32,767
uint162 bytes2 bytes0 to 65,535
int324 bytes4 bytes-2,147,483,648 to 2,147,483,647
rune4 bytes4 bytesAlias for int32, Unicode code point
uint324 bytes4 bytes0 to 4,294,967,295
int648 bytes8 bytes-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
uint648 bytes8 bytes0 to 18,446,744,073,709,551,615
int4 bytes8 bytesPlatform-dependent signed integer
uint4 bytes8 bytesPlatform-dependent unsigned integer
uintptr4 bytes8 bytesInteger large enough to store pointer bits

Floating Point Types

Data TypeSize (32-bit)Size (64-bit)Range/Description
float324 bytes4 bytes32-bit IEEE 754 floating point
float648 bytes8 bytes64-bit IEEE 754 floating point

Complex Types

Data TypeSize (32-bit)Size (64-bit)Range/Description
complex648 bytes8 bytesComplex number with float32 real and imaginary parts
complex12816 bytes16 bytesComplex number with float64 real and imaginary parts

Boolean Type

Data TypeSize (32-bit)Size (64-bit)Range/Description
bool1 byte1 bytetrue or false

String and Composite Types

Data TypeSize (32-bit)Size (64-bit)Range/Description
string8 bytes16 bytesHeader: pointer + length (actual string data stored separately)
[]T (slice)12 bytes24 bytesHeader: pointer + length + capacity
[N]T (array)N × sizeof(T)N × sizeof(T)Contiguous memory for N elements
map[K]V4 bytes8 bytesPointer to hash table structure
chan T4 bytes8 bytesPointer to channel structure
interface{}8 bytes16 bytesHeader: type info + value pointer
*T (pointer)4 bytes8 bytesMemory address
func(...) ...4 bytes8 bytesFunction pointer

Notes:

Practical Examples

Let’s explore each data type category with practical examples and use cases.

Integer Types

package main

import (
    "fmt"
    "math"
    "unsafe"
)

func integerExamples() {
    // Fixed-size integers
    var age int8 = 25          // Perfect for small values like age
    var temperature int16 = -40 // Temperature in Celsius
    var population int32 = 1_000_000 // City population
    var bigNumber int64 = 9_223_372_036_854_775_807 // Maximum int64

    // Unsigned integers
    var pixel byte = 255       // RGB color value (0-255)
    var port uint16 = 8080     // Network port number
    var fileSize uint32 = 2_000_000_000 // File size in bytes
    var memoryAddress uint64 = 0xFFFFFFFFFFFFFFFF

    // Platform-dependent integers
    var count int = 1000       // Most common integer type
    var arrayIndex uint = 42   // Array/slice indexing

    // Unicode rune (int32 alias)
    var emoji rune = '🚀'      // Unicode code point

    fmt.Printf("int8 size: %d bytes, value: %d\n", unsafe.Sizeof(age), age)
    fmt.Printf("int16 size: %d bytes, value: %d\n", unsafe.Sizeof(temperature), temperature)
    fmt.Printf("int32 size: %d bytes, value: %d\n", unsafe.Sizeof(population), population)
    fmt.Printf("int64 size: %d bytes, value: %d\n", unsafe.Sizeof(bigNumber), bigNumber)
    fmt.Printf("byte size: %d bytes, value: %d\n", unsafe.Sizeof(pixel), pixel)
    fmt.Printf("rune size: %d bytes, value: %c (%d)\n", unsafe.Sizeof(emoji), emoji, emoji)
}

Floating Point and Complex Types

func floatingPointExamples() {
    // Single precision (32-bit)
    var price float32 = 19.99
    var ratio float32 = 0.618  // Golden ratio approximation

    // Double precision (64-bit) - default for floating point literals
    var pi float64 = 3.141592653589793
    var scientificNotation float64 = 1.23e10 // 12,300,000,000

    // Complex numbers
    var signal complex64 = 3 + 4i    // 32-bit real and imaginary parts
    var wave complex128 = 1 + 2i     // 64-bit real and imaginary parts

    // Complex number operations
    magnitude := real(wave)*real(wave) + imag(wave)*imag(wave)

    fmt.Printf("float32 size: %d bytes, precision: ~7 digits\n", unsafe.Sizeof(price))
    fmt.Printf("float64 size: %d bytes, precision: ~15 digits\n", unsafe.Sizeof(pi))
    fmt.Printf("complex64 size: %d bytes\n", unsafe.Sizeof(signal))
    fmt.Printf("complex128 size: %d bytes\n", unsafe.Sizeof(wave))
    fmt.Printf("Complex wave: %v, magnitude²: %f\n", wave, magnitude)
}

Boolean and String Types

func booleanStringExamples() {
    // Boolean type
    var isActive bool = true
    var isComplete bool // Zero value is false
    var hasPermission = false // Type inferred

    // String type
    var name string = "Go Programming"
    var empty string // Zero value is ""
    var multiline = `This is a
    multi-line
    raw string literal`

    // String operations
    greeting := "Hello, " + name + "!"
    length := len(name)
    firstChar := name[0] // byte value

    // String iteration
    fmt.Println("Character by character:")
    for i, char := range name {
        fmt.Printf("Index %d: %c (Unicode: %d)\n", i, char, char)
    }

    fmt.Printf("bool size: %d byte\n", unsafe.Sizeof(isActive))
    fmt.Printf("string header size: %d bytes\n", unsafe.Sizeof(name))
    fmt.Printf("String length: %d, content: %q\n", length, greeting)
}

Composite Types: Arrays and Slices

func arraySliceExamples() {
    // Fixed-size arrays
    var scores [5]int = [5]int{95, 87, 92, 78, 85}
    var matrix [3][3]int // 2D array, zero-initialized
    autoSized := [...]string{"red", "green", "blue"} // Compiler counts elements

    // Dynamic slices (more common)
    var numbers []int // nil slice
    numbers = append(numbers, 1, 2, 3, 4, 5)

    // Slice with make
    buffer := make([]byte, 1024)      // Length 1024, capacity 1024
    dynamic := make([]int, 0, 10)     // Length 0, capacity 10

    // Slicing operations
    subset := numbers[1:4]            // [2, 3, 4]
    prefix := numbers[:3]             // [1, 2, 3]
    suffix := numbers[2:]             // [3, 4, 5]

    // Memory analysis
    arraySize := unsafe.Sizeof(scores)
    sliceHeaderSize := unsafe.Sizeof(numbers)

    fmt.Printf("Array [5]int size: %d bytes\n", arraySize)
    fmt.Printf("Slice header size: %d bytes\n", sliceHeaderSize)
    fmt.Printf("Slice length: %d, capacity: %d\n", len(numbers), cap(numbers))
    fmt.Printf("Array contents: %v\n", scores)
    fmt.Printf("Slice contents: %v\n", numbers)
}

Maps and Channels

func mapChannelExamples() {
    // Maps (hash tables)
    var ages map[string]int // nil map
    ages = make(map[string]int)
    ages["Alice"] = 30
    ages["Bob"] = 25

    // Map literal
    colors := map[string]string{
        "red":   "#FF0000",
        "green": "#00FF00",
        "blue":  "#0000FF",
    }

    // Channels for communication
    var messages chan string // nil channel
    messages = make(chan string, 3) // Buffered channel

    unbuffered := make(chan int)    // Unbuffered channel

    // Channel operations (in goroutines)
    go func() {
        messages <- "Hello"
        messages <- "World"
        close(messages)
    }()

    // Receive from channel
    for msg := range messages {
        fmt.Println("Received:", msg)
    }

    fmt.Printf("Map header size: %d bytes\n", unsafe.Sizeof(ages))
    fmt.Printf("Channel header size: %d bytes\n", unsafe.Sizeof(messages))
    fmt.Printf("Map length: %d\n", len(ages))
}

Pointers and Functions

func pointerFunctionExamples() {
    // Pointers
    var x int = 42
    var ptr *int = &x        // Pointer to int
    var nilPtr *int          // nil pointer

    // Dereferencing
    value := *ptr            // Get value at address
    *ptr = 100              // Modify value through pointer

    // Function types
    var operation func(int, int) int
    operation = func(a, b int) int {
        return a + b
    }

    // Function as first-class value
    calculator := map[string]func(int, int) int{
        "add":      func(a, b int) int { return a + b },
        "multiply": func(a, b int) int { return a * b },
    }

    result := calculator["add"](5, 3)

    fmt.Printf("Pointer size: %d bytes\n", unsafe.Sizeof(ptr))
    fmt.Printf("Function size: %d bytes\n", unsafe.Sizeof(operation))
    fmt.Printf("Original x: %d, Modified through pointer: %d\n", 42, x)
    fmt.Printf("Calculator result: %d\n", result)
}

Interfaces

// Define interfaces
type Shape interface {
    Area() float64
    Perimeter() float64
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

func interfaceExamples() {
    var shape Shape = Rectangle{Width: 5, Height: 3}
    var empty interface{} // Can hold any value

    // Interface can hold any type that implements its methods
    empty = 42
    empty = "hello"
    empty = []int{1, 2, 3}
    empty = shape

    // Type assertion
    if rect, ok := shape.(Rectangle); ok {
        fmt.Printf("Rectangle: %+v\n", rect)
    }

    // Type switch
    switch v := empty.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case Rectangle:
        fmt.Printf("Rectangle area: %.2f\n", v.Area())
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }

    fmt.Printf("Interface size: %d bytes\n", unsafe.Sizeof(shape))
    fmt.Printf("Empty interface size: %d bytes\n", unsafe.Sizeof(empty))
}

Memory Efficiency Tips

Choosing the Right Integer Type

// Good: Use appropriate size for your data
func goodIntegerUsage() {
    var age int8 = 25           // Age rarely exceeds 127
    var port uint16 = 8080      // Port numbers fit in 16 bits
    var fileSize int64 = 1_000_000_000 // Large files need 64 bits
}

// Less efficient: Using oversized types
func inefficientIntegerUsage() {
    var age int64 = 25          // Wastes 7 bytes per age value
    var port int64 = 8080       // Wastes 6 bytes per port
}

String vs Byte Slice Performance

func stringVsByteSlice() {
    text := "Hello, World!"

    // Strings are immutable - copying required for modifications
    modified := text + " Go is awesome!"

    // Byte slices are mutable - more efficient for modifications
    data := []byte(text)
    data = append(data, " Go is awesome!"...)
    result := string(data)

    fmt.Printf("Original: %s\n", text)
    fmt.Printf("Modified string: %s\n", modified)
    fmt.Printf("From byte slice: %s\n", result)
}

Struct Field Ordering for Memory Efficiency

// Less efficient: poor field ordering
type BadStruct struct {
    Flag1 bool    // 1 byte + 7 bytes padding
    BigNum int64  // 8 bytes
    Flag2 bool    // 1 byte + 7 bytes padding
    SmallNum int32 // 4 bytes + 4 bytes padding
}

// More efficient: group small fields together
type GoodStruct struct {
    BigNum int64    // 8 bytes
    SmallNum int32  // 4 bytes
    Flag1 bool      // 1 byte
    Flag2 bool      // 1 byte + 2 bytes padding
}

func structAlignment() {
    bad := BadStruct{}
    good := GoodStruct{}

    fmt.Printf("BadStruct size: %d bytes\n", unsafe.Sizeof(bad))   // 32 bytes
    fmt.Printf("GoodStruct size: %d bytes\n", unsafe.Sizeof(good)) // 16 bytes
}

Zero Values and Initialization

Every type in Go has a zero value - the value a variable has when declared but not explicitly initialized:

func zeroValues() {
    var (
        // Numeric types: 0
        intVal    int
        floatVal  float64
        complexVal complex128

        // Boolean: false
        boolVal   bool

        // String: empty string
        stringVal string

        // Pointers, slices, maps, channels, functions: nil
        ptrVal    *int
        sliceVal  []int
        mapVal    map[string]int
        chanVal   chan int
        funcVal   func()

        // Arrays: zero value of element type
        arrayVal  [3]int

        // Structs: zero value of each field
        structVal struct {
            Name string
            Age  int
        }
    )

    fmt.Printf("int zero value: %d\n", intVal)
    fmt.Printf("float64 zero value: %f\n", floatVal)
    fmt.Printf("bool zero value: %t\n", boolVal)
    fmt.Printf("string zero value: %q\n", stringVal)
    fmt.Printf("slice zero value (nil): %v\n", sliceVal == nil)
    fmt.Printf("array zero value: %v\n", arrayVal)
    fmt.Printf("struct zero value: %+v\n", structVal)
}

For more Go programming fundamentals, check out Learn Go in 60 Seconds.


Share this post on:

Previous Post
Learn Go in 60 Seconds