# Golang的基础数据类型 > 我们将从最基本的类型开始,Go语言的基本类型部分跟C语言很类似,熟习C语言的朋友们应该不会陌生 ## 整型 整型分为以下两个大类: - 按长度分为:`int8`、`int16`、`int32`、`int64` - 对应的无符号整型:`uint8`、`uint16`、`uint32`、`uint64` 其中,`uint8`就是我们熟知的`byte`型,`int16`对应C语言中的`short`型,`int64`对应C语言中的`long`型 | 类型名 | 字节宽度 | 取值范围 | | ------- | ---------- | ------------------------------------------------------------ | | int | 与平台有关 | 32 位系统 4 字节,64 位系统 8 字节 有符号整型 | | uint | 与平台有关 | 32 位 系统 4 字节,64 位 系统 8 字节 无符号整形 | | int8 | 1 字节 | 用 8 位表示的有符号整型 取值范围为:[-128, 127] | | int16 | 2 字节 | 用 16 位表示的有符号整型 取值范围为:[-32768,32767] | | int32 | 4 字节 | 用 32 位表示的有符号整型,取值范围为:[-2147483648,2147483647] | | int64 | 8 字节 | 用 64 位表示的有符号整型,取值范围为:[-9223372036854775808,9223372036854775807] | | uint8 | 1 字节 | 用 8 位表示的无符号整型,取值范围为:[0,255] | | uint16 | 2 字节 | 用 16 位表示的无符号整型,取值范围为:[0,65535] | | uint32 | 4 字节 | 用 32 位表示的无符号整型,取值范围为:[0,4294967295] | | uint64 | 8 字节 | 用 64 位表示的无符号整型,取值范围为:[0,18446744073709551615] | | uintptr | 与平台有关 | 32 位系统 4 字节,64 位系统 8 字节指针值的无符号整型 | ```{IMPORTANT} 在使用int和 uint类型时,不能假定它是32位或64位的整型,而是考虑int和uint可能在不同平台上的差异。 ``` 获取对象的长度的内建`len()`函数返回的长度可以根据不同平台的字节长度进行变化。实际使用中,切片或 `map `的元素数量等都可以用int来表示。在涉及到二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用`int`和` uint`。 ### 数字字面量语法(Number literals syntax) Go`1.13`版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制浮点数的格式定义数字,例如: - `v := 0b00101101`, 代表二进制的 101101,相当于十进制的 45 - `v := 0o377`,代表八进制的 377,相当于十进制的 255 - `v := 0x1p-2`,代表十六进制的 1 除以 2²,也就是 0.25。 - 而且还允许我们用 `_` 来分隔数字,比如说: `v := 123_456` 表示 v 的值等于 123456。 我们可以借助`fmt`函数来将一个整数以不同进制形式展示。 ::: {tabbed} INPUT ```go package main import "fmt" func main(){ // 十进制 var a int = 10 fmt.Printf("%d \n", a) // 10 fmt.Printf("%b \n", a) // 1010 占位符%b表示二进制 // 八进制 以0开头 var b int = 077 fmt.Printf("%o \n", b) // 77 // 十六进制 以0x开头 var c int = 0xff fmt.Printf("%x \n", c) // ff fmt.Printf("%X \n", c) // FF } ``` ::: ::: {tabbed} OUTPUT ```GO 10 1010 77 ff FF ``` ::: ### 思考,数值溢出 ```go package main import "fmt" func main() { var myInt int8 = 127 fmt.Printf("数字:%d 类型:%T 值:%v\n", myInt, myInt, myInt) myInt += 1 fmt.Printf("数字:%d 类型:%T 值:%v\n", myInt, myInt, myInt) } ``` ## 浮点型 Go语言支持两种浮点型数:`float32`和`float64`。 这两种浮点型数据格式遵循`IEEE 754`标准: - `float32` 的浮点数的最大范围约为 `3.4e38`,可以使用常量定义:`math.MaxFloat32` - `float64` 的浮点数的最大范围约为 `1.8e308`,可以使用一个常量定义:`math.MaxFloat64` | 类型名 | 字节宽度 | 取值范围 | | ------- | -------- | --------------------- | | float32 | 4字节 | IEEE-754 32位浮点型数 | | float64 | 8字节 | IEEE-754 64位浮点型数 | 打印浮点数时,可以使用`fmt`包配合动词`%f`,代码如下: ::: {tabbed} INPUT ```go package main import ( "fmt" "math" ) func main() { fmt.Printf("%f\n", math.Pi) fmt.Printf("%.2f\n", math.Pi) } ``` ::: ::: {tabbed} OUTPUT ```GO 3.141593 3.14 ``` ::: ## 复数 | 类型名 | 字节宽度 | 取值范围 | | ---------- | -------- | --------------- | | complex64 | 4字节 | 32 位实数和虚数 | | complex128 | 8字节 | 64 位实数和虚数 | ::: {tabbed} INPUT ```go package main import "fmt" func main() { var c1 complex64 c1 = 1 + 2i var c2 complex128 c2 = 2 + 3i fmt.Println(c1) fmt.Println(c2) } ``` ::: ::: {tabbed} OUTPUT ```GO (1+2i) (2+3i) ``` ::: ```{tip} 复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。 ``` ## 布尔型 Go语言中以`bool`类型进行声明布尔型数据,布尔型数据只有`true(真)`和`false(假)`两个值。 **注意:** 1. 布尔类型变量的默认值为`false`。 2. Go 语言中不允许将整型强制转换为布尔型. 3. 布尔型无法参与数值运算,也无法与其他类型进行转换。 ```go b1 := true var b2 bool // 默认为false fmt.Printf("b1 >>> %T\nb2 >>> %T\nb2 value: %v\n", b1, b2, b2) ``` ## 字符串 Go语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型`(int、bool、float32、float64 等)`一样。 Go 语言里的字符串的内部实现使用`UTF-8`编码。 字符串的值为双引号(")中的内容,可以在Go语言的源码中直接添加非`ASCII`码字符,例如: ``` s1 := "hello" s2 := "你好" ``` ### 字符串转义符 Go 语言的字符串常见转义符包含回车、换行、单双引号、制表符等,如下表所示。 | 转义符 | 含义 | | :----: | :--------------------------------: | | `\r` | 回车符(返回行首) | | `\n` | 换行符(直接跳到下一行的同列位置) | | `\t` | 制表符 | | `\'` | 单引号 | | `\"` | 双引号 | | `\\` | 反斜杠 | 举个例子,我们要打印一个Windows平台下的一个文件路径: ::: {tabbed} INPUT ```go package main import "fmt" func main() { str1 := "\"c:\\Code\\lesson1\\go.exe\"" str2 := `\"c:\\Code\\lesson1\\go.exe\"` // ``符号内的字符串强制不转义! fmt.Println("str1:", str1) fmt.Println("str2:", str2) } ``` ::: ::: {tabbed} OUTPUT ```GO str1: "c:\Code\lesson1\go.exe" str2: \"c:\\Code\\lesson1\\go.exe\" ``` ::: ```{tip} 反引号```` 内定义的字符串输出的时候不转义 ``` ### 多行字符串 Go语言中要定义一个多行字符串时,就必须使用`反引号`字符: ::: {tabbed} INPUT ```go package main import "fmt" func main() { s2 := ` 第一行 第二行 第三行 ` fmt.Printf("多行字符串如下:%s", s2) } ``` ::: ::: {tabbed} OUTPUT ```GO 多行字符串如下: 第一行 第二行 第三行 ``` ::: 反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。 ### 字符串的常用操作 | 方法 | 介绍 | | :---------------------------------: | :------------: | | len(str) | 求长度 | | +或fmt.Sprintf | 拼接字符串 | | strings.Split | 分割 | | strings.contains | 判断是否包含 | | strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 | | strings.Index(),strings.LastIndex() | 子串出现的位置 | | strings.Join(a[]string, sep string) | join操作 | ### 计算字符串长度 比较两个字符串长度并返回最大值与最小值 ```go //Compare two string lengths and return the maximum and minimum values func Len4Str(str1, str2 string) (max, min int) { l1, l2 := len(str1), len(str2) if l1>l2 { max, min = l1, l2 } else { max, min = l2, l1 } return } ``` ### 字符串拼接 #### 加号连接 `+` ```go //Concatenate strings by "+" func concateStrings(strArgs ...string) string { ret := "" for _, str := range strArgs { ret += str } return ret } ``` ```go //Concatenate strings via Sprintf func concateStrings2(strArgs ...string) string { ret := "" for _, str := range strArgs { ret = fmt.Sprintf("%s%s",ret,str) } return ret } ``` #### strings.Join ```{py:function} func Join(str []string, sep string) string This function concatenates all the elements present in the slice of string into a single string. This function is available in string package. ``` - 参数说明 | 参数 | 说明 | 备注 | | ---- | ------------------------ | -------------------- | | str | 待分割的字符串可迭代对象 | 字符串切片类型的参数 | | sep | 分隔符 | 字符串类型的参数 | - 返回值 ​ 返回一个字符串 ```go //Concatenate strings with join func concateStrings3(strArgs ...string) string { return strings.Join(strArgs, "") } ``` #### strings.Builder ```go //Concatenate strings with Builder func concateStrings4(strArgs ...string) string { strB := strings.Builder{} for _, str := range strArgs { strB.WriteString(str) } fmt.Println(strB.String()) return strB.String() } ``` ### 字符串切割 #### Split ```{py:function} func strings.Split(str, sep string) []string This function splits a string into all substrings separated by the given separator and returns a slice which contains these substrings. ``` - 参数说明 | 参数 | 说明 | 备注 | | ---- | -------------- | ---------------- | | str | 待分割的字符串 | 字符串类型的参数 | | sep | 分隔符 | 字符串类型的参数 | - 返回值 ​ 返回一个字符串切片。 ::: {tabbed} 示例 ```go // Go program to illustrate how to split a string package main import ( "fmt" "strings" ) // Main function func main() { // Creating and initializing the strings str1 := "Welcome, to the, online portal, of GeeksforGeeks" str2 := "My dog name is Dollar" str3 := "I like to play Ludo" // Displaying strings fmt.Println("String 1: ", str1) fmt.Println("String 2: ", str2) fmt.Println("String 3: ", str3) // Splitting the given strings // Using Split() function res1 := strings.Split(str1, ",") res2 := strings.Split(str2, "") res3 := strings.Split(str3, "!") res4 := strings.Split("", "GeeksforGeeks, geeks") // Displaying the result fmt.Printf("\nResult 1: %#v\n", res1) fmt.Printf("Result 2: %#v\n", res2) fmt.Printf("Result 3: %#v\n", res3) fmt.Printf("Result 4: %#v\n", res4) } ``` ::: ::: {tabbed} OUTPUT ```go String 1: Welcome, to the, online portal, of GeeksforGeeks String 2: My dog name is Dollar String 3: I like to play Ludo Result 1: []string{"Welcome", " to the", " online portal", " of GeeksforGeeks"} Result 2: []string{"M", "y", " ", "d", "o", "g", " ", "n", "a", "m", "e", " ", "i", "s", " ", "D", "o", "l", "l", "a", "r"} Result 3: []string{"I like to play Ludo"} Result 4: []string{""} ``` ::: #### SplitAfter ```{py:function} func SplitAfter(str, sep string) []string This function splits a string into all substrings after each instance of the given separator and returns a slice which contains these substrings. ``` - 参数说明 | 参数 | 说明 | 备注 | | ---- | -------------- | ---------------- | | str | 待分割的字符串 | 字符串类型的参数 | | sep | 分隔符 | 字符串类型的参数 | - 返回值 ​ 返回一个字符串切片。 ::: {tabbed} 示例 ```go // Go program to illustrate how to split a string package main import ( "fmt" "strings" ) // Main function func main() { // Creating and initializing the strings str1 := "Welcome, to the, online portal, of GeeksforGeeks" str2 := "My dog name is Dollar" str3 := "I like to play Ludo" // Displaying strings fmt.Println("String 1: ", str1) fmt.Println("String 2: ", str2) fmt.Println("String 3: ", str3) // Splitting the given strings // Using SplitAfter() function res1 := strings.SplitAfter(str1, ",") res2 := strings.SplitAfter(str2, "") res3 := strings.SplitAfter(str3, "!") res4 := strings.SplitAfter("", "GeeksforGeeks, geeks") // Displaying the result fmt.Printf("\nResult 1: %#v\n", res1) fmt.Printf("Result 2: %#v\n", res2) fmt.Printf("Result 3: %#v\n", res3) fmt.Printf("Result 4: %#v\n", res4) } ``` ::: ::: {tabbed} OUTPUT ```go String 1: Welcome, to the, online portal, of GeeksforGeeks String 2: My dog name is Dollar String 3: I like to play Ludo Result 1: []string{"Welcome,", " to the,", " online portal,", " of GeeksforGeeks"} Result 2: []string{"M", "y", " ", "d", "o", "g", " ", "n", "a", "m", "e", " ", "i", "s", " ", "D", "o", "l", "l", "a", "r"} Result 3: []string{"I like to play Ludo"} Result 4: []string{""} ``` ::: #### SplitAfterN ```{py:function} func SplitAfterN(str, sep string, m int) []string This function splits a string into all substrings after each instance of the given separator and returns a slice which contains these substrings. ``` ```{tip} str是字符串,sep是分隔符,m 用于查找要返回的子字符串数。 - 以str为分隔符,将s切分成多个子串,结果中**包含**str本身。 - 如果str为空,则将s切分成Unicode字符列表。如果s 中没有str子串,则将整个s作为 []string 的第一个元素返回。 - 参数n表示最多切分出几个子串,超出的部分将不再切分。 - 如果n为0,则返回 nil;如果 n 小于 0,则不限制切分个数,全部切分 ``` - 参数说明 | 参数 | 说明 | 备注 | | ---- | -------------- | ---------------- | | str | 待分割的字符串 | 字符串类型的参数 | | sep | 分隔符 | 字符串类型的参数 | | m | 分割次数 | int类型的参数 | - 返回值 ​ 返回一个字符串切片。 ::: {tabbed} 示例 ```go // Go program to illustrate how to split a string package main import ( "fmt" "strings" ) // Main function func main() { // Creating and initializing the strings str1 := "Welcome, to the, online portal, of GeeksforGeeks" str2 := "My dog name is Dollar" str3 := "I like to play Ludo" // Displaying strings fmt.Println("String 1: ", str1) fmt.Println("String 2: ", str2) fmt.Println("String 3: ", str3) // Splitting the given strings // Using SplitAfterN() function res1 := strings.SplitAfterN(str1, ",", 2) res2 := strings.SplitAfterN(str2, "", 4) res3 := strings.SplitAfterN(str3, "!", 1) res4 := strings.SplitAfterN("", "GeeksforGeeks, geeks", 3) // Displaying the result fmt.Printf("\nResult 1: %#v\n", res1) fmt.Printf("Result 2: %#v\n", res2) fmt.Printf("Result 3: %#v\n", res3) fmt.Printf("Result 4: %#v\n", res4) } ``` ::: ::: {tabbed} OUTPUT ```go String 1: Welcome, to the, online portal, of GeeksforGeeks String 2: My dog name is Dollar String 3: I like to play Ludo Result 1: []string{"Welcome,", " to the, online portal, of GeeksforGeeks"} Result 2: []string{"M", "y", " ", "dog name is Dollar"} Result 3: []string{"I like to play Ludo"} Result 4: []string{""} ``` ::: ## 字符类型 组成字符串的每个元素叫做“字符”,可以通过遍历或者单个获取字符串元素获得字符。 字符用单引号`'`包裹起来,如: ```go var a = '中' // rune var b = 'x' // byte ``` Go 语言的字符有以下两种: 1. `uint8`类型,或者叫 byte 型,代表了`ASCII码`的一个字符。 2. `rune`类型,代表一个 `UTF-8字符`。 当需要处理中文、日文或者其他复合字符时,则需要用到`rune`类型。`rune`类型实际是一个`int32`。 Go 使用了特殊的 rune 类型来处理 Unicode,让基于 Unicode 的文本处理更为方便,也可以使用 byte 型进行默认字符串处理,性能和扩展性都有照顾。 ::: {tabbed} INPUT ```go func traversalString() { s := "你好,李焕英!abc123" for i := 0; i < len(s); i++ { fmt.Printf("%T>>%v(%c)", s[i], s[i], s[i]) } fmt.Println() for _, r := range s { fmt.Printf("%T>>%v(%c)", r, r, r) } fmt.Println() } ``` ::: ::: {tabbed} OUTPUT ```go uint8>>228(ä)uint8>>189(½)uint8>>160( )uint8>>229(å)uint8>>165(¥)uint8>>189(½)ui nt8>>239(ï)uint8>>188(¼)uint8>>...... int32>>20320(你)int32>>22909(好)int32>>65292(,)int32>>26446(李)int32>>28949(焕) int32>>33521(英)int32>>65281(!)int32>>97(a)int32>>98(b)int32>>99(c)int32>>49(1) int32>>50(2)int32>>51(3) ``` ::: ```{WARNING} 因为UTF8编码下一个中文汉字由3~4个字节组成,所以我们不能简单的按照字节去遍历一个包含中文的字符串,否则就会出现上面输出中第一行的结果。 ``` **字符串底层是一个byte数组,所以可以和`[]byte`类型相互转换。字符串是不能修改的字符串是由byte字节组成,所以字符串的长度是byte字节的长度**。` rune`类型用来表示`UTF8`字符,一个`rune`字符由一个或多个`byte`组成。 ``` {admonition} 思考一下,rune 为什么是int32而不是uint16 Go 使用大量有符号值,不仅用于 runes,还用于数组索引、`Read/Write`字节计数等,这是因为`uint` 在任何语言中都会表现得令人困惑,除非你针对溢出进行算术保护(例如,如果`var a, b uint = 1, 2`,`a-b > 0`和`a-b > 1000000`: `int` 在日常生活中表现得更像数字,这是使用它们的一个令人信服的理由,并且没有同样令人信服的理由不使用它们。 ``` ### 练习 使用三种方式输出一个变量的地址 ::: {tabbed} INPUT ```go package main import ( "fmt" "unsafe" ) //使用三种方式输出一个变量的地址 func multiOutput() { var n byte = '1' // method 1 var point unsafe.Pointer = unsafe.Pointer(&n) //method 2 var p uintptr = uintptr(point) // method 3 var ptr *byte = &n fmt.Printf("%p\n", ptr) fmt.Printf("%x\n", p) fmt.Printf("%p\n", point) } func main() { multiOutput() } ``` ::: ::: {tabbed} OUTPUT ```go 0xc000014088 c000014088 0xc000014088 ``` ::: 输入一个uint32类型的数字,打印出对应的二进制表示格式 ::: {tabbed} INPUT ```go package main import ( "fmt" "math" "strings" ) //Given a number of type uint32, print its binary representation func BinaryFMT(num uint32) string { strB, caliper := strings.Builder{}, uint32(math.Pow(2, 31)) for i := 0; i < 32; i++ { if 0 == (num & caliper) { strB.WriteString("0") } else { strB.WriteString("1") } caliper = caliper >> 1 } fmt.Println(strB.String()) return strB.String() } func main() { BinaryFMT(12) } ``` ::: ::: {tabbed} OUTPUT ```go 00000000000000000000000000001100 ``` ::: 进阶用法 ```go package BinaryFormat import ( "fmt" "strings" ) //Prints a string of binary expressions for a number of type INT32 func BinaryFormat(n int32) string { // Consider the plus and minus signs unsignNum := uint32(n) strBuilder := strings.Builder{} caliper := (^uint32(0) >> 1) + 1 for caliper > 0 { if 0 == caliper&unsignNum { strBuilder.WriteString("0") } else { strBuilder.WriteString("1") } caliper >>= 1 } return strBuilder.String() } /* 注意:此函数输出的二进制表达形式为原码形式,实际的加减运算逻辑都是在补码的形势下进行计算的 */ ``` ### 修改字符串 要修改字符串,需要先将其转换成`[]rune`或`[]byte`,完成后再转换为`string`。无论哪种转换,都会重新分配内存,并复制字节数组。 ```go func changeString() { //修改uint8 s1 := "big" //string类型 byteS1 := []byte(s1) byteS1[0] = 'p' fmt.Printf("%s\n", string(byteS1)) //修改rune s2 := "白萝卜" runeS2 := []rune(s2) runeS2[0] = '红' fmt.Printf("%s\n", string(runeS2)) } ``` ## 自定义类型 - type signal uint8 - type ms map[string]string - type add func(a, b int) int - type user struct {name string; age int8} ```go type User struct { name string age int8 } ``` ## 强制类型转换 - byte和int类型可以相互转换 - float和int可以相互转换,但小数位会丢失 - bool和int不能相互转换 - 不同长度的int和float之间可以相互转换 - string可以转换为[]byte和[]rune类型,byte和rune也可以转换为字符串 - 低精度向高精度转换没问题,高精度像低精度转换会丢失位数 - 无符号像有符号转换,最高位就是了符号位