На этом шаге рассмотрим целочисленные типы данных в Go.
Язык обеспечивает как знаковую, так и беззнаковую целочисленную арифметику. Имеются знаковые целые числа четырех размеров - 8, 16, 32 и 64 бита, - представленные типами int8, int16, int32 и int64, а также соответствующие беззнаковые версии uint8, uint16, uint32 и uint64. Для большинства ситуаций достаточно использовать единственный целочисленный тип int. Переменные этого типа подходят для использования в качестве счетчиков циклов, индексов массивов и срезов и арифметических вычислений общего назначения.
Тип rune является синонимом для типа int32 и по соглашению указывает, что данное значение является символом Unicode. Эти два имени могут использоваться взаимозаменяемо. Тип byte является синонимом для типа uint8 и подчеркивает, что это значение является фрагментом неформатированных данных, а не малым числом.
Имена и диапазоны значений представлены в таблице 1.
Тип | Диапазон представляемых значений |
---|---|
byte | Синоним типа uint8 |
int | Диапазон int32 или int64, в зависимости от реализации |
int8 | [-128, 127] |
int16 | [-32768, 32767] |
int32 | [-2147483648, 2147483647] |
int64 | [-9223372036854775808, 9223372036854775807] |
rune | Синоним типа int32 |
uint | Диапазон uint32 или uint64, в зависимости от реализации |
uint8 | [0, 255] |
uint16 | [0, 65535] |
uint32 | [0, 4294967295] |
uint64 | [0, 18446744073709551615] |
uintptr | Беззнаковое целое, пригодное для хранения значения указателя |
В таблице 2 представлены арифметические операторы, применяющиеся только к встроенным целочисленным типам.
Оператор | Описание/результат |
---|---|
^x | Поразрядное дополнение значения x |
x %= y | Присваивает переменной x остаток от деления x на y; деление на нуль вызывает аварию |
x &= y | Присваивает переменной x результат поразрядной операции "И" над значениями x и y |
x |= y | Присваивает переменной x результат поразрядной операции "ИЛИ" над значениями x и y |
x ^= y | Присваивает переменной x результат поразрядной операции "исключающее ИЛИ" над значениями x и y |
x &^= y | Присваивает переменной x результат поразрядной операции "И-НЕ" над значениями x и y |
x >>= u | Присваивает переменной x результат поразрядного сдвига вправо значения x на беззнаковое целое число u бит |
x <<= u | Присваивает переменной x результат поразрядного сдвига влево значения x на беззнаковое целое число u бит |
x % y | Остаток от деления x на y; деление на нуль вызывает аварию |
x & y | Операция "поразрядное И" |
x | y | Операция "поразрядное ИЛИ" |
x ^ y | Операция "поразрядное исключающее ИЛИ" |
x &^ y | Операция "поразрядное И-НЕ" |
x << u | Поразрядный сдвиг влево значения x на беззнаковое целое число u бит |
x >> u | Поразрядный сдвиг вправо значения x на беззнаковое целое число u бит |
Если результат арифметической операции, как знаковой, так и беззнаковой, имеет больше битов, чем может быть представлено типом результата, мы говорим о переполнении (overflow). При этом старшие биты, которые не помещаются в результате, отбрасываются. Если исходное число имеет знаковый тип, результат может быть отрицательным, если левый бит равен 1, как показано в следующем примере с int8:
var u uint8 = 255 fmt.Println(u, u+1, u*u) // "255 0 1" var i int8 = 127 fmt.Println(i, i+1, i*i) // "127 -128 1"
Архив примера можно взять здесь.
Рассмотрим пример использования битовых операций для интерпретации значения uint8 в качестве набора восьми независимых битов. Он использует символы преобразования %b в Printf для вывода двоичных цифр числа; 08 уточняет поведение %b, заставляя дополнять результат нулями так, чтобы выводилось ровно 8 цифр.
var x uint8 = 1<<1 | 1<<5 var y uint8 = 1<<1 | 1<<2 fmt.Printf("%08b\n", x) // "00100010", множество {1,5} fmt.Printf("%08b\n", y) // "00000110", множество {1,2} fmt.Printf("%08b\n", x&y) // "00000010", пересечение {1} fmt.Printf("%08b\n", x|y) // "00100110", объединение {1,2,5} fmt.Printf("%08b\n", x^y) // "00100100", симметричная разность {2,5} fmt.Printf("%08b\n", x&^y) // "00100000", разность {5} for i := uint(0); i < 8; i++ { if x&(1<<i) != 0 { // Проверка принадлежности множеству fmt.Println(i) // "1", "5" } } fmt.Printf("%08b\n", x<<1) // "01000100", множество {2,6} fmt.Printf("%08b\n", x>>1) // "00010001", множество {0,4}
Архив примера можно взять здесь.
Целочисленные литералы любого размера и типа могут быть записаны как десятичные числа, как восьмеричные числа, если они начинаются с 0 (как 0666), или как шестнадцатеричные, если они начинаются с 0х или 0Х (как 0xabc). Шестнадцатеричные цифры могут быть как прописными, так и строчными. При выводе чисел с использованием пакета fmt можно управлять системой счисления и форматом вывода с помощью символов преобразования %d, %o и %х, как показано в следующем примере:
о := 0666 fmt.Printf("%d %[1]о %#[1]о\n", о) // "438 666 0666" x := int64(0xabc) fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x) // "2748 abc 0xabc 0XABC"
Архив примера можно взять здесь.
Форматная строка Printf содержит несколько символов преобразования %, которые требуют того же количества дополнительных аргументов, но [1] после % говорит функции Printf о том, что ей следует использовать первый операнд снова и снова. Во-вторых, наличие # при символах преобразования %о, %х или %Х говорит функции Printf о том, что при выводе должен быть добавлен префикс, указывающий систему счисления - 0, 0х или 0Х соответственно.
Литералы записываются как символ в одинарных кавычках. Простейший пример с использованием ASCII-символа - 'а', но так можно записать любой символ Unicode - либо непосредственно, либо с помощью числовых управляющих последовательностей (начинаются с символа \).
Руны выводятся с помощью символов преобразования %с, или %q - если требуются апострофы. Например:
ascii := 'а' Unicode := '★' newline := '\n' fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'" fmt .Printf ("%d %[1]c %[1]q\n", Unicode) // "9733 ★ '★'" fmt.Printf("%d %[1]q\n", newline) // "10 '\n'"
Архив примера можно взять здесь.
На следующем шаге рассмотрим вещественные типы данных в Go.