Go基础数据类型

Posted Ricky_0528

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go基础数据类型相关的知识,希望对你有一定的参考价值。

文章目录

1. 数据类型

1.1 基本数据类型

类型长度(字节)默认值说明
bool1false
byte10uint8,取值范围[0,255]
rune40Unicode Code Point, int32
int, uint4或8032 或 64 位,取决于操作系统
int8, uint810-128 ~ 127, 0 ~ 255
int16, uint1620-32768 ~ 32767, 0 ~ 65535
int32, uint3240-21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名
int64, uint6480
float3240.0
float6480.0
complex6480
complex128160
uintptr4或80以存储指针的 uint32 或 uint64 整数
fmt.Printf("os arch %s, int size %d\\n", runtime.GOARCH, strconv.IntSize) // int是4字节还是8字节,取决于操作系统是32位还是64位
var a int = 5
var b int8 = 5
var c int16 = 5
var d int32 = 5
var e int64 = 5
var f uint = 5
var g uint8 = 5
var h uint16 = 5
var i uint32 = 5
var j uint64 = 5
fmt.Printf("a = %d, b = %d, c = %d, d = %d, e = %d, f = %d, g = %d, h = %d, i = %d, j=%d\\n", a, b, c, d, e, f, g, h, i, j)
var k float32 = 5
var l float64 = 5
fmt.Printf("k = %f, l = %.2f\\n", k, l) // %.2f保留2位小数
var m complex128 = complex(4, 7)
var n complex64 = complex(4, 7)
fmt.Printf("type of m is %T, type of n is %T\\n", m, n) // %T输出变量类型
fmt.Printf("m=%v, n=%v\\n", m, n) // 按值的本来值输出
fmt.Printf("m=%+v, n=%+v\\n", m, n) // 在 %v 基础上,对结构体字段名和值进行展开
fmt.Printf("m=%#v, n=%#v\\n", m, n) // 输出 Go 语言语法格式的值
fmt.Printf("m的实部%f, m的虚部%f\\n", real(m), imag(m))
fmt.Printf("m的实部%e, m的虚部%g\\n", real(m), imag(m)) // %e科学计数法,%g根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
o := true // 等价于var o bool = true
fmt.Printf("o=%t\\n", o) // %t布尔变量
var pointer unsafe.Pointer = unsafe.Pointer(&a)
var p uintptr = uintptr(pointer)
var ptr *int = &a
fmt.Printf("p=%x pointer=%p ptr=%p\\n", p, pointer, ptr) // %p输出地址,%x十六进制
var q byte = 100 // byte是uint,取值范围[0,255]
fmt.Printf("q=%d, binary of q is %b\\n", q, q) // %b输出二进制
var r rune = '☻' // rune实际上是int32,即可以表示2147483647种字符,包括所有汉字和各种特殊符号
fmt.Printf("r=%d, r=%U\\n", r, r) // %U Unicode 字符
var s string = "I'm Ricky"
fmt.Printf("s=%s\\n", s)
var t error = errors.New("my error")
fmt.Printf("error is %v\\n", t)
fmt.Printf("error is %+v\\n", t) // 在 %v 基础上,对结构体字段名和值进行展开
fmt.Printf("error is %#v\\n", t) // 输出 Go 语言语法格式的值

数值型变量的默认值是0,字符串的默认值是空字符串,布尔型变量的默认值是false,引用类型、函数、指针、接口的默认值是nil;数组的默认值取每个元素对应类型的默认值,结构体的默认值取每个成员变量对应类型的默认值

var a int
var b byte
var f float32
var t bool
var s string
var r rune
var arr [3]int
var slc []int

fmt.Printf("default value of int %d\\n", a)
fmt.Printf("default value of byte %d\\n", b)
fmt.Printf("default value of float %.2f\\n", f)
fmt.Printf("default value of bool %t\\n", t)
fmt.Printf("default value of string [%s]\\n", s)
fmt.Printf("default value of rune %d, [%c]\\n", r, r)
fmt.Printf("default int array is %v\\n", arr) //取每个元素对应类型的默认值
fmt.Printf("default slice is nil %t\\n", slc == nil)

输出:

default value of int 0
default value of byte 0
default value of float 0.00
default value of bool false
default value of string []
default value of rune 0, []
default int array is [0 0 0]
default slice is nil true

1.2 复合数据类型

类型默认值说明
array取每个元素对应类型的默认值值类型
struct取每个成员变量对应类型的默认值值类型
string“”UTF-8 字符串
slicenil引用类型
mapnil引用类型
channelnil引用类型
interfacenil接口
functionnil函数

1.3 自定义数据类型

类型别名

type byte = uint8
type rune = int32
type semaphore = uint8

自定义类型

type user struct name string;age int  // 用分号把多行代码隔开
type signal uint8
type ms map[string]string
type add func(a, b int) int

2. 数组

数组是块连续的内存空间,在声明的时候必须指定长度,且长度不能改变。所以数组在声明的时候就可以把内存空间分配好,并赋上默认值,即完成了初始化

一维数组初始化

var arr1 [5]int = [5]int // 数组必须指定长度和类型,且长度和类型指定后不可改变
var arr2 = [5]int
var arr3 = [5]int3, 2 // 给前2个元素赋值
var arr4 = [5]int2: 15, 4: 30 // 指定index赋值
var arr5 = [...]int3, 2, 6, 5, 4// 根据里元素的个数推断出数组的长度
var arr6 = [...]struct 
    name string
    age int
"Tom", 18, "Jim", 20 // 数组的元素类型由匿名结构体给定

二维数组初始化

// 5行3列,只给前2行赋值,且前2行的所有列还没有赋满
var arr1 = [5][3]int1, 2, 3
// 第1维可以用...推测,第2维不能使用...
var arr2 = [...][3]int1, 2, 3

访问数组里的元素

  • 通过index访问
    • 首元素 arr[0]
    • 末元素 arr[len(arr) - 1]
  • 访问二维数组里的元素
    • 位于第三行第四列的元素 arr[2][3]

遍历数组

// 遍历数组里的元素
for i, ele := range arr 
    fmt.Printf("index=%d, element=%d\\n", i, ele)

// 也可以这样遍历数组
for i := 0; i < len(arr); i++ 
	// len(arr)获取数组的长度
    fmt.Printf("index=%d, element=%d\\n", i, arr[i])

//遍历二维数组
for row, array := range arr 
	// 先取出某一行
    for col, ele := range array 
    	// 再遍历这一行
        fmt.Printf("arr[%d][%d]=%d\\n", row, col, ele)
    

通过for range遍历数组时取得的是数组里每一个元素的拷贝

arr := [...]int1, 2, 3
for i, ele := range arr 
	// ele是arr中元素的拷贝
    arr[i] += 8 // 修改arr里的元素,不影响ele
    fmt.Printf("%d %d %d\\n", i, arr[i], ele)
    ele += 1 // 修改ele不影响arr
    fmt.Printf("%d %d %d\\n", i, arr[i], ele)

for i := 0; i < len(arr); i++ 
    fmt.Printf("%d %d\\n", i, arr[i])

在数组上调用cap()函数表示capacity容量,即给数组分配的内存空间可以容纳多少个元素;len()函数代表length长度,即目前数组里有几个元素;由于数组初始化之后长度不会改变,不需要给它预留内存空间,所以len(arr)==cap(arr);对于多维数组,其cap和len指第一维的长度

数组的长度和类型都是数组类型的一部分,函数传递数组类型时这两部分都必须吻合;go语言没有按引用传参,全都是按值传参,即传递数组实际上传的是数组的拷贝,当数组的长度很大时,仅传参开销都很大,如果想修改函数外部的数组,就把它的指针(数组在内存里的地址)传进来

// 参数必须是长度为5的int型数组(注意长度必须是5)
func update_array1(arr [5]int) 
	fmt.Printf("array in function, address is %p\\n", &arr[0])
	arr[0] = 888


func update_array2(arr *[5]int) 
	fmt.Printf("array in function, address is %p\\n", &((*arr)[0]))
	arr[0] = 888 // 因为传的是数组指针,所以直接在原来的内存空间上进行修改

3. 切片

切片是一个结构体,包含三个成员变量,array指向一块连续的内存空间,cap表示这块内存的大小,len表示目前该内存里存储了多少元素

type slice struct  
    array unsafe.Pointer 
    len int 
    cap int 

切片的初始化

var s []int // 切片声明,len=cap=0
s = []int // 初始化,len=cap=0
s = make([]int, 3) // 初始化,len=cap=3
s = make([]int, 3, 5) // 初始化,len=3,cap=5
s = []int1, 2, 3, 4, 5 // 初始化,len=cap=5
s2d := [][]int
    1,2, 3, // 二维数组各行的列数相等,但二维切片各行的len可以不等

切片相对于数组最大的特点就是可以追加元素,可以自动扩容。追加的元素放到预留的内存空间里,同时len加1,如果预留空间已用完,则会重新申请一块更大的内存空间,capacity大约变成之前的2倍(cap<1024)或1.25倍(cap>1024),把原内存空间的数据拷贝过来,在新内存空间上执行append操作

s := make([]int, 3, 5)
for i := 0; i < 3; i++ 
    s[i] = i + 1

fmt.Printf("s[0] address %p, s=%v\\n", &s[0], s)
// capacity还够用,直接把追加的元素放到预留的内存空间上
s = append(s, 4, 5) // 可以一次append多个元素
fmt.Printf("s[0] address %p, s=%v\\n", &s[0], s)
// capacity不够用了,得申请一片新的内存,把老数据先拷贝过来,在新内存上执行append操作
s = append(s, 6)
fmt.Printf("s[0] address %p, s=%v\\n", &s[0], s)

通过指定起止下标,可以从大切片中截取一个子切片

s := make([]int, 3, 5) // len=3, cap=5
sub_slice = s[1:3] // len=2, cap=4

刚开始,子切片和母切片共享底层的内存空间,修改子切片会反映到母切片上,在子切片上执行append会把新元素放到母切片预留的内存空间上;当子切片不断执行append,耗完了母切片预留的内存空间,子切片跟母切片就会发生内存分离,此后两个切片没有任何关系

func sub_slice() 
	// 截取一部分,创造子切片,此时子切片与母切片(或母数组)共享底层内存空间,母切片的capacity子切片可能直接用
	s := make([]int, 3, 5)
	for i := 0; i < 3; i++ 
		s[i] = i + 1
	
	fmt.Printf("s[1] address %p\\n", &s[1])
	sub_slice := s[1:3] // 从切片创造子切片,len=cap=2
	fmt.Printf("len %d cap %d\\n", len(sub_slice), cap(sub_slice))
	// 母切片的capacity还允许子切片执行append操作
	sub_slice = append(sub_slice, 6, 7) // 可以一次append多个元素
	sub_slice[0] = 8
	fmt.Printf("s=%v, sub_slice=%v, s[1] address %p, sub_slice[0] address %p\\n", s, sub_slice, &s[1], &sub_slice[0])
	// 母切片的capacity用完了,子切片再执行append就得申请一片新的内存,把老数据先拷贝过来,在新内存上执行append操作,此时的append操作跟母切片没有任何关系
	sub_slice = append(sub_slice, 8)
	sub_slice[0] = 9
	fmt.Printf("s=%v, sub_slice=%v, s[1] address %p, sub_slice[0] address %p\\n", s, sub_slice, &s[1], &sub_slice[0])

	arr := [5]int1, 2, 3, 4, 5
	fmt.Printf("arr[1] address %p\\n", &arr[1])
	sub_slice = arr[1:3] // 从数组创造子切片,len=cap=2
	fmt.Printf("len %d cap %d\\n", len(sub_slice), cap(sub_slice))
	// 母数组的capacity还允许子切片执行append操作
	sub_slice = append(sub_slice, 6, 7) // 可以一次append多个元素
	sub_slice[0] = 8
	fmt.Printf("arr=%v, sub_slice=%v, arr[1] address %p, sub_slice[0] address %p\\n", arr, sub_slice, &arr[1], &sub_slice[0])
	// 母数组的capacity用完了,子切片再执行append就得申请一片新的内存,把老数据先拷贝过来,在新内存上执行append操作,此时的append操作跟母数组没有任何关系
	sub_slice = append(sub_slice, 8)
	sub_slice[0] = 9
	fmt.Printf("arr=%v, sub_slice=%v, arr[1] address %p, sub_slice[0] address %p\\n", arr, sub_slice, &arr[1], &sub_slice[0])

Go语言函数传参,传的都是值,即传切片会把切片的arrayPointer, len, cap这3个字段拷贝一份传进来,由于传的是底层数组的指针,所以可以直接修改底层数组里的元素

func update_slice(s []int) 
	s[0] = 888

s := []int1, 2, 3
update_slice(s)
fmt.Printf("s=%v\\n", s)

4. 字符串

字符串里面可以包含任意Unicode字符

s := " Ricky☻" 

字符串里也可以包含转义字符

s := "He say:\\"I'm fine.\\" \\n\\\\Thank\\tyou.\\\\" 

字符串也可以用反引号来定义,反引号里的转义字符无效,反引号里的内容原封不动地输出,包括空白符和换行符

s := `here is first line. 

  there is third line.
`

字符串常用操作

方法介绍
len(str)求长度
strings.Split分割
strings.Contains判断是否包含
strings.HasPrefix,strings.HasSuffix前缀/后缀判断
strings.Index(),strings.LastIndex()子串出现的位置
s := "born to win, born to die."
fmt.Printf("sentence length %d\\n", len(s))
fmt.Printf("\\"s\\" length %d\\n", len("s")) // 英文字母的长度为1
fmt.Printf("\\"中\\"  length %d\\n", len("中")) // 一个汉字占3个长度
arr := strings.Split(s, " ")
fmt.Printf("arr[3]=%s\\n", arr[3])
fmt.Printf("contain die %t\\n", strings.Contains(s, "die"))          //包含子串
fmt.Printf("contain wine %t\\n", strings.Contains(s, "wine"))        //包含子串
fmt.Printf("first index of born %d\\n", strings.Index(s, "born"))    //寻找子串第一次出现的位置
fmt.Printf("last index of born %d\\n", strings.LastIndex(以上是关于Go基础数据类型的主要内容,如果未能解决你的问题,请参考以下文章

Go语言基础

Java基础——数据类型

C语言使用栈实现String类型的两个大数相加

Java中数据类型的转换

Go 粘包

Go语言判断一个字节的高位大于四