变量内存常量

教程地址:点击去往视频教程open in new window

1. 变量赋值

var 变量名 变量类型是变量的声明语法,其值为对应变量类型的默认值。

默认值:

  • 整型:0
  • 浮点型:0.0
  • 布尔:false
  • 字符串:空字符串
  • 指针型:nil

如果想要给变量赋值,格式为:var 变量名 变量类型 = 值

func main() {
	var a string = "mszlu"
}
1
2
3

Go是一门号称简洁的语言,简洁性体现在代码上,就是不需要写的代码,可以不用写。

var a string = "mszlu"这行代码,string这个类型是可以不用写的,Go会自动推导其类型。

func main() {
	var a = "mszlu"
}
1
2
3

同时你会发现,每次写var也挺麻烦,所以Go提供了更为简洁的写法:a := "mszlu"

只不过简短格式有一些限制:

  1. 只能用在函数内部
package main

import "fmt"

// a := "mszlu" 不能再函数外部书写
func main() {
	a := "mszlu"
	fmt.Printf("%s", a)
}

1
2
3
4
5
6
7
8
9
10

如果需要定义多个变量,但是每行都使用var比较繁琐,可以使用批量定义方式:

var (
    a int
    b string
    c []float32
)
1
2
3
4
5

2. 内存初识

变量一旦声明后,就会在内存中占用一部分空间,大家可能会想,内存是不是会很乱呢?

Golang提供了一套内存管理机制,通过对内存的有效管理来提升性能。

memory-layout-c

经典的进程视角内存布局:

  • bss和data两部分存放全局变量静态变量
  • text存放代码
  • stack栈区存放局部变量函数参数(用于静态内存分配),后进先出的数据结构
    • 栈区的数据大小在编译时就是已知的
    • 每一个线程都有一个栈区,不共享
  • heap堆区用于动态内存分配,指针,数组,大数据通常存储在堆区
    • 所有线程共享堆,是go做内存管理的主要区域

Golang中有一个语言特色的go协程(goroutine),是不同于线程的,但是类似于线程,所以也有单独的,但是goroutine的栈大小不固定,初始为2kb,动态增减。

所以go管理内存主要有两块区域:

  • 动态分配的全局堆
  • goroutine的本地堆栈

Golang喜欢在栈区分配对象,这便于垃圾回收,这是区别于很多编程语言的一点

3. 常量

如果我们定义一个了变量,并且初始化了一个值,这个值在程序运行期间,永远不会变化,这时候我们应该将其定义为常量

  • 避免魔法值
  • 常量可以执行编译期优化

例子:

package main

import "fmt"

const PI float64 = 3.1415926

func main() {
	fmt.Println(PI)
}

1
2
3
4
5
6
7
8
9
10

常量只能定义布尔型,整型,浮点型和底层类型为这些类型的(比如byte,rune)

3.1 iota

在golang中并没有枚举这种类型,但在go中有类似的方式,就是使用iota常量生成器。

  • 从定义开始,iota值从0加1

例子:

const (
	January Month = 1 + iota
	February
	March
	April
	May
	June
	July
	August
	September
	October
	November
	December
)

func main() {
	fmt.Println(December)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

奇数定义:

//go中值部分 可以写表达式
const (
  Odd1 = 2*iota + 1
  Odd2
  Odd3
)
1
2
3
4
5
6

问题:

const (
	Sunday Weekday = iota
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday = iota
)
//Saturday值是多少
func main() {
	fmt.Println(Saturday)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

4. 作用域

变量有其生效的区域,这个区域,我们称之为作用域

  • 函数内部声明的变量,为局部变量,作用域仅限于函数内部
  • 函数外部声明的变量,为全局变量,作用域在当前包有效,如果首字母大写,在所有包有效
  • {}包括的区域,称之为代码块,如果变量定义在{}内部,只在代码块内有效

例子:

func main() {
	//只在main函数内有效
	getValue()
	//不能使用 i只在getValue函数内有效
	fmt.Println(i)
}
func getValue() {
	var i = 10
	fmt.Println(i)
}
1
2
3
4
5
6
7
8
9
10
var i = 10

func main() {
	//能使用
	getValue()
	//能使用
	fmt.Println(i)
}
func getValue() {
	fmt.Println(i)
}
1
2
3
4
5
6
7
8
9
10
11
func main() {
	{
		var i = 10
		fmt.Println(i)
	}
	//不能使用
	fmt.Println(i)
}
1
2
3
4
5
6
7
8