前言:在 Go 编程语言中,数据类型用于声明函数和变量。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
一、数据类型
有符号整数采用2的补码形式表示,也就是最高bit位用来表示符号位,一个n-bit的有符号数的值域是从-2{n-1}−2n−1到2{n-1}-12n−1−1。无符号整数的所有bit位都用于表示非负数,值域是0到2^n-12n−1。例如,int8类型整数的值域是从-128到127,而uint8类型整数的值域是从0到255。
1.1 整型
1.2 浮点型
Go语言提供了两种精度的浮点数,float32和float64。它们的算术规范由IEEE754浮点数国际标准定义,该浮点数规范被所有现代的CPU支持。
1.3 其他数字类型
二、布尔型
一个布尔类型的值只有两种:true和false。if和for语句的条件部分都是布尔类型的值,并且==和<等比较操作也会产生布尔型的值。
三、字符串
字符串是一种值类型,且值不可变,即创建某个文本后你无法再次修改这个文本的内容;更深入地讲,字符串是字节的定长数组。
Go 支持以下 2 种形式的字面值:
解释字符串:该类字符串使用双引号括起来,其中的相关的转义字符将被替换
\a 响铃
\b 退格
\f 换页
\n 换行
\r 回车
\t 制表符
\v 垂直制表符
\' 单引号 (只用在 '\'' 形式的rune符号面值中)
\" 双引号 (只用在 "..." 形式的字符串面值中)
\\ 反斜杠
非解释字符串:该类字符串使用反引号括起来
`This is a raw string \n` 中的 `\n` 会被原样输出
三、派生类型
包括:
-(a)指针类型(Pointer)
-(b)数组类型
-(c)结构化类型(struct 结构体)
-(d)Channel 类型
-(e)函数类型
-(f)切片类型
-(g)接口类型(interface)
-(h)Map 类型
Go语言变量
变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。变量可以通过变量名访问。
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字:
var identifier type
可以一次声明多个变量:
var identifier1, identifier2 type
变量声明
第一种
指定变量类型,如果没有初始化,则变量默认为零值。零值就是变量没有做初始化时系统默认设置的值。
var v_name v_type
v_name = value
零值
- 数值类型(包括complex64/128)为 0
- 布尔类型为 false
- 字符串为 “”(空字符串)
- 以下几种类型为 nil:
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口
第二种
根据值自行判定变量类型。
var v_name = value
第三种
省略 var, 注意 := 左侧如果没有声明新的变量,就产生编译错误
v_name := value
多变量声明
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
整形变量
var v1 int = 10 // 正确的使用方式1
var v2 = 10 // 正确的使用方式2,编译器可以自动推导出v2的类型
v3 := 10 // 正确的使用方式3,编译器可以自动推导出v3的类型
浮点型变量
func main() {
// 如果浮点数声明时未指定变量的类型
// 默认为 float64 类型
var n1 = 3.99
fmt.Printf("n1的值为 %g, 类型是 %T, 占 %d 个字节\n", n1, n1, unsafe.Sizeof(n1)) // n1的值为 3.99, 类型是 float64, 占 8 个字节
// 使用类型推导
n2 := 4.567
fmt.Printf("n2的值为 %g, 类型是 %T, 占 %d 个字节\n", n2, n2, unsafe.Sizeof(n2)) // n2的值为 4.567, 类型是 float64, 占 8 个字节
// 声明为 float32 类型
var n3 float32 = 1.6789
fmt.Printf("n3的值为 %g, 类型是 %T, 占 %d 个字节\n", n3, n3, unsafe.Sizeof(n3)) // n3的值为 1.6789, 类型是 float32, 占 4 个字节
// 在用Printf进行格式化输出时,最好用 %g或%G 进行
// 格式化输出,如果用 %f 的话可能会造成精度丢失导
// 致数据不准确
var f1 float32 = 1.2098
fmt.Println("f1 =", f1) // f1 = 1.2098
fmt.Printf("f1 = %f\n", f1) // f1 = 1.209800
fmt.Printf("f1 = %g\n", f1) // f1 = 1.2098
fmt.Printf("f1 = %G\n", f1) // f1 = 1.2098
fmt.Printf("f1 = %e\n", f1) // f1 = 1.209800e+00
fmt.Printf("f1 = %E\n", f1) // f1 = 1.209800E+00
// 负的浮点型
f2 := -35412378.431267890112
fmt.Printf("f2 = %f, %g, %G, %e, %E\n", f2, f2, f2, f2, f2) // f2 = -35412378.431268, -3.541237843126789e+07, -3.541237843126789E+07, -3.541238e+07, -3.541238E+07
// 使用float32类型时,有时尾数部分可能丢失,造成精度损失
// 所以在开发中最好使用float64类型来声明浮点型变量
var f3 float32 = 444.000068889955
var f4 float64 = 444.000068889955
fmt.Println("f3 =", f3, "f4 =", f4) // f3 = 444.00006 f4 = 444.000068889955
fmt.Printf("f3 = %f, %g, %G, %e, %E\n", f3, f3, f3, f3, f3) // f3 = 444.000061, 444.00006, 444.00006, 4.440001e+02, 4.440001E+02
fmt.Printf("f4 = %f, %g, %G, %e, %E\n", f4, f4, f4, f4, f4) // f4 = 444.000069, 444.000068889955, 444.000068889955, 4.440001e+02, 4.440001E+02
}
// 科学计数法形式
f5 := 5.1234e2 // 5.1234 * 10的2次方
f6 := 5.1234E2 // 5.1234 * 10的2次方
f7 := 5.1234E-2 // 5.1234 * 10的-2次方
布尔型变量
布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换。只能是true,false,或者是表达式
var b bool
b = true
var b = true
b := true
b := b > 2
字符串变量
// 方式1
var str string // 声明一个字符串变量
// 方式2
var str = "Hello,世界"
// 方式3
str := "Hello,世界"
派生类型
指针类型
我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。
一个指针变量指向了一个值的内存地址。类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
var var_name *var-type
var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
指针使用流程:
- 定义指针变量。
- 为指针变量赋值。
- 访问指针变量中指向地址的值。
在指针类型前面加上* 号(前缀)
来获取指针所指向的内容。
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针。nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。一个指针变量通常缩写为 ptr。
package main
import "fmt"
func main() {
var ptr *int
fmt.Printf("ptr 的值为 : %x\n", ptr )
}
空指针判断:
if(ptr != nil) /* ptr 不是空指针 */
if(ptr == nil) /* ptr 是空指针 */
数组类型
声明数组
数组就是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素( element),一个数组包含的元素个数被称为数组的长度。
Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
var variable_name [SIZE] variable_type
以上为一维数组的定义方式。例如以下定义了数组 balance 长度为 10 类型为 float32:
var balance [10] float32
初始化数组
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果数组长度不确定,可以使用 ...
代替数组的长度,编译器会根据元素个数自行推断数组的长度:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
// 将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
访问数组元素
数组元素可以通过索引(位置)来读取。格式为数组名后加中括号,中括号中为索引的值。例如:
var salary float32 = balance[9]
以下演示了数组完整操作(声明、赋值、访问)的实例:
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i,j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j] )
}
}
多维数组
Go 语言支持多维数组,以下为常用的多维数组声明方式:
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
多维数组可通过大括号来初始值。以下实例为一个 3 行 4 列的二维数组:
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}, /* 第三行索引为 2 */
}
数组获取长度
数组的长度是该数组类型的一个内置常量,可以用Go语言的内置函数len()
来获取。下面是一个获取数组arr元素个数的写法:arrLength := len(arr)
数组的注意事项
- 数组的地址可以通过数组名来获取 &array
- 第一个元素的地址,就是数组的首地址
- 数组的各个元素的地址间隔是依据数组的类型决定的
数组长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度
- 数组是值传递类型,如果在其他函数中,去修改原来的数组,可以使用引用传递
切片类型
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
创建数组切片
创建数组切片的方法主要有两种——基于数组和直接创建,下面我们来简要介绍一下这两种方法。
- 基于数组
// 先定义一个数组
var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 基于数组创建一个数组切片
var mySlice []int = myArray[:5]
Go语言支持用myArray[first:last]
这样的方式来基于数组生成一个数组切片
// 基于myArray的所有元素创建数组切片:
mySlice = myArray[:]
// 基于myArray的前5个元素创建数组切片:
mySlice = myArray[:5]
// 基于从第5个元素开始的所有元素创建数组切片:
mySlice = myArray[5:]
- 直接创建
Go语言提供的内置函数make()可以用于灵活地创建数组切片。
// 创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
// 创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
// 直接创建并初始化包含5个元素的数组切片:
mySlice3 := []int{1, 2, 3, 4, 5}
// 当然,事实上还会有一个匿名数组被创建出来,只是不需要我们来操心而已。
- 基于数组切片创建数组切片
oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:3] // 基于oldSlice的前3个元素构建新数组切片
元素遍历
操作数组元素的所有方法都适用于数组切片,比如数组切片也可以按下标读写元素,用len()函数获取元素个数,并支持使用range关键字来快速遍历所有元素。
动态增减元素
数组切片支持Go语言内置的cap()
函数和len()
函数, cap()函数返回的是数组切片分配的空间大小,而len()函数返回的是数组切片中当前所存储的元素个数。
mySlice := make([]int, 5, 10)
fmt.Println("len(mySlice):", len(mySlice))
fmt.Println("cap(mySlice):", cap(mySlice))
如果需要后面继续新增元素,可以使用append()函数。函数append()的第二个参数其实是一个不定参数,我们可以按自己需求添加若干个元素,甚至直接将一个数组切片追加到另一个数组切片的末尾
//从尾端给mySlice加上3个元素
mySlice = append(mySlice, 1, 2, 3)
// 给mySlice后面添加另一个数组切片,需要加入...
mySlice = append(mySlice, mySlice2...)
内容复制
数组切片支持Go语言的另一个内置函数copy(),用于将内容从一个数组切片复制到另一个数组切片。如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
删除元素
/// 删除下表为 i 的元素
sli = append(sli[:i] , sli[i+1:]...)
/// 删除头元素
sli = sli[1:]
/// 删除尾元素
sli = sli[:len(sli) -1]
切片截取
可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound],实例如下:
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int,0,5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
Map
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
定义 Map
可以使用内建函数 make 也可以使用 map 关键字来定义 Map:
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type
/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)
如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对
下面实例演示了创建和使用map:
package main
import "fmt"
func main() {
var countryCapitalMap map[string]string /*创建集合 */
countryCapitalMap = make(map[string]string)
/* map插入key - value对,各个国家对应的首都 */
countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "罗马"
countryCapitalMap [ "Japan" ] = "东京"
countryCapitalMap [ "India " ] = "新德里"
/*使用键输出地图值 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [country])
}
/*查看元素在集合中是否存在 */
capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */
/*fmt.Println(capital) */
/*fmt.Println(ok) */
if (ok) {
fmt.Println("American 的首都是", capital)
} else {
fmt.Println("American 的首都不存在")
}
}
delete() 函数
delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下:
package main
import "fmt"
func main() {
/* 创建map */
countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
fmt.Println("原始地图")
/* 打印地图 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [ country ])
}
/*删除元素*/ delete(countryCapitalMap, "France")
fmt.Println("法国条目被删除")
fmt.Println("删除元素后地图")
/*打印地图*/
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [ country ])
}
}
map排序
- map是无序的
- 若要得到排序的map,可以先对key进行排序,然后根据keyzhi遍历输出即可
以下数据类型在相应博文介绍
- 结构化类型(struct 结构体)
- Channel 类型
- 函数类型
- 接口类型(interface)