Skip to content

Latest commit

 

History

History
213 lines (170 loc) · 5.63 KB

README.md

File metadata and controls

213 lines (170 loc) · 5.63 KB

1.1 名称

  • 函数、变量、常量、类型、语句标签和包都遵循: 字母或下划线开头,后面跟随任意数量的字符,数字和下划线,并且区分大小写
  • 若实体在函数中声明,那么实体只在函数局部有效。如果声明在函数外,将对包内所有源文件可见。
  • 实体的第一个字母的大小写决定其可见性是否跨包,大写表示对包外是可见和可访问的。

1.2 声明

package main
import "fmt"
const commonConstant = "common"

func main() {
	var a = commonConstant
	var b string // 会默认初始化为""
	var c string = "c"
	var d = "d"
	e := "e"                  // 短变量声明
	x, y := true, 2.3 // 一次定义多个变量
	fmt.Println(a)            // common
	fmt.Println(b)            // ""
	fmt.Println(c)            // c
	fmt.Println(d)            // d
	fmt.Println(e)            // e
	fmt.Println(x)
	fmt.Println(y)
}
  1. 定义变量时如果不指定初始值,那么默认初始化为零值(int为0,string为""...)

1.3 变量

1.3.1 指针

指针的值是一个变量的地址。使用指针可以在无须知道变量名称的情况下,简洁读取或更新变量的值。

package main
import "fmt"
func pointer() {
	a := "1"
	p := &a         // &z表示获取一个指向整型变量的指针,类型是整型指针(*int)
	fmt.Println(*p) // *p表示p指向的变量
	*p = "2"
	fmt.Println(a) // 2

	var x, y int
	fmt.Println(&x == &x, &x == &y, &x == nil) // true false false

	q := 1
	incr(&q)       // &q所指向的值加1,不依赖返回值就能实现更新
	fmt.Println(q) // 2
	fmt.Println(&q) // 指针地址
	fmt.Println(incr(&q)) // 3
}

func incr(p *int) int {
	*p++ // 递增p所指向的值,p本身不变(p是一个指针地址)
	return *p
}

func flagTest() {
	var str string
	flag.StringVar(&str, "s", "", "将空格替换为指定分隔符") // 传入变量的指针,不需要返回值就能改变str的值
	flag.Parse() // 解析用户传入的命令行参数
	if str != "" {
		fmt.Println(strings.Join(flag.Args(), str)) // 参数是 ./main -s / a bc 输出 a/bc
	} else {
		fmt.Println(flag.Args())
	}
}
  1. &z表示获取一个指向整型变量z的指针,类型是整型指针(*int)

  2. *p表示指针p指向的变量,*p代表一个变量。而p则代表指针,是一个0x开头的地址。

  3. 为什么使用指针? > > a. 相比java中都是值传递(对形参的修改不会影响实参),通过传递指针而不是复制整个数据结构,可以节省时间和内存。 > > b. 将变量的指针传递给函数,可以在函数内部直接修改变量的值,而不需要返回值。

1.3.2 new

通过new(type)方式创建变量的指针,常用于初始化复杂类型的指针,便于在堆上分配内存。

package main

import "fmt"

func main() {
	p := new(int)
	x := new(int)
	fmt.Println(p)      // 指针的地址
	fmt.Println(p == x) // 每new一次每次的地址都不同
	fmt.Println(*p)     // 初始为0
    p := new(Person)
	fmt.Println(p.Age) // 会被初始化为零值
}

type Person struct {
	Name string
	Age  int
}

new创建的变量是被初始化为零值的,Person中的Name和Age分别被初始化为""和0。

1.3.3 变量的生命周期

生命周期:程序执行过程中变量存在的时间段。

包级别的变量生命周期伴随着整个程序的执行时间。

局部变量的生命周期,每次执行声明语句时创建一个新的实体,一直存在到不可访问

通过可达性算法来判断变量是否能够回收。与JVM从根对象出发判断类似,将包级别的变量当前执行函数的局部变量作为源头,通过指针或其他方式的引用可以找到的变量则被视为可达。

package main

var global *int

func f() {
   var x int
   x = 1
   global = &x
}
func g() {
    y := new(int)
    *y = 1
}
  1. 针对f()函数,尽管在其返回后还能通过global访问,这种情况成为x从f中逃逸
  2. 当g()函数返回时,*y变得不可访问,可以被回收。这种情况被*y无法从g中逃逸

1.4 赋值

func assign() {
	var x int
	x = 1 // 有名称的变量
	fmt.Printf("x: %d\n", x)

	y := &x
	*y = 2 // 通过指针间接赋值
	fmt.Printf("*y: %d\n", *y)

	p := new(Person)
	p.Name = "lucy" // 结构体成员
	fmt.Printf("name: %s\n", p.Name)

	s := []int{1, 2, 3} // 数组或slice或map的元素
	fmt.Printf("slice: %d\n", s[1])

	c := 1
	c++
	c--
	fmt.Printf("c: %d\n", c)

	a := 0
	b := 1
	a, b, c = b, a, a+b // 多重赋值,即一次性赋值多个变量,并且变量支持右侧表达式推演
	fmt.Printf("a: %d, b: %d, c: %d\n", a, b, c)
}
  1. 有名称的变量赋值
  2. 通过指针间接赋值
  3. 结构体成员赋值
  4. 数组或slice或map元素赋值
  5. 多重赋值,一次赋值多个变量(左侧的变量个数需要和函数的返回值一样多,不需要的值赋值给空标识符_

1.5 包和文件

在go语言中包的作用和其他语言的库或模块作用类似,用于支持模块化、封装、编译隔离和重用

go init mod <module name> // 初始化module
go mod tidy // 整理依赖
.
├── README.md
├── src
│   └── source.go
├── go.mod
├── main.go
└── chapter1
    ├── README.md
    └── chapter1.go
package main

import (
	"go-in-action/chapter1" // <moduleName>/<folderName>
)

func main() {
	chapter1.Main()
}
  1. 大写开头的func才能够被其他go文件引用
  2. import引入的路径只需要包含文件夹,不需要包含文件名称