integer
Divided by bit length: 8 bits, 16 bits, 32 bits, 64 bits.
Corresponds to signed integers: int8, int16, int32, int64.
Corresponds to unsigned integers: uint8, uint16, uint32, uint64.
Rules for calculating maximum and minimum numbers:
- Signed integer: Because of the sign, the highest bit of the number of bits is used to store the sign, and the others are used to store the data value, so for the number of
n
bits, the value range is: -2^(n-1)^~2^ (n-1)^-1, which corresponds to int8, is -127 ~ 127. - Unsigned positive number: No need to record the sign with the highest bit, all digits represent the number. The value range is: 0 ~ 2^n^-1. For int8, it is 0 ~ 255.
special integer types
int
and uint
as well uintptr
.
int
And uint
depending on the specific platform, its size is the same as the native integer or the most computationally efficient value on that platform. Either 64-bit or 32-bit at the same time, but neither can be assumed to be 64-bit or 32-bit integers. Even under the same hardware, different compilers may choose different sizes.
As for uintptr
its size, it's not clear. But it's definitely big enough to hold a pointer. Often used for low-level programming.
Although int
, uint
may have the same length as int32
, int64
, and ..
other integer types, they are different types.
If you want to determine the size of the int
sumuint
fmt.Println(runtime.GOARCH) // 看看CPU型号
fmt.Println(strconv.IntSize) // 这才是int的大小,上面只是提供一个对照
overflow
Whether it is signed or unsigned, if the number of bits required to calculate the result exceeds the range of the type, it is called overflow . The overflowed high bits are silently discarded.
var i int8 = -128
fmt.Println(i-1) // 127
var i2 int8 = 127
fmt.Println(i2+1) // -128
var u uint8 = 0
fmt.Println(u-1) // 255
var u2 uint8 = 255
fmt.Println(u2+1) // 0
floating point number
float32
, float64
. Follow the IEEE 754
standard.
math.MaxFloat32
float32
The maximum value of the type is given : 3.4028234663852886e+38
.
math.MaxFloat64
则是float64
类型的最大值:1.7976931348623157e+308
。
浮点数可以用来表示小数。
十进制下,float32
有效位大约是6位。float64
有效位大约是15位。
浮点数的打印可以使用:
%g
,保留足够的精度显示%e
,使用指数形式显示%f
,使用无指数形式显示
f := 2.71828
fmt.Printf("%g, %[1]e, %[1]f \n", f) // 2.71828, 2.718280e+00, 2.718280
复数
complex64
、complex128
,二者分别由float32
和float64
组成。
使用real
函数提取复数的实部,使用imag
函数提取复数的虚部。
浮点数或者整数后面加i
就会变成一个虚数,且它的实部为0
。
x := 1+2i
y := 3+4i
布尔值
bool
类型就是布尔值,有true
(真)和false
(假)两个值。
布尔值无法隐式转换成数值,数值也不能转成布尔值。
它的零值是false
。
一元操作符!
表示逻辑取反。!true
表示false
。
字符串
string
表示字符串。它是不可变的字节序列。Go中的字符串内部实现用UTF-8
编码。
字符串的“长度”与遍历字符串的做法
字符串的“长度”
对字符串调用len
函数,获取到的不是字符串的长度(字符的个数),而是字符串的字节数(字节切片的长度)。
如下,尽管字符个数只有6,但字节长度len(s)
却有18,这是因为中文字符以UTF-8
存储,通常包含3~4个字节。
s := "中文字节数多"
fmt.Println("len: ", len(s)) // len: 18
对字符串使用下标索引操作,会返回对应索引的字节,而不是字符。
s := "中文字节数多"
fmt.Println("len: ", len(s)) // len: 18
for i :=0; i < len(s); i++ {
fmt.Printf("%d, %[1]c, %[1]T\n",s[i])
}
对应的输出如下:
len: 18
228, ä, uint8
184, ¸, uint8
173, , uint8
230, æ, uint8
150, �, uint8
135, �, uint8
229, å, uint8
173, , uint8
151, �, uint8
232, è, uint8
138, �, uint8
130, �, uint8
230, æ, uint8
149, �, uint8
176, °, uint8
229, å, uint8
164, ¤, uint8
154, �, uint8
因此不要随便乱用字符串的下标操作,否则可能获得有意想不到的结果。
遍历字符串
由上面的下标操作可以看出,对字符串的下标索引操作会获得单个字节而不是字符,假如现在我们想处理的是UTF-8
解码的字符的话,有两种方式,基本思路都是处理成rune
类型:
第一种,用UTF-8
解码器显式处理这些字符,unicode/utf8
包示例。
utf8.RuneCountInString(s)
返回字符串转为rune
后的个数,Go使用rune
代表一个UTF-8
字符。
utf8.utf8.DecodeRuneInString(s[i:])
处理当前字符串,并算出下一个rune
以及它所占的字节数。
s := "What? 中文字节数多"
runeCount := utf8.RuneCountInString(s)
fmt.Println("runeCount:", runeCount) // runeCount: 12
for i:= 0; i<len(s); {
r, size:= utf8.DecodeRuneInString(s[i:])
fmt.Printf("i: %d, r:%q, type:%T \n", i, r, r)
i += size
}
输出如下:
runeCount: 12
i: 0, r:'W', type:int32
i: 1, r:'h', type:int32
i: 2, r:'a', type:int32
i: 3, r:'t', type:int32
i: 4, r:'?', type:int32
i: 5, r:' ', type:int32
i: 6, r:'中', type:int32
i: 9, r:'文', type:int32
i: 12, r:'字', type:int32
i: 15, r:'节', type:int32
i: 18, r:'数', type:int32
i: 21, r:'多', type:int32
第二种,用range
循环,Go会隐式的进行UTF-8
解码。
注意,这里的i
,指的是字节的下标,而不是字符的下标。
s := "What? 中文字节数多"
for i, r := range s {
fmt.Printf("i: %d, rune: %q, type: %T \n", i, r, r)
}
输出如下:
i: 0, rune: 'W', type: int32
i: 1, rune: 'h', type: int32
i: 2, rune: 'a', type: int32
i: 3, rune: 't', type: int32
i: 4, rune: '?', type: int32
i: 5, rune: ' ', type: int32
i: 6, rune: '中', type: int32
i: 9, rune: '文', type: int32
i: 12, rune: '字', type: int32
i: 15, rune: '节', type: int32
i: 18, rune: '数', type: int32
i: 21, rune: '多', type: int32
Rune与Byte(字节)
int32
的别名是rune
,天然适合存储单个文字符号,为Go所采用的。
rune
类型值代表一个UTF-8
字符。以字节(byte)为单位对Unicode
码点作变长编码。现在计算机都用UTF-8
来表示单个字符。
字符串的是由“字符”组成的,字符用单引号’
包裹起来,如:
var b = 'h'
c := '冲'
fmt.Printf("%d, %q \n", b, b) // 104, 'h'
fmt.Printf("%d, %q \n", c, c) // 20914, '冲'
字节,byte
类型,底层类型是uint8
,由8个bit
组成,它可以代表一个ASCII
码。ASCII
码是满足早期计算机的使用的,它用7位表示128个“字符”(ASCII字符):大小写英文字母、数字、标点符号和设备控制符。
字符串与字节slice的转换
字符串底层是一个字节数组,所以可以和[]byte
类型互换。
s := "abc"
b := []byte(s)
s2 := string(b)
概念上,[]byte(s)
转换操作会分配新的字节数组,拷贝填入s
含有的字节,并生成一个slice
的引用,指向整个数组。反之,用string(b)
也会产生一份副本而不是操作真正的b
,以此保证上面s2
不变。
字符串不可变
字符串是不可变的,表现在其字符串值不可修改。平时我们看到的字符串修改操作、拼接操作并不改变原有的字符串值,而是将操作后生成新的字符串值赋予原来的变量。
s := "left foot"
t := s
s += ", right foot"
尽管字符串创建后,它底层的字节slice不可变,但是普通的字节slice
是可以随意改变的。
var a = []byte{'h', 'e', 'l', 'l', 'o'}
fmt.Printf("%p, %[1]q \n", a) // 0xc00000a098, "hello"
a[4] = ' '
fmt.Printf("%p, %[1]q \n", a) // 0xc00000a098, "hell "
由于字符串的不可变以及避免频繁的操作字符串而导致的多次内存分配和复制,可以使用bytes.Buffer
类型。
见GOPL的一个例子:
func intsToString(values []int) string {
var buf bytes.Buffer
buf.WriteByte('[')
for i, v := range values {
if i > 0 {
buf.WriteString(",")
}
fmt.Fprintf(&buf, "%d", v)
}
buf.WriteByte(']')
return buf.String()
}
func main() {
fmt.Println(intsToString([]int{1, 2, 3})) // [1,2,3]
}
追加ASCII
字符可以用writeByte
,追加UTF-8
编码的文字符号,最好用WriteRune
方法。
基本类型的值都是可比较的
基本类型的值都是可比较的,如布尔值、数值、字符串等。
数值的类型转换
很多整型—整型的转换不会引起值的变化,仅告知编译器如何解读这么值。但缩减大小的类型转换,以及整型与浮点型的相互转换,会因此值的改变或者损失精度。
浮点型转整型会舍弃小数部分并向0取整。
var f = 3.526
i := int(f)
fmt.Printf("f: %v, i: %v \n", f, i) // f: 3.526, i: 3
var i16 = int16(555)
var i8 = int8(i16)
fmt.Printf("i16: %v, i8: %v \n", i16, i8) // i16: 555, i8: 43
运算符
运算符降序排列:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
二元运算符分为五大优先级。同级别的运算符满足左结合律,可以用圆括号指定次序。
常量
常量是一种表达式,保证在编译阶段就计算出对应的值。所有常量本质上都属于基本类型:布尔型、字符串或者数字。
常量自编译后,其值恒定不变。
type Integer int
const I Integer = 10
const S string = "important_secret"
const (
NUM1 = 1
NUM2 = 2
NUM3
NUM4 = 5.5
STR1 = "STR1"
)
func main() {
fmt.Printf("I: %v \n", I)
fmt.Printf("S: %v \n", S)
fmt.Printf("NUM1: %v \n", NUM1)
fmt.Printf("NUM2: %v \n", NUM2)
fmt.Printf("NUM3: %v \n", NUM3)
fmt.Printf("NUM4: %v \n", NUM4)
fmt.Printf("STR1: %v \n", STR1)
}
输出如下:
I: 10
S: important_secret
NUM1: 1
NUM2: 2
NUM3: 2
NUM4: 5.5
STR1: STR1
本文正在参加技术专题18期-聊聊Go语言框架