• 【Go语言学习笔记】类型


    类型

    变量

    	var x int     // 自动初始化为0
    	var y = false // 自动推断为bool类型
    
    • 1
    • 2
    可以一次定义多个变量,包括用不同初始值定义不同类型
    
    • 1
    	var a, b int            // 相同类型的多个变量
    	var c, d = 100, "hello" // 不同类型初始化值
    
    • 1
    • 2
    简短模式
    	x := 100
    	a, s := 1, "abc"
    
    • 1
    • 2

    限制:

    • 定义变量,同时显式初始化;
    • 不能提供数据类型;
    • 只能用在函数内部

    = 和 := 的区别
    = 是赋值,:= 是声明变量并赋值,并且系统自动推断类型,不需要var关键字

    退化赋值

    退化赋值的前提条件是:最少有一个新变量被定义,且必须是同一作用域

    	x := 100
    	println(&x, x)
    	x, s := 200, "123"
    	println(&x, x, s)
    
    0xc000039f68 100    
    0xc000039f68 200 123
    // 对比内存地址,可以确认x属于同一变量 
    
    // 无新变量被定义
    	x := 100
    	println(&x, x)
    	x := 200
    	println(&x, x)
    
    no new variables on left side of :=
    
    // 不同作用域
    	x := 100
    	println(&x, x)
    
    	{
    		x, s := 200, "hello"
    		println(&x, x, s)
    	}
    0xc000039f68 100
    0xc000039f60 200 hello
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在处理函数错误返回值时,退化赋值允许重复使用err变量。

    多变量赋值
    	x, y := 1, 2
    	x, y = y+3, x+2 // 先计算出右值 y+3, x+2 然后再对x、y变量赋值
    
    • 1
    • 2

    命名

    • 以字母或者下划线开始,由多个字母、数字和下划线组合而成;
    • 区分大小写;
    • 使用驼峰拼写格式;
    • 局部变量优先使用短名;
    • 不要使用保留关键字;
    • 不建议使用与预定义常量、类型、内置函数相同的名字;
    • 专有名字通常会全部大写。
    空标识符

    空白标识符是未使用的值的占位符。它由下划线(_)表示。由于空白标识符没有名称,因此它们也被称为匿名占位符。

    在 Go 语言中, 不允许声明未使用的变量或导入语句。也就是说,我们无法声明变量并将其留下未使用。同样,如果您导入一个包,那么也必须使用它。

    这个时候就需要一个空白标识符。

    如果 Go 中的函数返回多个值,则必须定义相等数量的变量来保存这些值。但是,如果您只需要其中一些值而不需要其他值,若某次赋值需要匹配多个左值,但其中某个变量不会被程序使用, 那么用空白标识符来代替。该变量可避免创建无用的变量,并能清楚地表明该值将被丢弃。

    常量

    常量表示运行时更定不可改变的值。

    const x, y int = 123, 0x22
    
    • 1

    枚举

    go中借助iota标识符实现一组自增常量值来实现枚举类型

    const (
    	x = iota // 0
    	y        // 1
    	z        // 2
    )
    
    const (
    	_, _ = iota, iota * 2
    	a, b
    	c, d
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果中断iota自增,则必须显式回复,且后续自增值按行序号递增。

    const (
    	a = iota //0
    	b        //1
    	c = 100  //100
    	d        //100
    	e = iota //4
    	f        //5
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    自增默认数据类型为int,可显式指定类型

    const (
    	a = iota         //0
    	b                //1
    	c = 100          //100
    	d                //100
    	e float32 = iota //+4.000000e+000
    	f                //+5.000000e+000
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    常量和变量的不同

    常量通常会被编译器在预处理阶段直接展开,作为指令数据使用。变量在运行期分配存储内存。数字常量不会分配存储空间,因此无法获取地址。

    基本类型

    类型长度默认值说明
    bool1false
    byte10
    int,uint4,80默认证书类型,依据目标平台,32或64位
    int8,uint810-128~127,0~255
    int16,uint1620-32758~32767,0~65535
    int32,uint3240-21亿~21亿,0~42亿
    int64,uint6480
    float3240.0
    float6480.0默认浮点数类型
    complex648
    complex12816
    rune40
    uintptr4,80足以存储之战的uinit
    string“”字符串,默认为空字符串而非null
    array数组
    struct结构体
    functionnil函数
    interfacenil接口
    mapnil字典,引用类型
    slicenil切片,引用类型
    channelnil通道,引用类型

    引用类型

    reference type特值slice、map、channel这三种预定义类型。

    应用类型必须使用make函数创建,编译器会将make转换为目标专用的创建函数,以确保完成全部内存分配和相关属性初始化。

    func mkslice() []int {
    	s := make([]int, 0, 10)
    	s = append(s, 100)
    	return s
    }
    
    func mkmap() map[string]int {
    	m := make(map[string]int)
    	m["a"] = 1
    	return m
    }
    
    func main() {
    	m := mkmap()
    	println(m["a"])
    	s := mkslice()
    	println(s[0])
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    类型转换

    除常量、别名类型以及未命名类型外,Go强制要求使用类型转换。

    a:=10
    b:=byte(a)
    c:=a+int(b)
    
    • 1
    • 2
    • 3

    同时不能将非bool类型结果当作true/false使用

    自定义类型

    使用关键字type定义用户自定义类型,包括基于现有基础类型创建,或者是结构体、函数类型等。

    	type ( //组
    		user struct { // 结构体
    			name string
    			age  uint8
    		}
    		event func(string) bool // 函数类型
    	)
    
    	u := user{"Tome", 20}
    	fmt.Println(u)
    
    	var f event = func(s string) bool {
    		println(s)
    		return s != ""
    	}
    	f("abc")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    自定义类型创建基础类型,也只能表明他们有相同地城数据结构,两者间不存在任何关系,属于完全不同的两种类型,自定义类型不会继承基础类型的其他信息,不能视作别名,不能隐式转换,不能直接用于比较表达式。

    	type data int  // 自定义类型为基础类型
    	var d data = 10
    	var x int = d  // 不能隐式转换 Cannot use 'd' (type data) as the type int
    	println(x) 
    	println(d == x) // 两种完全不同的类型 Invalid operation: d == x (mismatched types data and int)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    表达式

    保留字

    break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var

    运算符

    初始化

    对于复合类型(数组、切片、字典、结构体)变量初始化时,有一些语法限制。

    • 初始化表达式必须含类型标签;
    • 左花括号必须在类型尾部,不能另起一行;
    • 多个成员初始值以逗号分隔;
    • 允许多行,但每行须以逗号或者右花括号结束。
    	type data struct {
    		x int
    		y string
    	}
    	a := data{1, "hello"}
    	b := data{
    		1,
    		"hello2",
    	}
    	c := []int{
    		1,
    		2}
    	
    	d:=[]int{1,2,
    		3,4,
    		5,
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    流控制

    if … else …

    条件表达式值必须是布尔类型,可以省略括号,且左花括号不能另起一行。

    	x := 3
    	if x > 5 {
    		println("a")
    	} else if x < 5 && x > 0 {
    		println("b")
    	} else {
    		println("c")
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    支持初始化语句,可以定义块局部变量或者执行初始化函数。

        // 局部变量的有效范围包含整个if/else 块
        // 定义一个或多个局部变量,也可以是函数返回值
    	if a, b := x+1, x+10; a < b {
    		println(a)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    switch
    	a, b, c, x := 1, 2, 3, 4
    	switch x {
    	case a, b: // 多个匹配条件命中其一即可OR
    		println("a|b")
    	case c:
    		println("c")
    	case 4: // 常量
    		println("d")
    	default:
    		println(x)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    switch同样支持初始化语句,按从上到下、从左到右顺序匹配case执行。只有全部失败时,才会执行default块。

    func main() {
    	switch x := 5; x {
    	default: //编译器确保不会先执行default块
    		x += 100
    		println(x)
    	case 5:
    		x += 50
    		println(x)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    不能出现重复case常量值。

    fallthrough 继续执行下一个case,但是不再匹配条件表达式,必须放在case块结尾,可以使用break语句阻止。

    for
    	for i := 0; i < 3; i++ { // 初始化表达式支持函数调用或定义局部变量
    		
    	}
    	for x<10{  // 类似“while x<10 {} 或 for ;x<10;{}”
    		x++
    	}
    	for{      // 类似“while true {} 或 for true {}”
    		break
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    for … range

    可以使用for…range 完成数据迭代,支持字符串、数组、数组指针、切片、字典、通道,返回索引、键值数据。

    	data := [3]string{"a", "b", "c"}
    	for i, s := range data {
    		println(i, s)
    	}
    	for i := range data {
    		println(i, data[i]) // 只返回索引
    	}
    	for _, s := range data { // 忽略索引
    		println(s)
    	}
    	for range data { // 仅迭代,可以用来执行清空channel等操作
    
    	}
    
    	for i, s := range data {
    		println(&i, &s) // 局部变量重复使用
    	}
    
    	//0xc000039ec0 0xc000039f00
    	//0xc000039ec0 0xc000039f00
    	//0xc000039ec0 0xc000039f00
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    range会复制目标数据,受直接影响的是数组,可改用数组指针或者切片类型

    	data := [3]int{10, 20, 30}
    	for i, s := range data { // 从data 复制品中取值
    		if i == 0 {
    			data[0] += 100
    			data[1] += 200
    			data[2] += 300
    		}
    		fmt.Printf("s: %d, data: %d\n", s, data[i])
    	}
    s: 10, data: 110
    s: 20, data: 220
    s: 30, data: 330
    
    	data := [3]int{10, 20, 30}
    	for i, s := range data[:] { // 仅复制slice 不包括底层array
    		if i == 0 {
    			data[0] += 100
    			data[1] += 200
    			data[2] += 300
    		}
    		fmt.Printf("s: %d, data: %d\n", s, data[i])
    	}
    s: 10, data: 110
    s: 220, data: 220
    s: 330, data: 330
    // 当i=0修改data时,s已经取值,所以是10 复制的仅是slice自身,底层array依旧是原对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    如果range目标表达式是函数调用,也仅被执行一次。

    goto continue break

    使用goto前,必须先定义标签,标签区分大小写,且未使用的标签会引发编译错误。而且不能跳转到其他函数,或者内层代码块内。配合标签,break和continue可在多层嵌套中指定目标层级。

  • 相关阅读:
    安卓的ATV系统
    操作系统学习笔记--进程与线程
    SpringBoot使用Swagger2创建API文档
    MF-SuP-pKa
    day5ARM
    【Python 零基础入门】Pandas
    JavaScript性能优化:实战攻略
    python 报错 module ‘asyncio‘ has no attribute ‘run‘
    vue+cesium项目demo
    工具配置-如何在NextCloud私有云盘安装的olnyOffice插件中添加中文字体支持实践操作...
  • 原文地址:https://blog.csdn.net/mashaokang1314/article/details/126901143