Go 常量与变量

39 阅读5分钟

需要养成一个好习惯: 代码中配上适当的注释说明 -> 方便人家阅读源码, 也有利于自己对于代码的逻辑以及业务的梳理和理解 💪

// 当行注释
/* 
* 多行注释
*/

变量

程序: 人类编写的, 需要电脑理解操作的东西 最终是需要编译 汇编 -> 机器码(0和1) (这是机器唯一能够直接执行~)

变量: 可以理解为是一块内存的标识 其本身有自己的'名字' 就是内存地址 (以十六进制表示 如 0x123344)

(变量的实质为内存空间)

变量的含义

package main

import "main"

func main(){
	// 可以通过 fmt.Printf 来格式化输出 内存的地址 
	var num int = 20
	// % 为占位符 与后面带输出的变量数量保持一致
	fmt.Printf("num的值为:%d\n",num) // num的值为20
	fmt.Printf("num指向的内存空间为:%p\n",&num) // %p 为输出内存地址, 这里需要用取地址符 (&)
	// num指向的内存空间为:0xc0000a6058
}

变量的定义

通过关键字var 定义变量 若没有赋值, 则打印的是该数据类型的默认值

package main

import "main"

func main(){
	// var 变量名 变量的数据类型
	// 定义变量的时候, 若没有给这个变量赋值, 则打印出来的是该数据类型的默认值
	// 定义变量的时候可以直接赋值(初始化), 或者也可以 定义变量 和 赋初值(用'='赋值) 分开写
	var name string
	var age int // string 默认值 "" int 默认值 0
	// 可以同时定义多个变量 用小括号包裹
	var (
		name1 string
		age1 int
	)
	// := 自动推导, 短变量声明, 如果给这个变量赋值了, 那么会自动推导它的数据类型(基本数据类型)
	// 只能用于变量
	// 在快速声明中, 若 := 左边的变量已经存在, 那么它将无法创建 (只能用新变量, 不能先定义)
	name := "sam" // 相当于 var name string   name="sam"
}

变量的交换

思路是 用第三个变量作为中间变量

package main

import "fmt"

func main() {
	// 交换两个变量 常用思路 引入一个中间(临时)变量
	var t int 
	t = a
	a = b
	b = t
	// 在 Go 中有对应的语法糖 加持 (其实是编译器帮我们实现了上面的逻辑, 方便我们开发)
	var a int = 1
	var b int = 2
	fmt.Println("交换前 a=", a, "b=", b) // 这个是自带换行输出效果
	a, b = b, a  // 完成a,b 两个数字交换
	fmt.Println("交换后 a=", a, "b=", b)
	// 交换前 a= 1 b= 2
	// 交换后 a= 2 b= 1
}

匿名变量

_ 可以接受任何值 匿名变量 不会占用内存空间, 也不会分配内存

一般用于接收函数返回值, 可能只想要其中的一部分返回值, 这时候其他变量就需要用匿名变量 _ 来接收

全局变量 & 局部变量

Go 中若出现全局变量与局部变量的变量名重名的情况, 那么会优先考虑局部变量 (就近原则)

package main

import "fmt"
// 全局变量: 在当前go文件中生效
// 定义在 .go 文件的非函数内 (package, import 下面)
// 全局变量的定义 必须使用 var 关键字 
// 全局变量 可以定义了 不用, 但是局部变量(函数体内定义) 就不行, 因为程序执行的是函数体内
var c int
func main() {
	// 局部变量: 只在一定的范围内生效..
	// 在函数体内声明变量
	// 局部和全局变量可以重名, 有全局变量用全局变量。若有重名, 则会优先用局部变量 (就近原则)
	fmt.Printf("变量c的内存地址%p\n", &c) // 变量c的内存地址0xaf9408
	c = a + b                               // 这里用的是全局变量
	fmt.Printf("a=%d,b=%d,c=%d\n", a, b, c) // a=1,b=2,c=3
	fmt.Printf("变量c的内存地址%p\n", &c)    // 变量c的内存地址0xaf9408 
	// 可以看出这里使用全局变量 后面的使用局部变量
	b = 3
	c := a + b // 这里是创建了个名为c的局部变量 故此后使用的是局部变量
	fmt.Printf("a=%d,b=%d,c=%d\n", a, b, c) // a=1,b=3,c=4
	fmt.Printf("变量c的内存地址%p\n", &c)    // 变量c的内存地址0xc0000200e0
	b = 5
	c = a + b                               // 1+5
	fmt.Printf("a=%d,b=%d,c=%d\n", a, b, c) // a=1,b=5,c=6
	fmt.Printf("变量c的内存地址%p\n", &c)    // 变量c的内存地址0xc0000200e0
	
}

常量

常量是不能变化的量, 是一个特殊的变量. 用关键字 const 修饰

Go语言底层的约定, 实际上可以修改。若想改, 则需要跳过常量名, 直接找到其内存的地址, 来修改其值

常量定义了 可以不使用 (这样并不会爆错, 但是普通常量就不可以了, 因为其存放的地方不一样)

package main

import "fmt"

func main() {
	// 建议这么做 建议用大写字母来定义 用以区分普通变量
	// 一旦定义了 不会发生变化 (! 不能用短变量声明的方式 := )
	// 定义格式: 显式定义 const 常量名 [type] = value 
	const URL string = "www.baidu.com"
	// 隐式定义 常量的自动推导 可以省略一些基本类型 (当然可以定义多个常量)
	const U1,U2,U3 = "1","2","3"
}

特殊常量iota

可以理解为常量计数器, 当有多个常量的时候, 帮助我们计数

package main

func main() {
	const (
		a = iota // iota 默认值为0 在一组const中, 当每次定义新的常量, 其会自动 + 1
		b = iota
		c = iota
	)
	fmt.Println(a,b,c) // 0 1 2
	const (
		aa = iota
		bb = iota
		cc = iota
		d  = 0 // iota计的是在一组const 里面 定义了多少个常量~
		e  = iota
		// 若常量没有赋值, 会默认沿用上一个变量定义的赋值~
		f = 0
		g
		h = iota
	)
	const (
		i = iota // 新的一组const iota 默认为0 iota = 0
		j = 0    // j = 0 iota + 1 = 1
		k = iota // iota + 1 = 2
	)
	fmt.Println(aa, bb, cc, d, e, f, h, h) // 0 1 2 4 0 0 7
	fmt.Println(i,j,k) // 0 0 2 
}

内存地址存放

常量和变量存放的内存区域会有不同 (有 栈, 堆, 常量池) 详情见之后的笔记~