💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
- 推荐:「stormsha的主页」👈,持续学习,不断总结,共同进步,为了踏实,做好当下事儿~
- 专栏导航
- Python面试合集系列:Python面试题合集,剑指大厂
- GO基础学习笔记系列:记录博主学习GO语言的笔记,该笔记专栏尽量写的试用所有入门GO语言的初学者
- 数据库系列:详细总结了常用数据库 mysql 技术点,以及工作中遇到的 mysql 问题等
- 运维系列:总结好用的命令,高效开发
- 算法与数据结构系列:总结数据结构和算法,不同类型针对性训练,提升编程思维
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨
💖The Start💖点点关注,收藏不迷路💖 |
📒文章目录
- 1、概述
- 2、值类型和引用类型
- 3、打印
- 4、简洁形式,使用 := 赋值操作符
- 5、init 函数
- 6、练习
1、概述
在Go语言中,声明变量的一般形式是使用var关键字,但与许多编程语言不同的是,Go选择将变量的类型放在变量的名称之后,而不是之前。这种设计选择主要是为了避免类似于C语言中含糊不清的声明形式。
例如,在C语言中,如果你想要声明两个指针变量,你可能会写:int* a, b;
。然而,在这个例子中,只有a
是指针,而b
不是。如果你想要两个变量都是指针,你需要将它们分开书写。
相比之下,在Go中,你可以轻松地将它们都声明为指针类型,如下所示:var a, b *int
。这种语法更清晰,减少了潜在的错误和混淆。你可以在Go语言的声明语法页面上找到更多关于这个话题的讨论。
其次,这种语法能够按照从左至右的顺序阅读,使得代码更加容易理解。
var a int
var b bool
var str string
或
var (a intb boolstr string
)
在Go语言中,声明全局变量时通常使用因式分解关键字的写法。当一个变量被声明后,系统会自动为其赋予该类型的零值,例如int
为0,float32(64)
为0.0,bool
为false
,string
为空字符串,指针为nil
。在Go中,所有内存都是经过初始化的。
变量的命名遵循骆驼命名法,即第一个单词小写,每个新单词的首字母大写,例如numShips
和startDate
。但如果希望全局变量能被外部包使用,则需要将第一个单词的首字母也大写。
变量(包括常量、类型或函数)在程序中有一定的作用范围,称为作用域。如果一个变量在函数体外声明,则被认为是全局变量,可以在整个包甚至外部包(被导出后)使用,无论在哪个源文件中声明或调用。
在函数体内声明的变量称为局部变量,其作用域仅限于函数体内,参数和返回值变量也是局部变量。在if和for等控制结构中声明的变量的作用域仅限于相应的代码块内。通常情况下,局部变量的作用域可以通过代码块(用大括号括起来的部分)来判断。
虽然变量的标识符必须唯一,但可以在某个代码块的内层代码块中使用相同名称的变量,此时外部的同名变量会被暂时隐藏(结束内部代码块的执行后,隐藏的外部同名变量会重新出现,而内部同名变量会被释放),任何操作都只会影响内部代码块的局部变量。
变量可以在编译期间被赋值,赋值给变量使用运算符等号=,也可以在运行时对变量进行赋值操作。
a = 15
b = false
一般情况下,当变量a
和变量b
之间类型相同时,才能进行如 a = b
的赋值。
声明与赋值(初始化)语句也可以组合起来。
var identifier [type] = value
var a int = 15
var i = 5
var b bool = false
var str string = "Stormsha very NB!"
但是 Go 编译器的智商已经高到可以根据变量的值来自动推断其类型,这有点像 Ruby 和 Python 这类动态语言,只不过它们是在运行时进行推断,而 Go 是在编译时就已经完成推断过程。因此,你还可以使用下面的这些形式来声明及初始化变量:
var a = 15
var b = false
var str = "Stormsha very NB!"
或
var (a = 15b = falsestr = "Stormsha very NB!"numShips = 50city string
)
不过自动推断类型并不是任何时候都适用的,当你想要给变量的类型并不是自动推断出的某种类型时,你还是需要显式指定变量的类型,例如:
var n int64 = 2
然而,var a 这种语法是不正确的,因为编译器没有任何可以用于自动推断类型的依据。变量的类型也可以在运行时实现自动推断,例如:
var (GENDER = os.Getenv("GENDER")SOURCE = os.Getenv("SOURCE")GOROOT = os.Getenv("GOROOT")
)
这种写法主要用于声明包级别的全局变量,当你在函数体内声明局部变量时,应使用简短声明语法 :=,例如:
a := 1
下面这个例子展示了如何通过 runtime 包在运行时获取所在的操作系统类型,以及如何通过 os
包中的函数 os.Getenv()
来获取环境变量中的值,并保存到 string
类型的局部变量 path
中。
package mainimport ("fmt""runtime""os"
)func main() {var goos string = runtime.GOOSfmt.Printf("当前操作系统是: %s\n", goos)source := os.Getenv("SOURCE")fmt.Printf("资源位置是:%s\n", source)
}
如果你在 Windows 下运行这段代码,则会输出 The operating system is: windows
以及相应的环境变量的值;如果你在 Linux 下运行这段代码,则会输出 The operating system is: linux
以及相应的的环境变量的值。
2、值类型和引用类型
程序中所用到的内存在计算机中使用一堆箱子来表示(这也是人们在讲解它的时候的画法),这些箱子被称为“字”。根据不同的处理器以及操作系统类型,所有的字都具有 32 位(4 字节)或 64 位(8 字节)的相同长度;所有的字都使用相关的内存地址来进行表示(以十六进制数表示)。
所有像 int
、float
、bool
和 string
这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
另外,像数组和结构 (struct)这些复合类型也是值类型。
当使用等号 =
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i
的值进行了拷贝:
可以通过&i来获取变量i的内存地址,例如:0xf840000040
(每次的地址可能不同)。值类型的变量的值存储在栈中。内存地址会因机器而异,即使相同的程序在不同的机器上执行也会有不同的内存地址,因为每台机器可能有不同的存储器布局和位置分配。
更复杂的数据通常需要使用多个字,这些数据一般使用引用类型保存。引用类型的变量r1
存储的是r1
的值所在的内存地址(数字),或者内存地址中第一个字所在的位置。
内存地址被称为指针,它实际上存储在另一个字中。引用类型的指针可以指向连续内存地址中的多个字(连续内存布局),这是效率最高的存储形式;也可以将这些字分散存储在内存中,每个字都指示下一个字所在的内存地址。
当使用赋值语句r2 = r1
时,只有引用(地址)被复制。如果r1
的值被改变,则所有引用该值的指针都会指向修改后的内容,在这个例子中,r2
也会受到影响。
在Go语言中,指针属于引用类型,其他引用类型还包括slices
、maps
和channel
。被引用的变量存储在堆中,以便进行垃圾回收,且比栈拥有更大的内存空间。
3、打印
函数Printf
可以在fmt包外部使用,因为它以大写字母P开头,主要用于向控制台打印输出。通常使用的格式为:
func Printf(format string, list of variables to be printed)
格式化字符串为:"当前操作系统是: %s\n"
。
这个格式化字符串可以包含一个或多个格式化标识符,例如:%..
,其中..
可以被不同类型的标识符替换,如%s
表示字符串标识符,%v
表示使用类型的默认输出格式的标识符。这些标识符对应的值从格式化字符串后的逗号开始按顺序添加,如果参数超过1个,也需要用逗号分隔。这些占位符可以很好地控制格式化输出的文本。
函数fmt.Sprintf
与Printf
的作用完全相同,但它将格式化后的字符串作为返回值返回给调用者,因此可以在程序中使用包含变量的字符串。
函数fmt.Print
和fmt.Println
会自动使用格式化标识符%v
对字符串进行格式化,两者都会在每个参数之间自动添加空格,后者还会在字符串末尾添加换行符。例如:
fmt.Print("年龄:", 18)
将输出:"年龄:18"
。
4、简洁形式,使用 := 赋值操作符
在变量初始化时,我们可以省略变量类型,让系统自动推断。在概述中的声明语句中,都是使用var
关键字声明的变量。因此,我们可以将它们简写为a := 50
或b := false
。
a
和b
的类型(int
和bool
)将由编译器自动推断。这是使用变量的首选形式,但只能在函数体内使用,不能用于全局变量的声明和赋值。使用操作符:=
可以高效地创建一个新的变量,这被称为初始化声明。
在相同的代码块中,我们不能对相同名称的变量再次使用初始化声明,例如:a := 20 是不被允许的,编译器会提示错误 "no new variables on left side of :="
。但是,a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
如果你在定义变量a
之前使用它,会得到编译错误 "undefined: a"
。
如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a:
func main() {var a string = "NB"fmt.Println("stormsha ")
}
尝试编译这段代码将得到错误 a declared and not used
。
此外,单纯地给 a
赋值也是不够的,这个值必须被使用,所以使用 fmt.Printf("stormsha ", a)
会移除错误。
但是全局变量是允许声明但不使用。
其他的简短形式为:
同一类型的多个变量可以声明在同一行,如:
var a, b, c int
多变量可以在同一行进行赋值,如:
a, b, c = 5, 7, "abc"
上面这行假设了变量 a,b 和 c 都已经被声明,否则的话应该这样使用:
a, b, c := 5, 7, "abc"
右边的这些值以相同的顺序赋值给左边的变量,所以 a
的值是 5, b
的值是 7,c
的值是 “abc”。这被称为 并行 或 同时 赋值。如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a。
空白标识符 _
也被用于抛弃值,如值 5 在:_, b = 5, 7
中被抛弃。
_
实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val
和错误 err
是通过调用 Func1
函数同时得到:
val, err = Func1(var1)
5、init 函数
除了在全局声明中初始化,变量也可以在init()
函数中初始化。init()
函数是一种特殊的函数,不能被显式调用,而是在每个包完成初始化后自动执行,且执行优先级高于main()
函数。
每个源文件可以包含多个init()
函数,同一源文件中的init()
函数按照从上到下的顺序执行。如果一个包有多个源文件包含init()
函数,官方鼓励但不保证按照文件名的顺序调用。初始化总是以单线程的方式按包的依赖关系顺序执行。
init()
函数的一个可能用途是在程序开始执行之前对数据进行检查或修复,以确保程序状态的正确性。
// trans/init.go
package transimport "math"var Pi float64func init() {Pi = 4 * math.Atan(1) // init() function computes Pi
}
在它的 init() 函数中计算变量 Pi 的初始值。
如下示例中导入了包 trans
(需要 init.go
目录为 ./trans/init.go
)并且使用到了变量 Pi:
package mainimport ("fmt""./trans"
)var twoPi = 2 * trans.Pifunc main() {fmt.Printf("2*Pi = %g\n", twoPi) // 2*Pi = 6.283185307179586
}
init()
函数也经常被用在当一个程序开始之前调用后台执行的 goroutine
,如下面这个例子当中的 backend()
:
func init() {// setup preparationsgo backend()
}
6、练习
推断以下程序的输出,然后编译并执行它们,验证和你的推断是否一致。
练习1:
package mainvar a = "G"func main() {n()m()n()
}func n() { print(a) }func m() {a := "O"print(a)
}
练习2:
package mainvar a = "G"func main() {n()m()n()
}func n() {print(a)
}func m() {a = "O"print(a)
}
练习3:
package mainvar a stringfunc main() {a = "G"print(a)f1()
}func f1() {a := "O"print(a)f2()
}func f2() {print(a)
}
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏、分享下吧,非常感谢!👍 👍 👍
🔥🔥🔥道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙
💖The End💖点点关注,收藏不迷路💖 |