03 Golang 基本数据类型

一、整型

1. 整型介绍

Go 语言中,整型可以分为以下几类:

 

类型描述
uint8 无符号 8位整型 (0 到 255)
uint16 无符号 16位整型 (0 到 65535)
uint32 无符号 32位整型 (0 到 4294967295)
uint64 无符号 64位整型 (0 到 18446744073709551615)
int8 有符号 8位整型 (-128 到 127)
int16 有符号 16位整型 (-32768 到 32767)
int32 有符号 32位整型 (-2147483648 到 2147483647)
int64 有符号 64位整型 (-9223372036854775808 到 9223372036854775807)

此外,还有一些特殊整型:

类型描述
uint 32位操作系统上就是uint32,64位操作系统上就是uint64
int 32位操作系统上就是int32,64位操作系统上就是int64
uintptr 无符号整型,用于存放一个指针

int 型是计算最快的一种类型。实际使用中,切片或 map 的元素数量等都可以用int来表示。在涉及到二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用intuint

 1 package main
 2  3 import "fmt"
 4  5 func main() {
 6     num1 := 123456
 7     fmt.Printf("%T\n", num1) // int
 8  9     var num int8
10     //num = 111111111 // .\main.go:7:6: constant 111111111 overflows int8 赋值长度超过了8位
11     fmt.Println(num)
12 13 }

 

查看各类整型类型的表数范围:

 1 package main
 2  3 import (
 4     "fmt"
 5     "unsafe"
 6     "math"
 7 )
 8  9 func main() {
10     var i8 int8
11     var i16 int16
12     var i32 int32
13     var i64 int64
14     var ui8 uint8
15     var ui16 uint16
16     var ui32 uint32
17     var ui64 uint64
18 19     fmt.Printf("%T %dB %v~%v\n", i8, unsafe.Sizeof(i8), math.MinInt8, math.MaxInt8)     // int8 1B -128~127
20     fmt.Printf("%T %dB %v~%v\n", i16, unsafe.Sizeof(i16), math.MinInt16, math.MaxInt16) // int16 2B -32768~32767
21     fmt.Printf("%T %dB %v~%v\n", i32, unsafe.Sizeof(i32), math.MinInt32, math.MaxInt32) // int32 4B -2147483648~2147483647
22     fmt.Printf("%T %dB %v~%v\n", i64, unsafe.Sizeof(i64), math.MinInt64, math.MaxInt64) // int64 8B -9223372036854775808~9223372036854775807
23 24     fmt.Printf("%T %dB %v~%v\n", ui8, unsafe.Sizeof(ui8), 0, math.MaxUint8)            // uint8 1B 0~255
25     fmt.Printf("%T %dB %v~%v\n", ui16, unsafe.Sizeof(ui16), 0, math.MaxUint16)         // uint16 2B 0~65535
26     fmt.Printf("%T %dB %v~%v\n", ui32, unsafe.Sizeof(ui32), 0, math.MaxUint32)         // uint32 4B 0~4294967295
27     fmt.Printf("%T %dB %v~%v\n", ui64, unsafe.Sizeof(ui64), 0, uint64(math.MaxUint64)) // uint64 8B 0~18446744073709551615
28 29     var ui uint
30     ui = uint(math.MaxUint64)                                  //再+1会导致overflows错误
31     fmt.Printf("%T %dB %v~%v\n", ui, unsafe.Sizeof(ui), 0, ui) // uint 8B 0~18446744073709551615
32 33     var iMax, iMin int
34     iMax = int(math.MaxInt64)                                           //再+1会导致overflows错误
35     iMin = int(math.MinInt64)                                           //再-1会导致overflows错误
36     fmt.Printf("%T %dB %v~%v\n", iMax, unsafe.Sizeof(iMax), iMin, iMax) // int 8B -9223372036854775808~9223372036854775807
37 38 }

2. 数字字面量语法

Go1.13 版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制浮点数的格式定义数字,还允许我们用 _ 来分隔数字:

 1 package main
 2  3 import "fmt"
 4  5 func main() {
 6     num1 := 123_456
 7     fmt.Println(num1)  // 123456
 8  9     // 定义一个二进制数
10     num2 := 0b00101101
11     fmt.Println(num2)  // 45 --> 0b00101101用十进制表示是45
12 13     // 定义一个八进制数
14     num3 := 0377
15     fmt.Println(num3)  // 255 --> 0377用十进制表示是255
16 17     // 定义一个十六进制数
18     num4 := 0xff
19     fmt.Println(num4)  // 255 --> 0xff用十进制表示是255
20 }

 

我们也可以使用 fmt函数来将一个整数以不同进制形式展示:

 1 package main
 2  3 import "fmt"
 4  5 func main() {
 6     a := 10
 7  8     fmt.Printf("%b \n", a) // 1010  占位符%b表示二进制
 9     fmt.Printf("%o \n", a) // 12    占位符%o表示八进制
10     fmt.Printf("%d \n", a) // 10    占位符%o表示八进制
11     fmt.Printf("%x \n", a) // a     占位符%x表示十六进制小写
12     fmt.Printf("%X \n", a) // A     占位符%X表示十六进制大写
13 }

二、浮点数类型

Go语言支持两种浮点型数:float32float64。这两种浮点型数据格式遵循IEEE 754标准。

类型描述
float32 -3.4028234663852886e+38 ~ 3.4028234663852886e+38
float64 -1.7976931348623157e+308 ~ 1.7976931348623157e+308
 1 package main
 2  3 import (
 4     "fmt"
 5     "unsafe"
 6     "math"
 7 )
 8  9 10 func main() {
11     var f32 float32
12     var f64 float64
13 14     fmt.Printf("%T %dB %v~%v\n", f32, unsafe.Sizeof(f32), -math.MaxFloat32, math.MaxFloat32) // float32 4B -3.4028234663852886e+38~3.4028234663852886e+38
15     fmt.Printf("%T %dB %v~%v\n", f64, unsafe.Sizeof(f64), -math.MaxFloat64, math.MaxFloat64) // float64 8B -1.7976931348623157e+308~1.7976931348623157e+308
16 17 }

 

打印浮点数时,可以使用fmt包配合动词 %f

 1 package main
 2  3 import (
 4     "fmt"
 5     "math"
 6 )
 7  8  9 func main() {
10     fmt.Printf("%f\n", math.Pi)   // 3.141593
11     fmt.Printf("%.2f\n", math.Pi) // 3.14
12 }

三、复数类型

复数类型有 complex64complex128 两种类型,complex64 的实部和虚部为32位,complex128 的实部和虚部为64位。

 1 package main
 2  3 import "fmt"
 4  5 func main() {
 6     var c1 complex64
 7     c1 = 1 + 2i
 8     var c2 complex128
 9     c2 = 2 + 3i
10     fmt.Println(c1) // (1+2i)
11     fmt.Println(c2) // (2+3i)
12 }

四、布尔类型

Go语言中以bool类型进行声明布尔型数据,布尔型数据只有true(真)false(假)两个值。布尔类型经常用在条件判断语句,循环语句逻辑表达式中,使用布尔类型时需要注意:

  • 布尔类型变量的默认值为false

  • Go 语言中不允许将整型强制转换为布尔型,即不能使用0和非0表示真假。

  • 布尔型无法参与数值运算,也无法与其他类型进行转换。

 1 package main
 2  3 import "fmt"
 4  5 func main() {
 6     // 初始化布尔类似变量
 7     var b1 bool = true
 8     var b2 bool = false
 9     var b3 = true
10     var b4 = false
11 12     b5 := true
13     b6 := false
14 15     fmt.Printf("b1: %v\n", b1) // b1: true
16     fmt.Printf("b2: %v\n", b2) // b2: false
17     fmt.Printf("b3: %v\n", b3) // b3: true
18     fmt.Printf("b4: %v\n", b4) // b4: false
19     fmt.Printf("b5: %v\n", b5) // b5: true
20     fmt.Printf("b6: %v\n", b6) // b6: false
21 22     // 布尔值用在条件判断中
23     age := 18
24     ok := age >= 18
25     if ok {
26         fmt.Println("你已经成年")
27     } else {
28         fmt.Println("你还未成年")
29     }
30 31     // 布尔值用在循环语句中
32     count := 10
33     for i := 0; i < count; i++ {
34         fmt.Printf("i: %v\n", i)
35     }
36 37     // 布尔值用在逻辑表达式中
38     age1 := 18
39     gender := ""
40 41     if age1 >= 18 && gender == "" {
42         fmt.Println("你是成年男子")
43     }
44 }

五、字符串类型

Go 语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(intboolfloat32float64 等)一样。 Go 语言里的字符串的内部实现使用UTF-8编码。

1. 定义字符串

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6     // 初始化当行字符串
 7     str1 := "hello \nworld!"
 8 
 9     // 初始化多行字符串
10     str2 := `hello\n
11              world`
12 
13     fmt.Println(str1)
14     fmt.Println(str2)
15 }

定义单行字符串使用英文下的双引号包裹,字符串中如果使用到转义字符会生效;定义一个多行字符串使用反引号包裹,但是所有的转义字符均无效,文本将会原样输出。

2. 字符串常用方法

2.1 字符串长度

1 str1 := "abcdefg"
2 fmt.Println(len(str1)) // 7

2.2 拼接字符串

  • 方式一,使用加号

 1 name := "cdc"
 2 age := "18"
 3 msg1 := name + " " + age
 4 fmt.Printf("msg1: %v\n", msg1) // cdc 18
 5 
 6 msg2 := ""
 7 msg2 += name
 8 msg2 += " "
 9 msg2 += age
10 fmt.Printf("msg2: %v\n", msg2) // cdc 18

golang 中字符串类型都是不可变的,每次运算都会产生一个新的字符串,所以会产生很多临时的无用的字符串,不仅没有用,还会给 gc 带来额外的负担,所以性能比较差,不推荐该方式。

  • 方式二,使用 fmt.Sprintf() 方法

1 name := "cdc"
2 age := "18"
3 msg := fmt.Sprintf("%s %s", name, age)
4 fmt.Printf("msg: %v\n", msg)

内部使用 []byte 实现,不像直接运算符这种会产生很多临时的字符串,但是内部的逻辑比较复杂,有很多额外的判断,还用到了 interface,所以性能也不是很好。

  • 方式三,使用 strings.Join() 方法

1 msg := strings.Join([]string{name, age}, " ")
2 fmt.Printf("msg: %v\n", msg)

Join 会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入,在已有一个数组的情况下,这种效率会很高,但是本来没有,去构造这个数据的代价也不小。

  • 方式四,使用 buffer.WriteString() 方法

 1 package main
 2 
 3 import (
 4     "bytes"
 5     "fmt"
 6 )
 7 
 8 func main() {
 9     var buffer bytes.Buffer
10     buffer.WriteString("cdc")
11     buffer.WriteString(" ")
12     buffer.WriteString("18")
13     fmt.Printf("msg: %v\n", buffer.String())
14 }

推荐该方式,可以当成可变字符使用,对内存的增长也有优化。

2.3 字符串分割

1 str2 := "how do you do"
2 splitRet := strings.Split(str2, "do")
3 fmt.Printf("%v -- %T -- %d\n", splitRet, splitRet, len(splitRet)) // [how   you  ] -- []string -- 3

2.4 判断是否包含

1 str2 := "how do you do"
2 fmt.Println(strings.Contains(str2, "do")) // true
3 fmt.Println(strings.Contains(str2, "hehe")) // false

2.5 判断前/后缀

1 str2 := "how do you do"
2 fmt.Println(strings.HasPrefix(str2, "how")) // true 判断前缀是否为how
3 fmt.Println(strings.HasPrefix(str2, "do")) // false
4 fmt.Println(strings.HasSuffix(str2, "how")) // false 判断后缀是否为how
5 fmt.Println(strings.HasSuffix(str2, "do")) // true

2.6 字串出现位置

1 str2 := "how do you do"
2 fmt.Println(strings.Index(str2, "do"))  // 4
3 fmt.Println(strings.LastIndex(str2, "do"))  // 11

2.7 字符串切片

1 // 通过索引切片,范围左闭右开
2 fmt.Println(str2[:5])  // how d  --> 从开头切到指定索引
3 fmt.Println(str2[3:6]) //  do --> 从指定开始索引位置切到指定结束索引位置
4 fmt.Println(str2[2:])  // w do you do --> 从指定索引位置切到字符串最后
5 fmt.Println(str2[:])   // how do you do --> 从开始切到最后

2.8 格式化输出

Go 语言中输出字符串时可以使用各种占位符来输出不同类型的值:

 1 // 普通占位符
 2 %v        相应值的默认格式。在打印结构体时,“加号”标记(%+v)会添加字段名
 3 %#v        相应值的Go语法表示                            
 4 %T        相应值的类型的Go语法表示                        
 5 %%        字面上的百分号,并非值的占位符
 6 
 7 // 布尔占位符
 8 %t        单词 truefalse
 9 
10 // 整数占位符
11 %b        二进制表示                                
12 %c        相应Unicode码点所表示的字符                    
13 %d        十进制表示                                    
14 %o        八进制表示                                    
15 %q        单引号围绕的字符字面值,由Go语法安全地转义        
16 %x        十六进制表示,字母形式为小写 a-f                
17 %X        十六进制表示,字母形式为大写 A-F                
18 %U        Unicode格式:U+1234,等同于 "U+%04X"
19 
20 // 浮点数和复数的组成部分
21 %b        无小数部分的,指数为二的幂的科学计数法,与 strconv.FormatFloat的 'b' 转换格式一致。例如 -123456p-78
22 %e        科学计数法,例如 -1234.456e+78                                    
23 %E        科学计数法,例如 -1234.456E+78                                    
24 %f        有小数点而无指数,例如 123.456                                    
25 %g        根据情况选择 %e 或 %f 以产生更紧凑的(无末尾的0)输出                
26 %G        根据情况选择 %E 或 %f 以产生更紧凑的(无末尾的0)输出
27 
28 // 字符串与字节切片
29 %s        输出字符串表示(string类型或[]byte)                    
30 %q        双引号围绕的字符串,由Go语法安全地转义                            
31 %x        十六进制,小写字母,每字节两个字符                                
32 %X        十六进制,大写字母,每字节两个字符
33 
34 // 指针
35 %p        十六进制表示,前缀 0x

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6     // 普通占位符
 7     type website struct {
 8         url string
 9     }
10 
11     web := website{url: "www.chendacheng.com"}
12 
13     name := "cdc"
14 
15     fmt.Printf("%v\n", name) // cdc
16     fmt.Printf("%+v\n", web) // {url:www.chendacheng.com}
17     fmt.Printf("%#v\n", web) // main.website{url:"www.chendacheng.com"}
18     fmt.Printf("%T\n", name) // string
19 
20     // 布尔占位符
21     fmt.Printf("%t\n", true) //输出值的 true 或 false
22 
23     // 整数占位符
24     fmt.Printf("%b\n", 100)  //二进制表示  1100100
25     fmt.Printf("%c\n", 1111) //数值对应的 Unicode 编码字符  ї
26     fmt.Printf("%d\n", 10)   //十进制表示  10
27     fmt.Printf("%o\n", 8)    //八进制表示  10
28     fmt.Printf("%q\n", 22)   //转化为十六进制并附上单引号  '\x16'
29     fmt.Printf("%x\n", 1223) //十六进制表示,用a-f表示  4c7
30     fmt.Printf("%X\n", 1223) //十六进制表示,用A-F表示  4C7
31     fmt.Printf("%U\n", 1024) //Unicode表示  U+0400
32 
33     fmt.Printf("%s\n", "wqdew")  //直接输出字符串或者[]byte  wqdew
34     fmt.Printf("%q\n", "dedede") //双引号括起来的字符串  "dedede"
35     fmt.Printf("%x\n", "abczxc") //每个字节用两字节十六进制表示,a-f表示  6162637a7863
36     fmt.Printf("%X\n", "asdzxc") //每个字节用两字节十六进制表示,A-F表示  6173647A7863
37 
38     // 浮点数和复数的组成部分
39     fmt.Printf("%b\n", 12.34)    //无小数部分,两位指数的科学计数法6946802425218990p-49
40     fmt.Printf("%e\n", 12.345)   //科学计数法,e表示  1.234500e+01
41     fmt.Printf("%E\n", 12.34455) //科学计数法,E表示  1.234455E+01
42     fmt.Printf("%f\n", 12.3456)  //有小数部分,无指数部分  12.345600
43     fmt.Printf("%g\n", 12.3456)  //根据实际情况采用%e或%f输出  12.3456
44     fmt.Printf("%G\n", 12.3456)  //根据实际情况采用%E或%f输出  12.3456
45 
46     // 指针占位符
47     fmt.Printf("%T\n", &name) //*string 字符串指针
48     fmt.Printf("%p\n", &name) //0xc000042270 指针地址
49 }

六、byte和rune类型

1. 字节类型简介

组成每个字符串的元素叫做字符,可以通过遍历或者单个获取字符串元素获得字符。 字符用英文单引号包裹。

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6     var a = ''
 7     var b = 'a'
 8     fmt.Printf("a: %v,%c\n", a, a) // a: 21326,华
 9     fmt.Printf("b: %v,%c\n", b, b) // b: 97,a
10 }

Go 语言的字符有以下两种:

  • uint8类型,或者叫 byte 型,代表一个ASCII码字符。

  • rune类型,代表一个 UTF-8字符

当需要处理中文、日文或者其他复合字符时,则需要用到rune类型。rune类型实际是一个int32。Go 使用了特殊的 rune 类型来处理 Unicode,让基于 Unicode 的文本处理更为方便,也可以使用 byte 型进行默认字符串处理,性能和扩展性都有照顾。

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6     s := "hello南京"
 7     for i := 0; i < len(s); i++ { //byte
 8         fmt.Printf("%v(%c) ", s[i], s[i])
 9     }
10 
11     fmt.Println()
12     
13     for _, r := range s { //rune
14         fmt.Printf("%v(%c) ", r, r)
15     }
16 }

两种遍历字符串的输出结果如下:

1 104(h) 101(e) 108(l) 108(l) 111(o) 229(å) 141() 151(—) 228(ä) 186(º) 172(¬) 
2 104(h) 101(e) 108(l) 108(l) 111(o) 21335(南) 20140(京) 

因为UTF8编码下一个中文汉字由3~4个字节组成,所以我们不能简单的按照字节去遍历一个包含中文的字符串,否则就会出现上面输出中第一行的结果。建议使用for + range 方式遍历字符串。

2. 修改字符串

字符串底层是一个byte数组,且字符串的长度是byte字节的长度,所以字符串类型可以和 []byte 类型相互转换。rune 类型用来表示utf8字符,一个 rune 字符由一个或多个 byte 组成。虽然字符串是不可变的,但是我们可以先将其转换成[]rune[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组,即不会修改原始字符串,而是生成一个新的修改过后的字符串。

 1 package main
 2  3 import "fmt"
 4  5 func main() {
 6     s1 := "big"
 7     // 强制类型转换
 8     byteS1 := []byte(s1)
 9     byteS1[0] = 'p'
10     fmt.Println(string(byteS1)) // pig
11 12     s2 := "白萝卜"
13     runeS2 := []rune(s2)
14     runeS2[0] = ''
15     fmt.Println(string(runeS2)) // 红萝卜
16 }

七、强制类型转换

 1 package main
 2  3 import "fmt"
 4  5 func main() {
 6     num := 12
 7     numFloat := float32(num)
 8     fmt.Printf("%T -- %v\n", numFloat, numFloat) // float32 -- 12
 9 10     a := 'a'
11     aStr := string(a)
12     fmt.Printf("%T -- %v\n", aStr, aStr) // string -- a
13 }
posted @ 2023-03-12 22:50  cdcx  阅读(136)  评论(0编辑  收藏  举报