• Go实战学习笔记-1.2基础语法:变量、常量、包、指针等


    1. 前言

    这里总结一下Go中变量、常量、包、函数、指针、输入输出及基础数据类型转换的基础用法,便于回顾。
    本次总结的内容大致如下:

    在这里插入图片描述

    2. 变量variables

    变量声明以及可代表的类型很多,var是变量声明的关键字,但也可以使用:=进行短声明,如下实例所示:

    //方式1
    var name string
    var name1,name2 string
    var num int
    //方式2
    var (
        name string
        num int
    )
    //方式3
    var name = "test"
    var num = 1
    //方式4
    name := "test"
    num := 1
    //返回值变量
    res := func() {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3. 常量constants

    目前Go仍不支持枚举类型,通常是使用常量来代替枚举的。
    常用使用关键词const,常用用于字符、字符串、布尔值和数值,可代表枚举值

    const name = "test"
    const num = 1
    const (
        name = "test"
        num = 1
    )
    //代替枚举
    const (
        Monday    = 1
        Tuesday   = 2
        Wednesday = 3
        Thursday  = 4
        Friday    = 5
        Saturday  = 6
        Sunday    = 7
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4. 包的导入和使用

    可以参考编程指南中对包和导包的示例:
    https://tour.go-zh.org/basics/1
    https://tour.go-zh.org/basics/2
    每个go程序都是由包组成的,程序以包中的main函数开始执行(包感觉是对模块化编程的实例化,可以用模块的概念来理解包,可以简单理解为将模块化的部分封装为库,库的引入是以包的方式引入的)。
    如果您正在编写可执行代码(而不是库),那么需要定义一个main包和一个main()函数,这将是软件的入口点。
    按照约定,包名与导入路径的最后一个元素相同。例如,“math/rand”包包含以语句包 rand 开头的文件。
    导入语句示例:

    import "fmt"
    import "math/rand"
    //或者分组:
    import (
        "fmt"
        "math/rand"
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对于一些GitHub上或者gitlab上的非标准包,我们通常使用web url的命名空间方式来导入,比如我导入一个github上的uci包:https://github.com/digineo/go-uci,则这样使用:

    import "github.com/digineo/go-uci"
    
    • 1

    5. 指针

    go的指针为了简化学习难度目前是不支持指针运算的,使用指针目前个人感觉更多是为了减少内存占用以及方便传参和修改参数,比如传参等如果是传递指针只需要传递一个地址,而如果是直接传递参数则会由于形参和实参的问题复制对应的变量内容,当然,如果传递后要更改传递参数的内容肯定也是需要传递指针类型值的,否则由于传递参数是复制的就达不到改变传递参数内容的效果了。
    可以参考指南:https://tour.go-zh.org/moretypes/1

    var p *int
    //或者
    i := 42
    p = &i
    //使用指针获取变量值及修改变量值
    j := *p
    *p = 32
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    6. 基础数据类型及数据类型转换

    go为了防止c中由于隐式数据类型转换引起的奇怪问题,所以go进行数据类型转换时需要清楚表明类型转换后的类型,也就是显式类型转换,这点在我看来是必要的,隐式类型转换往往会引起一些奇怪的bug,所以在编译阶段就搞清楚并表明要转换的数据类型个人觉得是必要的,写代码偷懒一会,查bug可能会花费更多的时间。

    //布尔类型
    bool
    //字符串类型
    string
    //整型
    int  int8  int16  int32  int64
    uint uint8 uint16 uint32 uint64 uintptr
    byte // uint8 的别名
    rune // int32 的别名 表示一个 Unicode 码点
    //浮点型
    float32 float64
    //复数类型
    complex64 complex128
    
    //类型转换,如果不添加类型编译器会报错
    var i int = 42
    var f float64 = float64(i)
    var u uint = uint(f)
    i := 42
    f := float64(i)
    u := uint(f)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    7. 示例

    package main
    
    import "fmt"
    
    func main() {
    	//1.变量
    	name := "张三"
    	num := 1
    	var age int
    	age = 10
    	fmt.Println(name,"编号是:",num,"年龄是:",age)
    
    	//2.常量
    	const name2 = "李四"
    	const (
    		ClassOne   = 1
    		ClassTwo   = 2
    		ClassThree = 3
    	)
    	fmt.Println(name2,"在",ClassOne,"班")
    
    	//3.指针
    	var p *int
    	p = &age
    	*p = 11
    	fmt.Println(name,"编号是:",num,"年龄是:",age)
    
    	//4.类型转换
    	f := 12.1
    	age = int(f)
    	var u uint64
    	u = 2
    	num = int(u)
    	fmt.Println(name,"编号是:",num,"年龄是:",age)
    }
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    结果:

    在这里插入图片描述

    8. printing和scanning

    具体可以参考这里:https://pkg.go.dev/fmt#pkg-overview,基本都是fmt包的实现,fmt包采用了类C的printf和scanf方式实现格式化I/O,我们这里结合着写了一些示例做了验证。

    8.1 printing

    8.1.1 一般动词打印

    • %v 打印结构时默认格式的值,加号标志 (%+v) 添加字段名称打印
    • %#v 打印结构中的值
    • %T 打印值类型
    • %% 打印百分号%

    示例:(关于结构体我们后面再专门详细总结)

    package main
    
    import "fmt"
    
    func main() {
    	type person struct {
    		name string
    		num int
    		age int
    	}
    
    	test := person{
    		name: "张三",
    		num:  1,
    		age:  10,
    	}
    	fmt.Printf("%v\n", test)
    	fmt.Printf("%+v\n", test)
    	fmt.Printf("%#v\n", test)
    	fmt.Printf("%T\n", test)
    	fmt.Printf("%%\n")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    结果:

    在这里插入图片描述

    8.1.2 布尔值

    • %t 打印结果为true或false

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	test := true
    	fmt.Printf("%t\n", test)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    8.1.3 整形值

    • %b base 2
    • %c 由相应的 Unicode 代码点表示的字符
    • %d base 10
    • %o base 8
    • %O base 8 带 0o 前缀
    • %q 一个单引号字符文字,使用 Go 语法安全转义。
    • %x base 16,小写字母代表 af
    • %X base 16,大写字母代表 AF
    • %U Unicode 格式:U+1234;与 “U+%04X” 相同

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	test := '1'
    	fmt.Printf("%b\n", test)
    	fmt.Printf("%c\n", test)
    	fmt.Printf("%d\n", test)
    	fmt.Printf("%o\n", test)
    	fmt.Printf("%O\n", test)
    	fmt.Printf("%q\n", test)
    	fmt.Printf("%x\n", test)
    	fmt.Printf("%X\n", test)
    	fmt.Printf("%U\n", test)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果:

    在这里插入图片描述

    8.1.4 浮点数和复数

    • %b 无十进制科学记数法,指数为 2 的幂,采用 strconv.FormatFloat 格式,格式为 ‘b’,例如 -123456p-78
    • %e 科学记数法,例如 -1.234456e+78
    • %E 科学记数法,例如 -1.234456E+78
    • %f 小数点但没有指数,例如 123.456
    • %F 同义词 %f
    • %g %e 代表大指数,否则使用%f。精度将在下面讨论。
    • %G %E 用于大指数,否则使用%F
    • %x 十六进制表示法(具有两个指数的十进制幂),例如 -0x1.23abcp+20
    • %X 大写十六进制表示法,例如 -0X1.23ABCP+20

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	test := 123.456
    	fmt.Printf("%b\n", test)
    	fmt.Printf("%e\n", test)
    	fmt.Printf("%E\n", test)
    	fmt.Printf("%f\n", test)
    	fmt.Printf("%F\n", test)
    	fmt.Printf("%g\n", test)
    	fmt.Printf("%G\n", test)
    	fmt.Printf("%X\n", test)
    	fmt.Printf("%X\n", test)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果:

    在这里插入图片描述

    8.1.5 字符串和字节切片

    • %s 字符串或切片的未解释字节
    • %q 使用 Go 语法安全转义的双引号字符串
    • %x base 16,小写,每个字节两个字符
    • %X base 16,大写,每个字节两个字符

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	test := "abc"
    	fmt.Printf("%s\n", test)
    	fmt.Printf("%q\n", test)
    	fmt.Printf("%x\n", test)
    	fmt.Printf("%X\n", test)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果:

    在这里插入图片描述

    8.1.6 切片和指针

    切片:(关于切片我们后面会单独总结)

    • %p 以 16 进制表示的第 0 个元素的地址,前导 0x

    指针:

    • %p 以 16 进制表示,前导 0x
    • %b、%d、%o、%x 和 %X 动词也适用于指针, 将值完全格式化为整数。

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	s := []int{1, 2, 3}
    	fmt.Printf("%p,%p,%p\n", &s[0], &s[1], &s[2])
    	p := &s[0]
    	fmt.Printf("%p,%b,%d,%o,%x,%X", p, p, p, p, p, p)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    结果:

    在这里插入图片描述

    8.1.7 默认格式复合对象及宽度精度

    %v 的默认格式是:

    • bool: %t
    • int, int8 etc.: %d
    • uint, uint8 etc.: %d, %#x if printed with %#v
    • float32, complex64, etc: %g
    • string: %s
    • chan: %p
    • pointer: %p

    对于复合对象,使用这些规则以递归方式打印元素,布局如下(这些我们后面还会单独总结):

    • struct: {field0 field1 …}
    • array, slice: [elem0 elem1 …]
    • maps: map[key1:value1 key2:value2 …]
    • pointer to above: &{}, &[], &map[]

    宽度由紧接在动词前面的可选十进制数指定。如果不存在,则使用默认宽度。精度在(可选)宽度之后由句点号后跟十进制数指定。如果不存在点号’.',则使用默认精度。没有后面数字的句点指定精度为零。示例如下:

    • %f 默认宽度,默认精度
    • %9f 宽度 9,默认精度
    • %.2f 默认宽度,精度 2
    • %9.2f 宽度 9,精度 2
    • %9.f 宽度 9,精度 0

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	f := 123.456
    	fmt.Printf("%f,%9f,%.2f,%9.2f,%9.f\n", f, f, f, f, f)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    结果:

    在这里插入图片描述

    可以看出来多出的宽度打印时会以空格填充。

    8.1.8 特殊标志

    ‘+’ 总是为数值打印一个符号;
    保证 %q (%+q) 仅 ASCII 输出
    ‘-’ 用空格填充右侧而不是左侧(左对齐字段)
    ‘#’ 替代格式:为二进制添加前导 0b (%#b), o 表示八进制(%#o),
    0x 或 0X 表示十六进制(%#x 或 %#X);为 %p (%#p) 抑制 0x;
    对于 %q,如果 strconv.CanBackquote返回 true,则打印原始(反引号)字符串;
    始终为 %e、%E、%f、%F、%g 和 %G 打印小数点;
    不要删除 %g 和 %G 的尾随零;
    如果字符对于 %U (%#U) 是可打印的,则写入例如 U+0078 ‘x’。
    ’ ’ (空格) 为省略的登录数字留一个空格 (%d);
    在字节之间放置空格以十六进制打印字符串或切片 (% x, % X)
    ‘0’ 用前导零而不是空格填充;
    对于数字,这会在符号后移动填充;
    忽略字符串、字节切片和字节数组
    示例:

    package main
    
    import "fmt"
    
    func main() {
    	a := 51
    	fmt.Printf("%+q\n", a)
    	b := 123.456
    	fmt.Printf("%9f,%9.2f,%-9.2f\n", b, b, b)
    	c := 16
    	fmt.Printf("%#b,%#o\n", c, c)
    	fmt.Printf("%09.2f", b)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    结果:

    在这里插入图片描述

    8.1.9 显式参数索引

    就是对后续传递的要打印的动词通过索引可以改变打印顺序。
    示例:

    package main
    
    import "fmt"
    
    func main() {
    	a := 51
    	b := 123.456
    	fmt.Printf("%[2]v,%[1]v\n", a, b)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    结果:

    在这里插入图片描述

    8.2 scanning

    一组类似的函数扫描格式化文本以产生值。

    • Scan、Scanf 和 Scanln 从 os.Stdin 中读取;
    • Fscan、Fscanf 和 Fscanln 从指定的 io.Reader 中读取;
    • Sscan、Sscanf 和 Sscanln 从参数字符串中读取。

    Scan、Fscan、Sscan 将输入中的换行符视为空格。
    Scanln、Fscanln 和 Sscanln 在换行处停止扫描,并要求项目后跟换行符或 EOF。
    Scanf、Fscanf 和 Sscanf 根据格式字符串解析参数,类似于 Printf。在接下来的文本中,“空格”表示除换行符之外的任何 Unicode 空白字符。
    在所有扫描函数中,紧跟换行符的回车符被视为普通换行符(\r\n 与 \n 含义相同)。

    8.2.1 Scan、Scanf、Scanln(用在获取标准输入内容)

    • Scan 扫描从标准输入读取的文本,将连续的空格分隔值存储到连续的参数中。换行符算作空格。它返回成功扫描的项目数。如果这小于参数的数量,则 err 将报告原因。
    • Scanf 扫描从标准输入读取的文本,将连续的空格分隔值存储到由格式确定的连续参数中。它返回成功扫描的项目数。如果这小于参数的数量,则 err 将报告原因。输入中的换行符必须与格式中的换行符匹配。一个例外:动词 %c 总是扫描输入中的下一个符文,即使它是空格(或制表符等)或换行符。
    • Scanln 类似于 Scan,但在换行处停止扫描,并且在最后一项之后必须有换行符或 EOF。

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	var a,b,c string
    
    	fmt.Println("请输入:")
    	_, _ = fmt.Scan(&a)
    	fmt.Println(a)
    
    	fmt.Println("请输入:")
    	fmt.Scanf("%v %v %v", &a, &b, &c)
    	fmt.Println(a, b, c)
    
    	fmt.Println("请输入:")
    	fmt.Scanln(&a)
    	fmt.Println(a)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    结果:

    在这里插入图片描述

    8.2.2 Sprint、Sprintf、Sprintln(用来格式化内容生成字符串)

    • Sprint 使用其操作数的默认格式格式化并返回结果字符串。当两者都不是字符串时,在操作数之间添加空格。
    • Sprintf 根据格式说明符格式化并返回结果字符串。
    • Sprintln 使用其操作数的默认格式格式化并返回结果字符串。操作数之间总是添加空格并附加换行符。

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	name := "张三"
    	age := 10
    	s := fmt.Sprint(name, "今年", age, "岁\n")
    	fmt.Println(s)
    
    	s2 := fmt.Sprintf("%s今年%d岁\n", name, age)
    	fmt.Println(s2)
    
    	s3 := fmt.Sprintln(name, "今年", age, "岁")
    	fmt.Println(s3)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果:

    在这里插入图片描述

    8.2.3 Sscan、Sscanf、Sscanln(用来格式化字符串解析内容)

    • Sscan 扫描参数字符串,将连续的空格分隔值存储到连续的参数中。换行符算作空格。它返回成功扫描的项目数。如果这小于参数的数量,则 err 将报告原因。
    • Sscanf 扫描参数字符串,将连续的空格分隔值存储到由格式确定的连续参数中。它返回成功解析的项目数。输入中的换行符必须与格式中的换行符匹配。
    • Sscanln 类似于 Sscan,但在换行处停止扫描,并且在最后一项之后必须有换行符或 EOF。

    示例:

    package main
    
    import "fmt"
    
    func main() {
    	s1 := "张三\n今年 10 岁"
    	var s2,s3,s4,s5 string
    	fmt.Sscan(s1, &s2, &s3, &s4, &s5)
    	fmt.Println(s2, s3, s4, s5)
    
    	s1 = "张三 今年 10 岁"
    	var name string
    	var age int
    	fmt.Sscanf(s1, "%s 今年 %d 岁", &name, &age)
    	fmt.Println(name, age)
    
    	s1 = "张三\n今年 10 岁"
    	s2,s3,s4,s5 = "","","",""
    	fmt.Sscanln(s1, &s2, &s3, &s4, &s5)
    	fmt.Println(s2, s3, s4, s5)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    结果:

    在这里插入图片描述

    9. 最后

    虽然官方不建议使用其它语言类比着学习Go,但是学习语言的过程中抽象通用的部分学习起来会快一些。常量、变量、指针可以当成简化的C来学习,基本上快捷声明和赋值的方式比较新颖,但是也更加简单了,看到指针也不会那么头疼害怕内存泄漏了,包的概念和Java、python等包的概念和使用基本类似,只是为了区分标准库的包和非标的包引入了weburl方式的包导入,这点也不是很难理解和使用。数据类型转换需要显式转换避免隐式类型转换可能引起的bug。最后的打印和扫描我目前使用的不是很多,通常我都会使用一些日志库,打印可以分级并且有颜色区分,当然你需要做一些小的程序需要打印等到用户输入以及格式化的时候这几个函数都是和C使用比较类似的,而且在此基础上还做了一些简化和优化,这里我总结的稍微细一些是为了便于有特殊地方使用可以方便来查阅,不必死记硬背。

    对于IO这里其实还有很多函数和接口我们没有展开讨论,后续我们专门总结函数和接口的时候再来举例说明,这些对于我们开发大型的软件以及某些时候自行造轮子会更加有帮助。

  • 相关阅读:
    linux 冷启动热启动
    二 linux操作系统安装git并与gitlab通过SSH方式互通
    steam搬砖项目2023年现状分析,到底还能不能做?
    iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑)
    【计算机网络笔记一】网络体系结构
    什么算泄露公司机密的行为(什么程度算公司泄密行为)
    这些年,我与Google不得不说的那些事儿
    STM32——井字棋
    爬取微博热榜并将其存储为csv文件
    业务中台(功能编排+领域模型的结合)-spider-node
  • 原文地址:https://blog.csdn.net/weixin_39510813/article/details/126517622