4. Golang的基础数据类型

我们将从最基本的类型开始,Go语言的基本类型部分跟C语言很类似,熟习C语言的朋友们应该不会陌生

4.1. 整型

整型分为以下两个大类:

  • 按长度分为:int8int16int32int64

  • 对应的无符号整型:uint8uint16uint32uint64

其中,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 字节指针值的无符号整型

重要

在使用int和 uint类型时,不能假定它是32位或64位的整型,而是考虑int和uint可能在不同平台上的差异。

获取对象的长度的内建len()函数返回的长度可以根据不同平台的字节长度进行变化。实际使用中,切片或 map 的元素数量等都可以用int来表示。在涉及到二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用int uint

4.1.1. 数字字面量语法(Number literals syntax)

Go1.13版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制浮点数的格式定义数字,例如:

  • v := 0b00101101, 代表二进制的 101101,相当于十进制的 45

  • v := 0o377,代表八进制的 377,相当于十进制的 255

  • v := 0x1p-2,代表十六进制的 1 除以 2²,也就是 0.25。

  • 而且还允许我们用 _ 来分隔数字,比如说: v := 123_456 表示 v 的值等于 123456。

我们可以借助fmt函数来将一个整数以不同进制形式展示。

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
}
10 
1010 
77 
ff 
FF 

4.1.2. 思考,数值溢出

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)

}

4.2. 浮点型

Go语言支持两种浮点型数:float32float64

这两种浮点型数据格式遵循IEEE 754标准:

  • float32 的浮点数的最大范围约为 3.4e38,可以使用常量定义:math.MaxFloat32

  • float64 的浮点数的最大范围约为 1.8e308,可以使用一个常量定义:math.MaxFloat64

类型名

字节宽度

取值范围

float32

4字节

IEEE-754 32位浮点型数

float64

8字节

IEEE-754 64位浮点型数

打印浮点数时,可以使用fmt包配合动词%f,代码如下:

package main
import (
        "fmt"
        "math"
)
func main() {
        fmt.Printf("%f\n", math.Pi)
        fmt.Printf("%.2f\n", math.Pi)
}
3.141593
3.14

4.3. 复数

类型名

字节宽度

取值范围

complex64

4字节

32 位实数和虚数

complex128

8字节

64 位实数和虚数

package main
import "fmt"
func main() {
       var c1 complex64
        c1 = 1 + 2i
        var c2 complex128
        c2 = 2 + 3i
        fmt.Println(c1)
        fmt.Println(c2)
}
(1+2i)
(2+3i)

小技巧

复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。

4.4. 布尔型

Go语言中以bool类型进行声明布尔型数据,布尔型数据只有true(真)false(假)两个值。

注意:

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

  2. Go 语言中不允许将整型强制转换为布尔型.

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

b1 := true
var b2 bool // 默认为false
fmt.Printf("b1 >>> %T\nb2 >>> %T\nb2 value: %v\n", b1, b2, b2)

4.5. 字符串

Go语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64 等)一样。 Go 语言里的字符串的内部实现使用UTF-8编码。 字符串的值为双引号(“)中的内容,可以在Go语言的源码中直接添加非ASCII码字符,例如:

s1 := "hello"
s2 := "你好"

4.5.1. 字符串转义符

Go 语言的字符串常见转义符包含回车、换行、单双引号、制表符等,如下表所示。

转义符

含义

\r

回车符(返回行首)

\n

换行符(直接跳到下一行的同列位置)

\t

制表符

\'

单引号

\"

双引号

\\

反斜杠

举个例子,我们要打印一个Windows平台下的一个文件路径:

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)
}
str1: "c:\Code\lesson1\go.exe"
str2: \"c:\\Code\\lesson1\\go.exe\"

小技巧

反引号```` 内定义的字符串输出的时候不转义

4.5.2. 多行字符串

Go语言中要定义一个多行字符串时,就必须使用反引号字符:

package main

import "fmt"

func main() {
	s2 := `
第一行
第二行
第三行
`
	fmt.Printf("多行字符串如下:%s", s2)
}
多行字符串如下:
第一行
第二行
第三行

反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。

4.5.3. 字符串的常用操作

方法

介绍

len(str)

求长度

+或fmt.Sprintf

拼接字符串

strings.Split

分割

strings.contains

判断是否包含

strings.HasPrefix,strings.HasSuffix

前缀/后缀判断

strings.Index(),strings.LastIndex()

子串出现的位置

strings.Join(a[]string, sep string)

join操作

4.5.4. 计算字符串长度

比较两个字符串长度并返回最大值与最小值

//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		
}

4.5.5. 字符串拼接

4.5.5.1. 加号连接 +

//Concatenate strings by "+"
func concateStrings(strArgs ...string) string {
	ret := ""
	for _, str := range strArgs {
		ret += str
	}
	return ret
}
//Concatenate strings via Sprintf
func concateStrings2(strArgs ...string) string {
	ret := ""
	for _, str := range strArgs {
		ret = fmt.Sprintf("%s%s",ret,str)
	}
	return ret
}

4.5.5.2. strings.Join

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

分隔符

字符串类型的参数

  • 返回值

​ 返回一个字符串

//Concatenate strings with join
func concateStrings3(strArgs ...string) string {
	return strings.Join(strArgs, "")
}

4.5.5.3. strings.Builder

//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()
}

4.5.6. 字符串切割

4.5.6.1. Split

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

分隔符

字符串类型的参数

  • 返回值

​ 返回一个字符串切片。

// 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)

}
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{""}

4.5.6.2. SplitAfter

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

分隔符

字符串类型的参数

  • 返回值

​ 返回一个字符串切片。

// 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)

}
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{""}

4.5.6.3. SplitAfterN

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.

小技巧

str是字符串,sep是分隔符,m 用于查找要返回的子字符串数。

  • 以str为分隔符,将s切分成多个子串,结果中包含str本身。

  • 如果str为空,则将s切分成Unicode字符列表。如果s 中没有str子串,则将整个s作为 []string 的第一个元素返回。

  • 参数n表示最多切分出几个子串,超出的部分将不再切分。

  • 如果n为0,则返回 nil;如果 n 小于 0,则不限制切分个数,全部切分

  • 参数说明

参数

说明

备注

str

待分割的字符串

字符串类型的参数

sep

分隔符

字符串类型的参数

m

分割次数

int类型的参数

  • 返回值

​ 返回一个字符串切片。

// 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)
}
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{""}

4.6. 字符类型

组成字符串的每个元素叫做“字符”,可以通过遍历或者单个获取字符串元素获得字符。 字符用单引号'包裹起来,如:

var a = '中'  // rune
var b = 'x'  // byte

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

  1. uint8类型,或者叫 byte 型,代表了ASCII码的一个字符。

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

当需要处理中文、日文或者其他复合字符时,则需要用到rune类型。rune类型实际是一个int32

Go 使用了特殊的 rune 类型来处理 Unicode,让基于 Unicode 的文本处理更为方便,也可以使用 byte 型进行默认字符串处理,性能和扩展性都有照顾。

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()
}
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)

警告

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

字符串底层是一个byte数组,所以可以和[]byte类型相互转换。字符串是不能修改的字符串是由byte字节组成,所以字符串的长度是byte字节的长度 rune类型用来表示UTF8字符,一个rune字符由一个或多个byte组成。

思考一下,rune 为什么是int32而不是uint16

Go 使用大量有符号值,不仅用于 runes,还用于数组索引、Read/Write字节计数等,这是因为uint 在任何语言中都会表现得令人困惑,除非你针对溢出进行算术保护(例如,如果var a, b uint = 1, 2a-b > 0a-b > 1000000

int 在日常生活中表现得更像数字,这是使用它们的一个令人信服的理由,并且没有同样令人信服的理由不使用它们。

4.6.1. 练习

使用三种方式输出一个变量的地址

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()
}
0xc000014088
c000014088
0xc000014088

输入一个uint32类型的数字,打印出对应的二进制表示格式

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)
}
00000000000000000000000000001100

进阶用法

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()
}

/*
	注意:此函数输出的二进制表达形式为原码形式,实际的加减运算逻辑都是在补码的形势下进行计算的
*/

4.6.2. 修改字符串

要修改字符串,需要先将其转换成[]rune[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。

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))
}

4.7. 自定义类型

  • type signal uint8

  • type ms map[string]string

  • type add func(a, b int) int

  • type user struct {name string; age int8}

type User struct {
    name string
    age int8
}

4.8. 强制类型转换

  • byte和int类型可以相互转换

  • float和int可以相互转换,但小数位会丢失

  • bool和int不能相互转换

  • 不同长度的int和float之间可以相互转换

  • string可以转换为[]byte和[]rune类型,byte和rune也可以转换为字符串

  • 低精度向高精度转换没问题,高精度像低精度转换会丢失位数

  • 无符号像有符号转换,最高位就是了符号位