1 Introduzione ai tipi di dati del linguaggio Go

Nel linguaggio Go, i tipi di dati sono la base della programmazione e determinano la forma dei dati che le variabili possono memorizzare. I tipi di dati di base forniti dal linguaggio Go sono principalmente divisi nelle seguenti categorie:

Tipo di Dato Descrizione Utilizzo di memoria
bool Tipo booleano, utilizzato per memorizzare vero o falso 1 byte
int, uint Intero con segno e senza segno, la dimensione predefinita dipende dalla piattaforma del sistema 4 o 8 byte
int8, uint8 Interi con segno e senza segno a 8 bit 1 byte
int16, uint16 Interi con segno e senza segno a 16 bit 2 byte
int32, uint32 Interi con segno e senza segno a 32 bit 4 byte
int64, uint64 Interi con segno e senza segno a 64 bit 8 byte
float32 Numero in virgola mobile a 32 bit 4 byte
float64 Numero in virgola mobile a 64 bit 8 byte
complex64 Numero complesso con parti reali e immaginarie a 32 bit 8 byte
complex128 Numero complesso con parti reali e immaginarie a 64 bit 16 byte
byte Simile a uint8 1 byte
rune Simile a int32, rappresenta un punto codice Unicode 4 byte
string Tipo stringa Dipende dalla lunghezza della stringa
error Interfaccia di errore, utilizzata per restituire informazioni sull'errore Nessuna dimensione fissa

Questi tipi possono essere scelti in base alle diverse esigenze, come calcoli numerici, elaborazione del testo o controllo logico.

2 Tipi di Dati Integer

2.1 Panoramica dei Tipi Integer

Il linguaggio Go ha diversi tipi di interi incorporati, classificati come segue:

  • Interi con segno: int8, int16, int32 (o rune), int64 e int
  • Interi senza segno: uint8 (o byte), uint16, uint32, uint64 e uint

Le dimensioni di int e uint sono di 4 byte su sistemi a 32 bit e di 8 byte su sistemi a 64 bit. Gli intervalli di valori dei tipi di dati integer sono mostrati nella seguente tabella:

Tipo Intervallo di Valori
int8 -128 a 127
uint8 0 a 255
int16 -32768 a 32767
uint16 0 a 65535
int32 -2147483648 a 2147483647
uint32 0 a 4294967295
int64 -9223372036854775808 a 9223372036854775807
uint64 0 a 18446744073709551615

2.2 Uso delle Variabili Integer

La sintassi di base per dichiarare una variabile intera è la seguente:

var nome_variabile tipo_dati = valore_iniziale

Esempio di codice:

package main
import "fmt"

func main() {
    var a int = 10 // Variabile intera con segno
    var b uint = 20 // Variabile intera senza segno
    var c int8 = -128 // Valore int8 più piccolo
    fmt.Println(a, b, c)
}

2.3 Operazioni Integer

Il linguaggio Go supporta operatori aritmetici comuni, come l'addizione (+), la sottrazione (-), la moltiplicazione (*), la divisione (/) e il modulo (%), nonché operatori bitwise come AND bitwise (&), OR (|), XOR (^), spostamento a sinistra (<<) e spostamento a destra (>>).

package main
import "fmt"

func main() {
    x := 10
    y := 3

    // Operazioni aritmetiche
    fmt.Println(x + y) // Addizione
    fmt.Println(x - y) // Sottrazione
    fmt.Println(x * y) // Moltiplicazione
    fmt.Println(x / y) // Divisione
    fmt.Println(x % y) // Modulo

    // Operazioni bitwise
    fmt.Println(x & y)  // AND bitwise
    fmt.Println(x | y)  // OR bitwise
    fmt.Println(x ^ y)  // XOR bitwise
    fmt.Println(x << 1) // Spostamento a sinistra di 1
    fmt.Println(x >> 1) // Spostamento a destra di 1
}

3 Tipi di Dati Floating Point

3.1 Panoramica dei Tipi di Dati Floating Point

Nel linguaggio Go, i tipi di dati floating point includono float32 e float64, corrispondenti a dati floating point a 32 bit e 64 bit. In generale, si consiglia di utilizzare float64 poiché fornisce un intervallo maggiore e una precisione più accurata.

  • float32 ha approssimativamente 23 bit significativi, fornendo circa 7 cifre decimali di precisione.
  • float64 ha approssimativamente 52 bit significativi, fornendo circa 16 cifre decimali di precisione.

3.2 Utilizzo delle Variabili Floating Point

Le variabili floating point possono essere dichiarate fornendo letterali diretti o utilizzando la parola chiave var:

package main
import "fmt"

func main() {
    var f1 float32 = 3.14 // Specificare esplicitamente il tipo float32
    f2 := 3.14           // Inference automatica come tipo float64
    fmt.Println(f1, f2)
}

3.3 Aritmetica Floating Point e Problemi

L'aritmetica floating point può portare a perdita di precisione. Eseguire operazioni con precisione molto alta è un problema comune, specialmente quando si sottraggono due numeri molto vicini.

package main
import "fmt"

func main() {
    f1 := .1
    f2 := .2
    f3 := f1 + f2
    fmt.Println(f3) // L'output potrebbe non essere il .3 atteso a causa di problemi di precisione

    // Risoluzione dei problemi di precisione utilizzando l'output formattato
    fmt.Printf("%.1f\n", f3) // Output corretto a .3
}

4 Tipo di Dati Booleano

4.1 Panoramica del Tipo di Dati Booleano

Il booleano è il tipo di dato più semplice e può avere solo due valori: true (vero) e false (falso). Ricopre una posizione molto importante nelle istruzioni condizionali e nelle strutture di controllo del loop.

4.2 Utilizzo delle Variabili Booleane

Dichiarazione e utilizzo delle variabili booleane:

package main
import "fmt"

func main() {
    var success bool = true
    var fail bool = false
    fmt.Println("Operazione riuscita:", success)
    fmt.Println("Operazione fallita:", fail)
}

I valori booleani sono spesso utilizzati nelle istruzioni condizionali:

package main
import "fmt"

func main() {
    a := 10
    b := 20
    fmt.Println("a == b:", a == b) // false
    fmt.Println("a < b:", a < b)   // true
}

5 Tipo di Dati Stringa

5.1 Panoramica della Stringa

Una stringa è una collezione di caratteri. Nel linguaggio Go, le stringhe sono immutabili. Ogni stringa è composta da due parti: un puntatore all'array di byte sottostante e una lunghezza. Le stringhe possono contenere qualsiasi dato, inclusi byte.

5.2 Utilizzo delle Variabili Stringa

Le variabili stringa sono generalmente dichiarate usando doppi apici " per crearle, ma è anche possibile utilizzare il backtick ` per creare stringhe multilinea:

package main
import "fmt"

func main() {
    var s1 string = "ciao"
    s2 := "mondo"
    s3 := `Questa è una
    stringa su
    più linee`
    fmt.Println(s1)
    fmt.Println(s2)
    fmt.Println(s3)
}

Una volta creata una stringa, il suo contenuto non può essere modificato. L'operazione seguente è illegale e provocherà un errore di compilazione:

s := "ciao"
s[] = 'C` // Errore di compilazione: il contenuto delle stringhe è immutabile

5.3 Operazioni sulle Stringhe

Le stringhe sono molto comuni e importanti nella programmazione. Il linguaggio Go fornisce un ricco insieme di funzioni incorporate per la manipolazione delle stringhe. Ecco alcune operazioni comunemente utilizzate.

5.3.1 Concatenazione delle Stringhe

Nel linguaggio Go, è possibile utilizzare l'operatore più (+) per concatenare le stringhe, che è il metodo più diretto. Inoltre, quando si tratta di frequenti concatenazioni di stringhe multiple, è consigliabile utilizzare strings.Builder per prestazioni migliori.

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Concatenazione delle stringhe usando +
    ciao := "Ciao, "
    mondo := "Mondo!"
    risultato := ciao + mondo
    fmt.Println(risultato) // Output: Ciao, Mondo!

    // Concatenazione delle stringhe usando strings.Builder
    var sb strings.Builder
    sb.WriteString("Ciao, ")
    sb.WriteString("Mondo!")
    fmt.Println(sb.String()) // Output: Ciao, Mondo!
}

5.3.2 Divisione delle stringhe

La divisione delle stringhe può essere fatta utilizzando la funzione strings.Split, che suddivide la stringa in una slice in base al delimitatore specificato.

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Definire una stringa
    frase := "Go è un linguaggio di programmazione open source"

    // Suddividi la stringa per spazio
    parole := strings.Split(frase, " ")
    for _, parola := range parole {
        fmt.Printf("%s\n", parola)
    }
    // Output:
    // Go
    // è
    // un
    // linguaggio
    // di
    // programmazione
    // open
    // source
}

5.3.3 Accesso all'indice

In Go, una stringa è una sequenza immutabile di byte. È possibile utilizzare l'indicizzazione per accedere a byte specifici all'interno di una stringa. Tuttavia, è importante notare che poiché una stringa può contenere caratteri multibyte (come caratteri codificati in UTF-8), l'indicizzazione diretta potrebbe non restituire il singolo carattere atteso.

package main

import "fmt"

func main() {
    s := "Ciao, mondo"
    for i := 0; i < len(s); i++ {
        fmt.Printf("%d: %x\n", i, s[i])
    }
    // Nota: ciò restituirà la rappresentazione esadecimale dei byte, non dei caratteri
}

Per iterare attraverso la stringa per carattere, è possibile utilizzare il ciclo range.

package main

import "fmt"

func main() {
    s := "Ciao, mondo"
    for indice, valoreRune := range s {
        fmt.Printf("%d: %U '%c'\n", indice, valoreRune, valoreRune)
    }
    // Output: indice, codifica Unicode e il carattere stesso per ciascun carattere
}

5.3.4 Ottenere la lunghezza

La funzione len può ottenere la lunghezza di una stringa, ovvero la lunghezza della sequenza di byte sottostante. Per le stringhe UTF-8, se è necessario ottenere il numero di caratteri (rune), è necessario utilizzare la funzione utf8.RuneCountInString. Questo gestisce correttamente i caratteri multibyte.

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "Ciao, mondo"
    fmt.Println("Lunghezza dei byte:", len(s)) // Output della lunghezza dei byte
    fmt.Println("Lunghezza dei rune:", utf8.RuneCountInString(s)) // Output della lunghezza dei caratteri
}

Dall'esempio precedente, possiamo vedere che il linguaggio Go fornisce ricche funzioni di libreria per le operazioni sulle stringhe, rendendo facile completare varie attività di elaborazione delle stringhe. Durante la scrittura del codice, è importante prestare attenzione alla distinzione tra byte e caratteri, specialmente quando si lavora con set di caratteri non ASCII.