Числа со знаком
// Диапазон: от -128 до 127.
// В памяти: 1 байт.
type int8 int8
// Диапазон: от -32_768 до 32_767.
// В памяти: 2 байт.
type int16 int16
// Диапазон: от -2_147_483_648 до 2_147_483_648.
// В памяти: 4 байт.
type int32 int32
// Диапазон: от -9_223_372_036_854_775_808 до 9_223_372_036_854_775_807.
// В памяти: 8 байт.
type int64 int649_223_372_036_854_775_808 — это девять квинтиллионов двести двадцать три квадриллиона триста семьдесят два триллиона тридцать шесть миллиардов восемьсот пятьдесят четыре миллиона семьсот семьдесят пять тысяч восемьсот семь.
Числа без знака
// Диапазон: от 0 до 255.
// В памяти: 1 байт.
type uint8 uint8
// Диапазон: от 0 до 65_535.
// В памяти: 2 байт.
type uint16 uint16
// Диапазон: от 0 до 4_294_967_295.
// В памяти: 4 байт.
type uint32 uint32
// Диапазон: от 0 до 18_446_744_073_709_551_615.
// В памяти: 8 байт.
type uint64 uint64Для того, чтобы запомнить, сколько места в памяти занимает тот или иной тип данных, вы можете разделить число после типа на 8.
Например, для типа uint16: 16 / 8 = 2, тип uint16 занимает в памяти 2 байта
Платформозависимые типы данных
// int число со знаком, размер которого зависит от платформы.
// В памяти он занимает минимум 4 байта (32 бита) и не является алиасом для любых других целочисленных типов со знаком.
type int int
// uint имеет аналогичные характеристика как и int, но при этом не имеет знака
type uint uint
Почему int и uint занимают минимум 4 байта (32 бита) в Go?
Давайте представим, что разработчики языка Go сделали поддержку и теперь int на 16-и разрядных машинах будет иметь такой же диапазон как и int16 (от -32 _768 до 32_767), что это будет обозначать на практике?
Представим, что у нас есть следующий код:
var balance int = 40_000Если мы запустим этот код на машине с разрядностью 32 или 64, то он отработает как ожидается.
Однако, при выполнении кода на 16-и битной машине мы получили бы переполнение, что приведет к изменению нашего баланса и мы получим -25536.
Переполнение в Go приводит к обрезке значения и не вызывает никакой паники.
При этом число 2 _147_483 _647 достаточен для решения большей части повседневных задач.
В очень старых версиях Go int был алиасом для типа int32
Интересный факт
https://stackoverflow.com/a/42351584
// uintptr целочисленный тип данных, размер которого достаточен, чтобы вместить любой указатель.
// Размер uintptr совпадает с размером слова процессора (word size).
type uintptr uintptrUintptr является целочисленным типом, который используется для хранения адресов памяти в виде числа.
Обратите внимание, что uintptr не является полноценным указателем, а представляет собой целочисленное значение без семантики указателя, в отличии от *T.
Различия и особенности указателей и uintptr мы рассмотрим в отдельном разделе.
На практике это обозначает, что GC (сборщик мусора) ничего не знает про указатели, которые хранятся в типах uintptr. Более того, uintptr не предотвратит утилизацию объекта и GC освободит объект на который указывал этот адрес. (1, 2).
Стоит отметить, что uintptr часто используется в низкоуровневом программировании, позволяя работать с: адресной арифметикой, системными вызовами.
Специальные типы данных
byte
// byte является алиасом для типа uint8, который ввели для того, чтобы было проще различать байтовые значения от 8-и битовых беззнаковых чисел
type byte = uint8
Так как тип byte не является самостоятельным типом, то все фукнции, которые работают с uint8 смогут работать и с byte значениями и наоборот.
Сам же тип byte был введен для того, чтобы различать семантику использования переменных, например:
// тип указывает, что элементы представляют низкоуровневые данные или ASCII/UTF-8 символы, а не маленькие числа
var byteData []byte
// тут наоборот, тип указывает, что мы работаем со срезом маленьких чисел
var uintData []uint8С типом byte будет работать арифметика и при переполнении значения мы будем его обрезать.
Отметим, что []byte позволяет более эффективно работать со строками, которые в Go являются неизменяемыми типами данных.
s := "code"
b := []byte(s) // []byte{'c','o','d','e'}
В дополнение к типу byte в Go также существует тип rune.
rune
// rune является алиасом для типа int32, который ввели для того, чтобы было проще различать Unicode символы и целочисленные значения со знаком в коде.
type rune = int32
В UTF-8 один символ может включать больше одного байта. Например, символ `世` занимает 3 байта [E4, B8, 96].
Таким образом мы не можем полагаться на []byte(“世”), так как если мы отдельный байт, то испортим и символ и строку. Тип rune позволяет нам не задумываться об этом.
Работает это за счет стандартизации кодировки UTF-8. Например
| Первый байт | Длина символа | Пример |
|---|---|---|
| 0xxxxxxx | 1 байт | ASCII символы |
| 110xxxxx | 2 байта | |
| 1110xxxx | 3 байта | |
| 11110xxx | 4 байта |
s := "Go世"
b := []byte(s) // [71 111 228 184 150]
// Преобразуем в двоичную систему
// [01000111, 01101111, 11100100, 10111000, 10010110]
// 71 (0x47) → ASCII 'G' → rune = 0x47 → 01000111
// 111 (0x6F) → ASCII 'o' → rune = 0x6F → 01101111
// 228 (0xE4) → 11100100 → первый байт 3-байтового символа, так как начинается с 1110
// следующие байты символа
// 184 (0xB8) → 10111000
// 150 (0x96) → 10010110 = 0x4E16 ('世')
// Подробнее устройство строк, кодировок и пакета utf8 в Go мы рассмотрим в отдельной статье.
iota
// iota является предварительно объявленным идентификатором, который можно использовать только в блоке const. Индексация значений начинается с нуля
const iota = 0
const (
A = iota // 0
B // 1
C // 2
)Тип данных iota также можно использовать с арифметическими выражениями:
const (
A = iota + 5 // 5
B // 6
C // 7
)А также битовыми выражениями, например:
const (
KB = 1 << (10 * iota) // 1
MB // 1_024
GB // 1_048_576
)Стоит отметить, что тип iota обнуляется в каждом блоке const
const (
A1 = iota // 0
B1 // 1
)
const (
A2 = iota // 0, iota сбросился
B2 // 1
)
Leave a Reply