go基本数据类型
最厉害的菜鸡 人气:0整数
按位长度分度分为:8位,16位,32位,64位。
对应有符号整数:int8,int16,int32,int64。
对应无符号整数:uint8,uint16,uint32,uint64。
计算最大数和最小数规则:
- 有符号整数:由于有符号,因此位数的最高位被用来存储符号,其他为存储数据值,所以对于
n
位数来说,取值范围是:-2^(n-1)^~2^(n-1)^-1,对应int8来说,就是 -127 ~ 127。 - 无符号正数:不需要用最高位记录符号,全部位数表示数字。取值范围是:0 ~ 2^n^-1。对于int8来说,就是0 ~ 255。
特殊的整数类型
int
和uint
以及uintptr
。
int
和uint
根据特定的平台,其大小与原生的整数相同或者是该平台上运算效率最高的值。要么同时是64位,要么同时是32位,但也不能假定他们就是64位或者32位的整型。即使在一样的硬件下,不同编译器也有可能选用不同的大小。
至于uintptr
,它的大小并不明确。但它肯定足够容纳一个指针的大小。常用于底层编程。
虽然int
、uint
的长度可能与int32
、int64
、..
其他整型相同但是他们是不同的类型。
如果想确定int
和uint
的大小
fmt.Println(runtime.GOARCH) // 看看CPU型号 fmt.Println(strconv.IntSize) // 这才是int的大小,上面只是提供一个对照
溢出
不论是有符号数还是无符号数,若计算结果所需的位超出类型的范围,就称为溢出。溢出的高位会被无提示地抛弃。
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
浮点数
float32
、float64
。遵循IEEE 754
标准。
math.MaxFloat32
给出了float32
类型的最大值: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
加载全部内容