4.Golang数据类型

栏目: Go · 发布时间: 5年前

内容简介:Go 语言数据类型包含基础类型和复合类型两大类。基础数据类型包括:布尔型、整型、浮点型、复数型、字符型、字符串型、错误类型。复合数据类型包括:指针、数组、切片、字典、通道、结构体、接口。

Go 语言数据类型包含基础类型和复合类型两大类。

基础数据类型包括:布尔型、整型、浮点型、复数型、字符型、字符串型、错误类型。

复合数据类型包括:指针、数组、切片、字典、通道、结构体、接口。

Go 语言在声明变量时会默认给变量赋个当前类型的空值,声明变量的方式:

声明方式 说明
var 变量名 <变量类型> 声明单个变量
var 变量名1, 变量名2,... <变量类型> 声明多个同类型变量
变量名 := 值 声明变量,并赋值;Go 语言会根据所赋值推断变量的类型
变量名1, 变量名2,... := 值1, 值2,... 声明多个同类型变量并赋值,几个变量必须赋几个值

一、布尔类型 (bool)

值:true 和 false,默认值为 false

package main

import "fmt"

func main() {
    var v1, v2 bool         // 声明变量,默认值为 false
    v1 = true               // 赋值
    v3, v4 := false, true   // 声明并赋值

    fmt.Print("v1:", v1)   // v1 输出 true
    fmt.Print("\nv2:", v2) // v2 没有重新赋值,显示默认值:false
    fmt.Print("\nv3:", v3) // v3 false
    fmt.Print("\nv4:", v4) // v4 true
}

二、数字类型

数字类型比较多,默认值都是 0。定义 int 类型时,默认根据系统类型设置取值范围,32位系统与 int32 的值范围相同,64位系统与 int64 的值范围相同。见下表:

类型 名称 存储空间 值范围 数据级别
uint8 无符号8位整形 8-bit 0 ~ 255
uint16 无符号16位整形 16-bit 0 ~65535 6万多
uint32 无符号32位整形 32-bit 0 ~ 4294967295 40多亿
uint64 无符号64位整形 64-bit 0 ~ 18446744073709551615 死了都不会溢出
int8 8位整形 8-bit -128 ~ 127 正负百
int16 16位整形 16-bit -32768 ~ 32767 正负3万多
int32 32位整形 32-bit -2147483648 ~ 2147483647 正负20多亿
int64 64位整形 64-bit -9223372036854775808 ~ 9223372036854775807 正负大到没概念
int 系统决定 系统决定 32位系统为 int32 的值范围,64位系统为 int64 的值范围
-
float32 32位浮点数 32-bit IEEE-754 1.401298464324817070923729583289916131280e-45 ~ 3.402823466385288598117041834516925440e+38 精度6位小数
float64 64位浮点数 64-bit IEEE-754 4.940656458412465441765687928682213723651e-324 ~ 1.797693134862315708145274237317043567981e+308 精度15位小数
-
complex64 复数,含 float32 位实数和 float32 位虚数 64-bit 实数、虚数的取值范围对应 float32
complex128 复数,含 float64 位实数和 float64 位虚数 128-bit 实数、虚数的取值范围对应 float64
-
byte 字符型,unit8 别名 8-bit 表示 UTF-8 字符串的单个字节的值,对应 ASCII 码的字符值
rune 字符型,int32 别名 32-bit 表示 单个 Unicode 字符
uintptr 无符号整型 由系统决定 能存放指针地址即可
package main

import "fmt"

func main() {
    // 无符号整形,默认值都是0
    var u8 uint8
    var u16 uint16
    var u32 uint32
    var u64 uint64
    fmt.Printf("u8: %d, u16: %d, u32: %d, u64: %d\n", u8, u16, u32, u64) // 默认值都为0
    u8 = 255
    u16 = 65535
    u32 = 4294967295
    u64 = 18446744073709551615
    fmt.Printf("u8: %d, u16: %d, u32: %d, u64: %d\n", u8, u16, u32, u64)

    // 整型
    var i8 int8
    var i16 int16
    var i32 int32
    var i64 int64
    fmt.Printf("i8: %d, i16: %d, i32: %d, i64: %d\n", i8, i16, i32, i64) // 默认值都为0
    i8 = 127
    i16 = 32767
    i32 = 2147483647
    i64 = 9223372036854775807
    fmt.Printf("i8: %d, i16: %d, i32: %d, i64: %d\n", i8, i16, i32, i64)

    // int 型,取值范围32位系统为 int32,64位系统为 int64,取值相同但为不同类型
    var i int
    //i = i32 // 报错,编译不通过,类型不同
    //i = i64 // 报错,编译不通过,类型不同
    i = -9223372036854775808
    fmt.Println("i: ", i)

    // 浮点型,f32精度6位小数,f64位精度15位小数
    var f32 float32
    var f64 float64
    fmt.Printf("f32: %f, f64: %f\n", f32, f64) // 默认值都为 0.000000
    f32 = 1.12345678
    f64 = 1.12345678901234567
    fmt.Printf("f32: %v, f64: %v\n", f32, f64) // 末位四舍五入,输出:f32: 1.1234568, f64: 1.1234567890123457

    // 复数型
    var c64 complex64
    var c128 complex128
    fmt.Printf("c64: %v, c128: %v\n", c64, c128) // 实数、虚数的默认值都为0
    c64 = 1.12345678 + 1.12345678i
    c128 = 2.1234567890123456 + 2.1234567890123456i
    fmt.Printf("c64: %v, c128: %v\n", c64, c128) // 输出:c64: (1.1234568+1.1234568i), c128: (2.1234567890123457+2.1234567890123457i)

    // 字符型
    var b byte                                       // uint8 别名
    var r1, r2 rune                                  // uint16 别名
    fmt.Printf("b: %v, r1: %v, r2: %v\n", b, r1, r2) // 默认值为0
    b = 'a'
    r1 = 'b'
    r2 = '字'
    fmt.Printf("b: %v, r1: %v, r2: %v\n", b, r1, r2) // 输出:b: 97(ASCII表示的数), r1: 98(utf-8表示的数), r2: 23383 (utf-8表示的数)

    b = u8
    r1 = i32
    fmt.Printf("b: %v, r1: %v\n", b, r1) // 输出:b: 255, r1: 2147483647

    // 指针地址
    var p uintptr
    fmt.Printf("p: %v\n", p) // 默认值为0
    p = 18446744073709551615 // 64位系统最大值
    //p = 18446744073709551616 // 报错:超出最大值
    fmt.Printf("p: %v\n", p)

}

三、字符串 (string)

Go 语言默认编码都是 UTF-8。

package main

import "fmt"

func main() {
    var str1 string // 默认值为空字符串 ""
    str1 = `hello world`
    str2 := "你好世界"

    str := str1 + " " + str2 // 字符串连接
    fmt.Println(str1)
    fmt.Println(str2)
    fmt.Println(str) // 输出:hello world 你好世界

    // 遍历字符串
    l := len(str)
    for i := 0; i < l; i++ {
        chr := str[i]
        fmt.Println(i, chr) // 输出字符对应的编码数字
    }
}

四、指针(pointer)

指针其实就是指向一个对象(任何一种类型数据、包括指针本身)的地址值,对指针的操作都会映射到指针所指的对象上。

package main

import (
    "fmt"
)

func main() {
    var p *int // 定义指向int型的指针,默认值为空:nil

    // nil指针不指向任何有效存储地址,操作系统默认不能访问
    //fmt.Printf("%x\n", *p) // 编译报错

    var a int = 10
    p = &a        // 取地址
    add := a + *p // 取值

    fmt.Println(a)   // 输出:10
    fmt.Println(p)   // 输出:0xc0420080b8
    fmt.Println(add) // 输出:20
}

五、数组(array)

数组为一组相同数据类型数据的集合,数组定义后大小固定,不能更改,每个元素称为 element ,声明的数组元素默认值都是对应类型的0值。而且数组在 Go 语言中是一个值类型(value type), 所有值类型变量在赋值和作为参数传递时都会产生一次复制动作 ,即对原值的拷贝。

package main

import "fmt"

func main() {
    // 1.声明后赋值
    // var <数组名称> [<数组长度>]<数组元素>
    var arr [2]int   // 数组元素的默认值都是 0
    fmt.Println(arr) // 输出:[0 0]
    arr[0] = 1
    arr[1] = 2
    fmt.Println(arr) // 输出:[1 2]

    // 2.声明并赋值
    // var <数组名称> = [<数组长度>]<数组元素>{元素1,元素2,...}
    var intArr = [2]int{1, 2}
    strArr := [3]string{`aa`, `bb`, `cc`}
    fmt.Println(intArr) // 输出:[1 2]
    fmt.Println(strArr) // 输出:[aa bb cc]

    // 3.声明时不设定大小,赋值后语言本身会计算数组大小
    // var <数组名称> [<数组长度>]<数组元素> = [...]<元素类型>{元素1,元素2,...}
    var arr1 = [...]int{1, 2}
    arr2 := [...]int{1, 2, 3}
    fmt.Println(arr1) // 输出:[1 2]
    fmt.Println(arr2) // 输出:[1 2 3]
    //arr1[2] = 3 // 编译报错,数组大小已设定为2

    // 4.声明时不设定大小,赋值时指定索引
    // var <数组名称> [<数组长度>]<数组元素> = [...]<元素类型>{索引1:元素1,索引2:元素2,...}
    var arr3 = [...]int{1: 22, 0: 11, 2: 33}
    arr4 := [...]string{2: "cc", 1: "bb", 0: "aa"}
    fmt.Println(arr3) // 输出:[11 22 33]
    fmt.Println(arr4) // 输出:[aa bb cc]

    // 遍历数组
    for i := 0; i < len(arr4); i++ {
        v := arr4[i]
        fmt.Printf("i:%d, value:%s\n", i, v)
    }
}

六、切片(slice)

因为数组的长度定义后不可修改,所以需要切片来处理可变长数组数据。切片可以看作是一个可变长的数组,是一个引用类型。它包含三个数据:1.指向数组的指针,2.切片中的元素,3.切片的大小

声明一个切片,或从数组中取一段作为切片数据:

package main

import "fmt"

func main() {
    var sl []int             // 声明一个切片
    sl = append(sl, 1, 2, 3) // 往切片中追加值
    fmt.Println(sl)          // 输出:[1 2 3]

    var arr = [5]int{1, 2, 3, 4, 5} // 初始化一个数组
    var sl1 = arr[0:2]              // 冒号:左边为起始位(包含起始位数据),右边为结束位(不包含结束位数据);不填则默认为头或尾
    var sl2 = arr[3:]
    var sl3 = arr[:5]

    fmt.Println(sl1) // 输出:[1 2]
    fmt.Println(sl2) // 输出:[4 5]
    fmt.Println(sl3) // 输出:[1 2 3 4 5]

    sl1 = append(sl1, 11, 22) // 追加元素
    fmt.Println(sl1)          // 输出:[1 2 11 22]
}

使用 make 直接创建切片,语法:make([]类型, 大小,预留空间大小)

package main

import "fmt"

func main() {
    var sl1 = make([]int, 5)          // 定义元素个数为5的切片
    sl2 := make([]int, 5, 10)         // 定义元素个数5的切片,并预留10个元素的存储空间(预留空间不知道有什么用?)
    sl3 := []string{`aa`, `bb`, `cc`} // 直接创建并初始化包含3个元素的数组切片

    fmt.Println(sl1, len(sl1)) // 输出:[0 0 0 0 0] 5
    fmt.Println(sl2, len(sl2)) // 输出:[0 0 0 0 0] 5
    fmt.Println(sl3, len(sl3)) // [aa bb cc] 3

    sl1[1] = 1 // 声明或初始化大小中的数据,可以指定赋值
    sl1[4] = 4
    //sl1[5] = 5 // 编译报错,超出定义大小
    sl1 = append(sl1, 5)       // 可以追加元素
    fmt.Println(sl1, len(sl1)) // 输出:[0 1 0 0 4 5] 6

    sl2[1] = 1
    sl2 = append(sl2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
    fmt.Println(sl2, len(sl2)) // 输出:[0 1 0 0 0 1 2 3 4 5 6 7 8 9 10 11] 16

    // 遍历切片
    for i := 0; i < len(sl2); i++ {
        v := sl2[i]
        fmt.Printf("i: %d, value:%d \n", i, v)
    }
}

七、字典(map)

map 是一种键值对的无序集合,与 slice 类似也是一个引用类型。map 本身其实是个指针,指向内存中的某个空间。

声明方式与数组类似,声明方式: var 变量名 map[key类型]值类型 或直接使用 make 函数初始化: make(map[key类型]值类型)

其中 key 值可以是任何可以用 == 判断的值类型,对应的值类型没有要求。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    // 声明后赋值
    var m map[int]string
    fmt.Println(m) // 输出空的map:map[]
    //m[1] = `aa`    // 向未初始化的map中赋值报错:panic: assignment to entry in nil map

    // 声明并初始化,初始化使用{} 或 make 函数(创建类型并分配空间)
    var m1 = map[string]int{}
    var m2 = make(map[string]int)
    m1[`a`] = 11
    m2[`b`] = 22
    fmt.Println(m1) // 输出:map[a:11]
    fmt.Println(m2) // 输出:map[b:22]

    // 初始化多个值
    var m3 = map[string]string{"a": "aaa", "b": "bbb"}
    m3["c"] = "ccc"
    fmt.Println(m3) // 输出:map[a:aaa b:bbb c:ccc]

    // 删除 map 中的值
    delete(m3, "a") // 删除键 a 对应的值
    fmt.Println(m3) // 输出:map[b:bbb c:ccc]

    // 查找 map 中的元素
    v, ok := m3["b"]
    if ok {
        fmt.Println(ok)
        fmt.Println("m3中b的值为:", v) // 输出:m3中b的值为: bbb
    }
    // 或者
    if v, ok := m3["b"]; ok { // 流程处理后面讲
        fmt.Println("m3中b的值为:", v) // 输出:m3中b的值为: bbb
    }

    fmt.Println(m3["c"]) // 直接取值,输出:ccc

    // map 中的值可以是任意类型
    m4 := make(map[string][5]int)
    m4["a"] = [5]int{1, 2, 3, 4, 5}
    m4["b"] = [5]int{11, 22, 33}
    fmt.Println(m4)                // 输出:map[a:[1 2 3 4 5] b:[11 22 33 0 0]]
    fmt.Println(unsafe.Sizeof(m4)) // 输出:8,为8个字节,map其实是个指针,指向某个内存空间
}

八、通道(channel)

说到通道 channel,则必须先了解下 Go 语言的 goroutine 协程(轻量级线程)。channel 就是为 goroutine 间通信提供通道。goroutine 是 Go 语言提供的语言级的协程,是对 CPU 线程和调度器的一套封装。

channel 也是类型相关的,一个 channel 只能传递一种类型的值。

声明: var 通道名 chan 通道传递值类型 或 make 函数初始化: make(chan 值类型, 存储空间大小)

package main

import (
    "fmt"
    "time"
)

func main() {
    var ch1 chan int            // 声明一个通道
    ch1 = make(chan int)        // 未初始化的通道不能存储数据,初始化一个通道
    ch2 := make(chan string, 2) // 声明并初始化一个带缓冲空间的通道

    // 通过匿名函数向通道中写入数据,通过 <- 方式写入
    go func() { ch1 <- 1 }()
    go func() { ch2 <- `a` }()

    v1 := <-ch1 // 从通道中读取数据
    v2 := <-ch2
    fmt.Println(v1) // 输出:1
    fmt.Println(v2) // 输出:a

    // 写入,读取通道数据
    ch3 := make(chan int, 1) // 初始化一个带缓冲空间的通道
    go readFromChannel(ch3)
    go writeToChannel(ch3)

    // 主线程休眠1秒,让出执行权限给子 Go 程,即通过 go 开启的 goroutine,不然主程序会直接结束
    time.Sleep(1 * time.Second)
}

func writeToChannel(ch chan int) {
    for i := 1; i < 10; i++ {
        fmt.Println("写入:", i)
        ch <- i
    }
}

func readFromChannel(ch chan int) {
    for i := 1; i < 10; i++ {
        v := <-ch
        fmt.Println("读取:", v)
    }
}

// ------  输出:--------
1
a
写入: 1
写入: 2
写入: 3
读取: 1
读取: 2
读取: 3
写入: 4
写入: 5
写入: 6
读取: 4
读取: 5
读取: 6
写入: 7
写入: 8
写入: 9
读取: 7
读取: 8
读取: 9

goroutine 和 channel 的详细讲解待补充。

九、结构体(struct)

结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。

package main

import "fmt"

// 定义一个结构体 person
type person struct {
    name string
    age  int
}

func main() {
    var p person   // 声明一个 person 类型变量 p
    p.name = "max" // 赋值
    p.age = 12
    fmt.Println(p) // 输出:{max 12}

    p1 := person{name: "mike", age: 10} // 直接初始化一个 person
    fmt.Println(p1.name)                // 输出:mike

    p2 := new(person) // new函数分配一个指针,指向 person 类型数据
    p2.name = `张三`
    p2.age = 15
    fmt.Println(*p2) // 输出:{张三 15}
}

十、接口(interface)

十一、错误


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

SRE

SRE

贝特西 拜尔 (Betsy Beyer)、等 / 孙宇聪 / 电子工业出版社 / 2016-10-1 / CNY 108.00

大型软件系统生命周期的绝大部分都处于“使用”阶段,而非“设计”或“实现”阶段。那么为什么我们却总是认为软件工程应该首要关注设计和实现呢?在《SRE:Google运维解密》中,Google SRE的关键成员解释了他们是如何对软件进行生命周期的整体性关注的,以及为什么这样做能够帮助Google成功地构建、部署、监控和运维世界上现存最大的软件系统。通过阅读《SRE:Google运维解密》,读者可以学习到......一起来看看 《SRE》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具