数据类型的基本介绍
Go中的每一种数据都定义了明确的数据类型,在内存中分配了不同大小的内存空间。具体来说,Go中的数据类型可分为基本数据类型和复杂数据类型。如下:
整数类型
Go中的整数类型如下:
类型 | 有无符号 | 占用存储空间 | 表数范围 |
---|---|---|---|
int8 | 有 | 1字节 | [ − 2 7 , 2 7 − 1 ] [-2^{7},2^{7}-1] [−27,27−1] |
int16 | 有 | 2字节 | [ − 2 15 , 2 15 − 1 ] [-2^{15},2^{15}-1] [−215,215−1] |
int32 | 有 | 4字节 | [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [−231,231−1] |
int64 | 有 | 8字节 | [ − 2 63 , 2 63 − 1 ] [-2^{63},2^{63}-1] [−263,263−1] |
uint8 | 无 | 1字节 | [ 0 , 2 8 − 1 ] [0,2^{8}-1] [0,28−1] |
uint16 | 无 | 2字节 | [ 0 , 2 16 − 1 ] [0,2^{16}-1] [0,216−1] |
uint32 | 无 | 4字节 | [ 0 , 2 32 − 1 ] [0,2^{32}-1] [0,232−1] |
uint64 | 无 | 8字节 | [ 0 , 2 64 − 1 ] [0,2^{64}-1] [0,264−1] |
int | 有 | 32位系统占用4字节,64位系统占用8字节 | [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [−231,231−1] 或 [ − 2 63 , 2 63 − 1 ] [-2^{63},2^{63}-1] [−263,263−1] |
uint | 无 | 32位系统占用4字节,64位系统占用8字节 | [ 0 , 2 32 − 1 ] [0,2^{32}-1] [0,232−1] 或 [ 0 , 2 64 − 1 ] [0,2^{64}-1] [0,264−1] |
rune | 有 | 等价于int32,占用4字节 | [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [−231,231−1] |
byte | 无 | 等价于uint8,占用1字节 | [ 0 , 2 8 − 1 ] [0,2^{8}-1] [0,28−1] |
使用案例如下:
package main import ( "fmt" "unsafe" ) func main() { // 整数类型 var num int16 = 1024 fmt.Printf("num value = %d\n", num) // num value = 1024 fmt.Printf("num type = %T\n", num) // num type = int16 fmt.Printf("num size = %d\n", unsafe.Sizeof(num)) // num size = 2 }
说明一下:
字符类型
Go中没有专门的字符类型,一般使用整数类型中的byte来存储单个字符。如下:
package main import ( "fmt" "unsafe" ) func main() { // 字符类型 var ch1 byte = 'a' fmt.Printf("ch1 value = %c\n", ch1) // ch1 value = a fmt.Printf("ch1 type = %T\n", ch1) // ch1 type = uint8 fmt.Printf("ch1 size = %d\n", unsafe.Sizeof(ch1)) // ch1 size = 1 var ch2 rune = '龙' fmt.Printf("ch2 value = %c\n", ch2) // ch2 value = 龙 fmt.Printf("ch2 type = %T\n", ch2) // ch2 type = int32 fmt.Printf("ch2 size = %d\n", unsafe.Sizeof(ch2)) // ch2 size = 4 }
说明一下:
浮点数类型
Go中的浮点数类型如下:
类型 | 占用存储空间 | 表数范围 |
---|---|---|
float32 | 4字节 | [ − 3.403 E 38 , 3.403 E 38 ] [-3.403E38,3.403E38] [−3.403E38,3.403E38] |
float64 | 8字节 | [ − 1.798 E 308 , 1.798 E 308 ] [-1.798E308,1.798E308] [−1.798E308,1.798E308] |
使用案例如下:
package main import ( "fmt" "unsafe" ) func main() { // 浮点数类型 var num float32 = 3.1415926 fmt.Printf("num value = %.2f\n", num) // num value = 3.14 fmt.Printf("num type = %T\n", num) // num type = float32 fmt.Printf("num size = %d\n", unsafe.Sizeof(num)) // num size = 4 }
说明一下:
浮点数常量的表示形式
Go中浮点数常量有两种表示形式,分别是十进制表示形式和科学计数法表示形式。如下:
// 十进制表示形式 var num1 = 3.1415926 var num2 = .1415926 fmt.Printf("num1 = %.2f\n", num1) // num1 = 3.14 fmt.Printf("num2 = %.2f\n", num2) // num2 = 0.14 // 科学计数法表示形式 var num3 = 3.1415926e2 var num4 = 3.1415926e-2 fmt.Printf("num3 = %f\n", num3) // num3 = 314.159260 fmt.Printf("num4 = %f\n", num4) // num4 = 0.031416
说明一下:
复数类型
Go中的复数类型如下:
类型 | 占用存储空间 | 表数范围 |
---|---|---|
complex64 | 8字节 | 实部和虚部的范围与float32类型相同 |
complex128 | 16字节 | 实部和虚部的范围与float64类型相同 |
使用案例如下:
package main import ( "fmt" "unsafe" ) func main() { // 复数类型 var z complex64 = 1 + 2i fmt.Printf("z value = %v\n", z) // z = (1+2i) fmt.Printf("z type = %T\n", z) // z type = complex64 fmt.Printf("z size = %d\n", unsafe.Sizeof(z)) // z size = 8 fmt.Printf("z = %f\n", real(z)) // z的实部 = 1.000000 fmt.Printf("z = %f\n", imag(z)) // z的虚部 = 2.000000 }
说明一下:
布尔类型
Go中的布尔类型取值为true或false,占用1字节大小。如下:
package main import ( "fmt" "unsafe" ) func main() { // 布尔类型 var flag bool = true fmt.Printf("flag value = %t\n", flag) // flag value = true fmt.Printf("flag type = %T\n", flag) // flag type = bool fmt.Printf("flag size = %d\n", unsafe.Sizeof(flag)) // flag size = 1 }
说明一下:
string类型
字符串就是一串固定长度的字符连接起来的字符序列,Go中的string是由单个字节连接起来的。string类型底层包含两个字段,一个字段是字符串指针,该指针指向对应的字符串,另一个字段记录着字符串的长度。如下:
使用案例如下:
package main import ( "fmt" "unsafe" ) func main() { // string类型 var s string = "Hello World" fmt.Printf("s value = %s\n", s) // s value = Hello World fmt.Printf("s type = %T\n", s) // s type = string fmt.Printf("s size = %d\n", unsafe.Sizeof(s)) // s size = 16 }
说明一下:
string元素的访问
通过变量名[下标]的方式能够访问string中指定下标的元素。如下:
package main import ( "fmt" ) func main() { var s string = "Hello World" for i := 0; i < len(s); i++ { fmt.Printf("s[%d]: %c\n", i, s[i]) // 访问string的第i个元素 } }
说明一下:
需要注意的是,Go中的string是由单个字节连接起来的,string中元素的类型为byte。如果string中含有码值超过255的Unicode字符,那么在以byte为单位访问string元素时就会出现乱码,因为在UTF-8中一个汉字对应3个字节。
rune作为Go中表示Unicode字符的类型,其大小为4字节,因此可以将string转换为rune切片类型,转换后再通过变量名[下标]的方式访问rune切片中的元素,这样就能解决乱码问题。如下:
package main import ( "fmt" ) func main() { var s string = "Hello 世界" tmp := []rune(s) for i := 0; i < len(tmp); i++ { fmt.Printf("tmp[%d]: %c\n", i, tmp[i]) // 访问rune的第i个元素 } }
字符串的表示形式
Go中有如下两种方式来表示字符串:
使用案例如下:
package main import ( "fmt" ) func main() { var s1 string = "hello\nworld" // 双引号的方式表示字符串 fmt.Printf("s1 = %s\n", s1) var s2 string = `hello\nworld` // 反引号的方式表示字符串 fmt.Printf("s2 = %s\n", s2) }
运行代码后可以看到,用双引号方式表示的字符串中的\n被识别为换行符输出,而用反引号方式表示的字符串中的\n被当作普通字符输出。如下:
字符串拼接
Go中的字符串可以通过+进行拼接。如下:
package main import ( "fmt" ) func main() { // 字符串拼接 var s string = "Hello" + "World" fmt.Printf("s = %s\n", s) // s = HelloWorld }
常量
Go中通过const关键字声明常量,在声明常量时必须给常量一个初始值,多个常量可以同时声明,常量声明后不能修改。如下:
package main import ( "fmt" ) func main() { const s1 string = "Hello" // 声明常量 const s2 = "World" // 声明常量时省略类型 const s3, s4 string = "Hello", "Golang" // 声明多个同类型常量 const year, name = 2021, "dragon" // 声明多个不同类型常量 fmt.Println(s1, s2) // Hello World fmt.Println(s3, s4) // Hello Golang fmt.Println(year, name) // 2021 dragon }
需要注意的是,只有数值类型、string类型和bool类型可以声明为常量,声明常量时可以省略常量的数据类型,这时将会声明为对应的默认类型。如下:
package main import ( "fmt" ) func main() { const num1 = 10 const num2 = 3.14 const num3 = 1 + 2i const ch = 'a' const flag = true const s = "dragon" fmt.Printf("num1 type = %T\n", num1) // num1 type = int fmt.Printf("num2 type = %T\n", num2) // num2 type = float64 fmt.Printf("num3 type = %T\n", num3) // num3 type = complex128 fmt.Printf("ch type = %T\n", ch) // ch type = int32 fmt.Printf("flag type = %T\n", flag) // flag type = bool fmt.Printf("s type = %T\n", s) // s type = string }
说明一下:
批量声明常量
可以通过下面这种方式批量声明常量,这时除了第一个常量以外,其他常量右边的初始化表达式都可以省略,省略初始化表达式的常量将使用前面常量的初始化表达式进行初始化。如下:
package main import ( "fmt" ) func main() { const ( a = 1 b c = 2 d ) fmt.Printf("a = %d\n", a) // a = 1 fmt.Printf("b = %d\n", b) // b = 1 fmt.Printf("c = %d\n", c) // c = 2 fmt.Printf("d = %d\n", d) // d = 2 }
批量声明常量时可以使用iota常量生成器进行初始化,初始化时iota将会在第一个常量所在行被置为0,然后在下面每一行依次加一。如下:
package main import ( "fmt" ) func main() { const ( a = iota b c d ) fmt.Printf("a = %d\n", a) // a = 0 fmt.Printf("b = %d\n", b) // b = 1 fmt.Printf("c = %d\n", c) // c = 2 fmt.Printf("d = %d\n", d) // d = 3 }
我们也可以在复杂的常量表达式中使用iota常量生成器,例如下面让每一个常量都是前一个常量的2倍。如下:
package main import ( "fmt" ) func main() { const ( a = 1 << iota b c d ) fmt.Printf("a = %d\n", a) // a = 1 fmt.Printf("b = %d\n", b) // b = 2 fmt.Printf("c = %d\n", c) // c = 4 fmt.Printf("d = %d\n", d) // d = 8 }
类型转换
Go中的数据类型不会自动转换,不同类型变量之间赋值或参与运算都需要显式进行类型转换,否则会产生报错。如下:
package main import "fmt" func main() { var num1 int = 10 //var num2 float64 = num1 // error:类型不同,无法赋值 var num2 float64 = 1.2 // var ret = num1 * num2 // error:类型不同,无法参与运算 fmt.Printf("num1 = %d, num2 = %.2f\n", num1, num2) // num1 = 10, num2 = 1.20 }
说明一下:
基本数据类型相互转换
Go中通过T(v)的方式将v转换为T类型,其中v是需要转换的变量,T是需要转换为的类型。如下:
package main import "fmt" func main() { // 基本数据类型相互转换 var num int = 10 var num1 float64 = float64(num) var num2 int8 = int8(num) var num3 uint16 = uint16(num) fmt.Printf("num1 = %.2f, num2 = %d, num3 = %d\n", num1, num2, num3) }
说明一下:
基本数据类型转string
要将基本数据类型转换为string类型,可以借助fmt包中的Sprintf函数,该函数的使用方式与Printf函数一样,只不过Sprintf是将格式化后的字符串作为返回值返回。如下:
package main import "fmt" func main() { // 基本数据类型转string var i1 int = 10 var i2 float32 = 3.14 var i3 bool = true var i4 uint = 20 var s string = fmt.Sprintf("%d %.2f %t %d", i1, i2, i3, i4) fmt.Printf("s = %q\n", s) // s = "10 3.14 true 20" }
说明一下:
除此之外,通过strconv包中的函数,也可以将基本数据类型转换为string类型:
使用案例如下:
package main import ( "fmt" "strconv" ) func main() { // 基本数据类型转string var i1 int = 10 var i2 float32 = 3.14 var i3 bool = true var i4 uint = 20 var s1 string = strconv.FormatInt(int64(i1), 10) var s2 string = strconv.FormatFloat(float64(i2), 'f', -1, 32) var s3 string = strconv.FormatBool(i3) var s4 string = strconv.FormatUint(uint64(i4), 10) fmt.Printf("s1 = %q\n", s1) // s1 = "10" fmt.Printf("s2 = %q\n", s2) // s2 = "3.14" fmt.Printf("s3 = %q\n", s3) // s3 = "true" fmt.Printf("s4 = %q\n", s4) // s4 = "20" }
说明一下:
string转基本数据类型
通过strconv包中的函数,也可以将string类型转换为基本数据类型:
使用案例如下:
package main import ( "fmt" "strconv" ) func main() { // string转基本数据类型 var s1 string = "10" var s2 string = "3.14" var s3 string = "true" var s4 string = "20" i1, err1 := strconv.ParseInt(s1, 10, 0) i2, err2 := strconv.ParseFloat(s2, 32) i3, err3 := strconv.ParseBool(s3) i4, err4 := strconv.ParseUint(s4, 10, 0) fmt.Printf("i1: value = %d, type = %T, err = %v\n", i1, i1, err1) //i1: value = 10, type = int64, err =fmt.Printf("i2: value = %.2f, type = %T, err = %v\n", i2, i2, err2) //i2: value = 3.14, type = float64, err = fmt.Printf("i3: value = %t, type = %T, err = %v\n", i3, i3, err3) // i3: value = true, type = bool, err = fmt.Printf("i4: value = %d, type = %T, err = %v\n", i4, i4, err4) // i4: value = 20, type = uint64, err = }
说明一下:
int与string类型相互转换
strconv包中提供了专门用于int与string类型相互转换的函数:
使用案例如下:
package main import ( "fmt" "strconv" ) func main() { // int转string var num1 int = 10 s1 := strconv.Itoa(num1) fmt.Printf("s1 value = %q, s1 type = %T\n", s1, s1) // s1 value = "10", s1 type = string // string转int var s2 string = "20" num2, err := strconv.Atoi(s2) fmt.Printf("num2 value = %d, num2 type = %T, err = %v\n", num2, num2, err) // num2 value = 20, num2 type = int, err =}
说明一下:
指针类型
在类型的前面加上*就是对应类型的指针类型,通过取地址运算符&可以获取变量的地址,通过解引用运算符*可以获取指针指向变量的值。如下:
使用案例如下:
package main import ( "fmt" "unsafe" ) func main() { // 指针类型 var num int = 10 var ptr *int = &num *ptr = 20 fmt.Printf("num = %d\n", num) // num = 20 fmt.Printf("address = %p\n", ptr) // address = 0xc00000e0b8 fmt.Printf("ptr size = %d\n", unsafe.Sizeof(ptr)) // ptr size = 8 }
说明一下:
值类型和引用类型
Go中的数据类型可以分为值类型和引用类型:
string是值类型
Go中的string类型虽然在赋值和传递时赋值的是引用,但string类型并没有被归为引用类型。原因如下: