四、Go 语言变量 变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念,变量可以通过变量名访问
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字
- 变量声明 声明变量的一般形式是使用 var 关键字 var identifier type
//可以一次声明多个变量: var identifier1, identifier2 type
实例 package main
import "fmt"
func main() { var a string = "hello" fmt.Println(a)
var b, c int = 1, 2
fmt.Println(b, c)
}
//输出结果如下 hello 1 2
2.变量声明方法 第一种,指定变量类型,如果没有初始化,则变量默认为零值
var v_name v_type v_name = value
package main
import "fmt"
func main() { //声明一个变量并初始化 var a = "goroute" fmt.Println(a)
//没有初始化就为零
var b int
fmt.Println(b)
//布尔值零值为false
var c bool
fmt.Println(c)
}
//输出的结果如下 goroute 0 false
数值类型(包括complex64/128)为 0
布尔类型为 false
字符串为 ""(空字符串)
第二种,根据值自行判定变量类型
var v_name = value
package main
import "fmt"
func main() { var a = true fmt.Println(a) }
//输出结果如下 true
第三种,如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误
package main
import "fmt"
func main() { a := 1 fmt.Println(a) }
//此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句
a := 1 相等于: var a int a =1 1 2 可以将 var a string = “hello” 简写为 a := “hello” package main
import "fmt"
func main() { //相当于var a string = "hello" a := "hello" fmt.Println(a) }
- 多变量声明 类型相同多个变量, 非全局变量 package main
import "fmt"
func main() { a, b, c := 1, 2, 3 fmt.Println(a, b, c) }
因式分解法 // 这种因式分解关键字的写法一般用于声明全局变量
var( test1 int test2 float32 test3 string )
实例 package main
import "fmt"
var x, y int
var ( a int b bool )
var c, d int = 1, 2
var e, f = 666, "hello world!"
func main() { //这种不带声明格式的只能在函数体中出现 g, h := 7878, "happy" fmt.Println(x, y, a, b, c, d, e, f, g, h) }
//输出结果如下 0 0 0 false 1 2 666 hello world! 7878 happy
- 值类型和引用类型 所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:
可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)
值类型变量的值存储在堆中
内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同
更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。
这个内存地址称之为指针,这个指针实际上也被存在另外的某一个值中
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。
当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。
如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。
4.1 内存的压栈 压栈的顺序:先进后出
函数加入栈后,会执行赋值
弹栈:变量的释放
4.2 内存的堆 堆的顺序:先进先出
4.3 示例 package main
import "fmt"
//变量在内存中的存储方式
var num int = 10 var sun int = 20
func main() {
var test1 = "hello"
var test2 = "world"
fmt.Println("num=", num)
//调用内存地址使用&
fmt.Println("num的内存地址是", &num)
fmt.Println("sun的内存地址是", &sun)
//调用函数中中变量
fmt.Println(test1)
fmt.Println(test2)
}
//输出结果如下 num= 10 num的内存地址是 0x709258 sun的内存地址是 0x709260 hello world
上面代码中,在全局定义的变量是存放在栈中的,他是属于文件的
var num int = 10 var sun int = 20
而局部变量则是存放在堆中,是属于main函数的
var test1 = "hello"
var test2 = "world"
当全局变量加载完以后会加载main函数,但是main函数中定义的变量并不会被压栈,而是会被放入堆中
在test函数中是无法调用main函数中的变量的,存放位置是不同的
函数中的变量会随着函数的结束而结束它的生命周期
- 变量的作用域 在函数内部声明的变量教局部变量,生命周期仅限于函数内部
在函数外部声明的变量交全局变量,生命周期作用于整个包,如果是大写首字母,则作用域整个程序
语句块中的变量,生命周期仅限于语句块,语句块外部不能被识别
示例
package main
import "fmt"
//定义全局变量 var a string = "10"
func main() { n() m()
fmt.Println("主函数输出:", a)
}
func n() { println("n函数输出:", a) }
func m() { a := "0" fmt.Println("m的函数输出:", a) }
//输出结果如下 n函数输出: 10 m的函数输出: 0 主函数输出: 10
- 简短形式,使用 := 赋值操作符 查看变量的数据类型 package main
import "fmt"
/* := 定义即声明,无需写var,系统会自动推断数据类型,无需自定义数据类型 格式只能在函数体内 */
func main() { num := "hello" fmt.Println(num) //查看变量的数据类型 fmt.Printf("num的数据类型是:%T", num) }
//输出结果为 hello num的数据类型是:string
- 空白标识符 空白标识符_ 也被用于抛弃值,如值5在_,b=5,7中被抛弃
_实际上是一个只写变量,你不能得到它的值,这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你不需要从一个函数得到所有返回值
并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和 错误 err 是通过调用Func1 函数同时得到:val,err = Func(var1)
package main
import "fmt"
func main() { _, numb, strs := numbers() //只获取函数返回值的后两个 fmt.Println(numb, strs) }
func numbers() (int, int, string) { a, b, c := 1, 2, "str" return a, b, c }
//输出结果如下 2 str
五、常量
- 什么是常量 常量是一个简单值的标识符,使用 const 修饰,是程序在运行时,不会被修改的量
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
const identifier [type] = value
//可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型
//显式类型定义: const b string = "abc"
//隐式类型定义: const b = "abc"
//多个相同类型的声明可以简写为: const c_name1, c_name2 = value1, value2