go语言学习笔记学习...
go语言教程
- 1. 介绍与安装
- Golang是什么?
- 安装
- 2. Hello World
- 写一个Hello World
- 注意事项
- 如何在VS Code中写go?
- 3. 变量与常量
- 变量的声明
- 类型推断(Type Inference):变量声明的简写
- 声明多个变量
- 常量的使用
- 4. 数据类型
- 基本数据类型
- 关于复数类型的使用
- 类型转换
- 5. 函数
- 多个返回值
- 命名返回值
- 空白符(占位符)
- 6. 包
- 什么是包,为什么使用包?
- main函数和main包
- 创建自定义包
- init函数
- 使用空白标识符
- 7. if-else语句
- 8. 循环
- 9. switch语句
- fallthrough语句
- 10. 数组和切片
1. 介绍与安装
Golang是什么?
Go 亦称为 Golang(语言叫做 Go,Golang 只是官方网站的网址),是由谷歌开发的一个开源的编译型的静态语言。Golang 的主要关注点是使得高可用性和可扩展性的 Web 应用的开发变得简便容易。(注:Go 的定位是系统编程语言,只是对 Web 开发支持较好)
安装
前往https://go.dev/dl/中下载相应平台的安装包。
Mac用户直接运行安装即可,无需关心系统环境变量PATH,安装程序会自动添加(/usr/local/go/bin
)。
Windows用户直接运行安装,但安装路径一定要改成不含空格的(例如C:\envs\go\
),无需关心系统环境变量PATH,安装程序会自动添加(C:\envs\go\bin
)。
Linux用户下载 tar.gz
文件,解压到 /usr/local
,然后将/usr/local/go/bin
添加到 PATH 环境变量中,安装完成。
安装后,在终端中执行go version
能够显示版本号则表示安装成功:
国内用户请注意,安装成功后,需要修改代理,否则后期安装go的插件会频繁失败。在终端执行:
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=auto
2. Hello World
写一个Hello World
在任意位置(比如桌面),新建一个文件hello.go
,并编辑内容为
package main //定义包名,一般定义为外层文件夹名;只有包名为main的.go文件才能直接使用go run命令执行
import "fmt" //引入用于输出的包fmt
func main() { //main函数是程序的入口;func是定义函数的关键字(类比python中的def)
fmt.Println("Hello, world~") //fmt.Println表示输出一行
} // 函数体必须包含在{}内
打开终端,执行go run hello.go
,你将看到程序输出:
下文我们在运行代码时,都使用go run
命令。
可以通过Go语言提供的
go build
或者go run
命令对Go语言程序进行编译:go build
命令可以将Go语言程序代码编译成二进制的可执行文件,但是需要我们手动运行该二进制文件;只有声明包名为main
的.go
文件才能直接使用go run
命令执行go run
命令则更加方便,它会在编译后直接运行Go语言程序,编译过程中会产生一个临时文件,但不会生成可执行文件,这个特点很适合用来调试程序。
注意事项
go语言的规则非常严格,一个是有着强类型特征,一个是有着严格的格式要求。在这里指出一些奇特的规则,以便在今后的学习中一个一个地体会到它们。
- 在go中,每一行都被认为是完整的一条语句,可以看作go会在每行末尾自动追加一个
;
,但以左括号(([{
)结尾的行不会被追加;
。前面的Hello World
代码如果写成如下格式
func main() //报错,go在执行时会把fucn main()看作一条语句,忽略了后面的函数体
{
fmt.Println("Hello, world~")
}
- 将会报错
.\hello.go:5:6: missing function body
。下文中if/for/while/switch
后面的左花括号与这里同理。所以记住一点:在go中,左括号之前不可以换行。 - go语言是强类型语言,这一点跟C语言很像,而且更严格。go语言连自动类型转换都没有,也就是说,int型和float型变量进行加法运算将会报错。更严厉地,go虽然支持类型别名,即通过
type Myint int
语句声明Myint
类型时int
类型的别名(类比C 的typedef
),别名之间也视为不同类型。在代码中体会一下:
type Myint int
var num1 int = 1
var num2 Myint = 2 //合法,go会自动推断2是不是Myint允许的值,就像2作为float32类型也没问题
num2 = num1 //报错,两个变量的类型不匹配
num2 = 1024 //合法,自动推断1024可以作为Myint型
num2 = "64" //报错,字符串无法作为Myint类型,或者说Myint类型无法保存字符串
- 通过
import
引入的模块必须被使用。这条规则着实让我确信go的开发者严重强迫症。上面的hello.go
中,如果注释掉输出语句,则会报错引入的fmt
没有被使用。另外,定义过的变量也必须被使用,常量可以只定义不使用,只会弹出警告。
如何在VS Code中写go?
打开VS Code,在拓展中搜索Go,安装该拓展。随便打开一个.go
文件,VS Code会在右下角提示:
点击“Install All”即可自动安装好依赖包。这些依赖包仅仅是VS Code编辑代码时需要的,并不影响go代码的执行。接下来,我们将在VS Code中进行编程学习。当然,并不一定用VS Code,别的都是可以的,只要Hello World能运行就没问题。
3. 变量与常量
变量的声明
go中声明变量的基本形式是var name type
。重新编辑hello.go
:
package main
import "fmt"
func main() {
var age int //声明一个变量age,类型为int,默认初始值为0
fmt.Println("The age is", age)
age = 18 //可以直接通过赋值的方式修改age的值
fmt.Println("The age has been changed to", age)
var weight int = 100 //定义变量时赋初值
fmt.Println("The weight is", weight)
}
运行结果
The age is 0
The age has been changed to 18
The weight is 100
可以注意到int型变量age
的初始值默认为0了。go中的变量都有默认零值,如字符串默认为空串,不是数字也不是字符串的默认为nil
。nil
表示零值,类似于C语言的NULL
或python的None
,可在下文中注意nil
与它们的区别。
类型推断(Type Inference):变量声明的简写
上面介绍了go声明变量的基本形式是var name type
,日常使用中是可以省略的。
- 当变量指定初值时,可以省略
type
,go能够推断出值的类型(类比python),如
var age = 19 //19将被推断为int型,这是golang默认的整型
- 变量可以省略
var
,但需要将=
改为:=
,如
age := 3.14 //定义了一个变量age,初值为3.14,类型被推断为float64,这是golang默认的浮点型
声明多个变量
var width, height int = 100, 50 // 普通写法
a, b := 1, 2 // 简写
num, str := 1, "There is a string." // 同时定义多个不同类型的变量方法1
var ( // 同时定义多个不同类型的变量方法2
name = "naveen"
age = 29
height int //没有赋值,默认为0
)
注意
=
或:=
左右两边的数量必须相同,否则报错;例如:
x, y := 9 //报错,:=左右两边变量名和值的数量不一样,无法完成赋值
:=
左边至少有一个变量名是前面不曾出现的,例如:
a, b := 1, 2 //合法
b, c := 10, 20 //合法,因为c初次被定义
a, b := 4, 5 //报错,:=左边所有变量都是已被定义的,应当改为=
常量的使用
声明常量的基本形式是const name type = value
,与变量的声明很相似,有如下规则:
const
不可以省略,type
可以省略;- 常量定义时必须赋初值,并且必须是明确的值,不能是其他变量或函数返回值;
- 常量一旦定义,值就不能修改了;
a := 1024 //定义变量a,值为1024
const b = a //报错,常量的初值必须明确,不可以来自变量
const c = 2048 //合法;这是常量定义的简写形式,2048赋值给常量c后将被默认推断为int型
可以看出,定义形式与变量的区别就是var
变成const
且必须赋初值。
事实上,所有的值都是常量(如数字1024
,字符串"This is a const string"
都是常量)。值常量都是无类型的,但在使用它时会有一个默认类型(如1024
默认推断为int
型,"Hello world"
默认推断为string
型)。值常量可以赋值给任何合理的变量类型,比如将1024
赋值给float64
型变量是完全可以的,而将"Hello"
赋值给int
型显然是出错的。
说到这里,不妨想一下,既然不同类型的变量之间不能互相赋值或运算,那常量可以吗?答案是在合理的情况下是可以的。因为常量是无类型的,在使用时会自动根据上下文推断出一个合理的类型(不一定推断成默认类型)。例如2 3.14
,3.14 / 10
,"str" "ing"
都是合法的,他们会被推断为相同的允许类型,然后运算;而3 "str"
显然是出错的,因为不可能被推断成相同类型。
4. 数据类型
基本数据类型
上一节中已经见到了int
类型。下面是 Go 支持的基本数据类型:
bool
;值只有true
和false
;- 数字类型
int8, int16, int32, int64, int
;有符号整数,int8
就是占内存为8位(一个字节);uint8, uint16, uint32, uint64, uint
;无符号整数;float32, float64
;浮点数,即小数;complex64, complex128
;复数;byte
;字节型,其实就是uint8
的别名;rune
;这是int32
的别名,后面的章节将会详述;
string
;字符串类型;
这里需要提醒,int
类型是有符号整数,但位数取决于你运行代码的系统是32位的还是64位。下面我们验证一下:
package main
import ( //import引入多个依赖时,必须使用括号并换行
"fmt"
"unsafe"
)
func main() {
var a int = 1024
fmt.Println("a =", a)
fmt.Println("Size of a is", unsafe.Sizeof(a)) //unsafe.Sizeof可以获取变量占用的字节数
fmt.Printf("Type of a is %T\n", a) //Printf用法和C的printf一致,%T可以输出变量的类型
}
运行结果
a = 1024
Size of a is 8
Type of a is int
说明在我的系统上,int
型变量占用了8个字节,也就是64个二进制位。
关于复数类型的使用
- complex64:实部和虚部都是 float32 类型的的复数。
- complex128:实部和虚部都是 float64 类型的的复数。
这里用一段代码来说明如何定义复数以及计算:
package main
import "fmt"
func main() {
c1 := complex(5, 7) //使用内建函数获取一个复数5 7i
c2 := 8 27i //直接写出复数值
c3 := c1 c2 //复数相加,实部和虚部分别相加,其他运算完全符合复数运算规则
fmt.Println("c1 c2 =", c3)
}
运行结果
c1 c2 = (13 34i)
类型转换
前面说过Go 有着非常严格的强类型特征,那如果确实有不同类型转换的需求呢?举例来说明:
a := 3 // int型
b := 3.14 // float64型
c := a b //报错,a和b的类型不一致
d := float64(a) b //合法,a被转化为float64类型,两个float64相加是合法的
直接使用type(value)
就可以将类型转换为想要的类型,但要注意,理论上这个值能够转换成目标类型才可以哦~ 比如语句float64("1024")
会报错,原因是字符串不能转换成浮点数。
那字符串与数字之间应当如何转换呢?应当使用strconv
包,转换方法如下:
// string转int;Atoi返回值第二个量表示转换失败原因,这里用_占位;在go中,_表示占位,不可当作变量
num, _ := strconv.Atoi("1024")
// string转int64;ParseInt三个参数:数字字符串,字符串的进制,返回值的位数;
// 第三个参数不能小于返回值的二进制位数,不能大于64,
// 而ParseInt返回值总是int64,故第三个参数总是填64即可;
num, err := strconv.ParseInt("1024", 10, 64)
// int转string;
str := strconv.Itoa(1024)
// int64转string;第一个参数是要转换的数字,第二个参数是数字的进制
str := strconv.FormatInt(1024, 2)
5. 函数
定义函数的基本形式:
func functionname(parametername type) returntype {
// 函数体(具体实现的功能)
}
千言万语不及一行代码,直接来看看代码怎么写:
package main
import "fmt"
//定义一个函数,用来计算两个整数的和
func add(a, b int, tip string) int {
c := a b
fmt.Println("Tip:", tip)
return c
}
func main() {
c := add(1, 2, "This is to calculate sum of two integers.")
fmt.Println("Sum of 1 and 2 is", c)
}
运行结果
Tip: This is to calculate sum of two integers.
Sum of 1 and 2 is 3
关于函数参数,可以注意到,aad
函数的参数a
并没有指定类型,那么a
的类型就会和其后面的变量的类型保持一致。所以,函数参数至少最后一个参数是必须指定类型的。
多个返回值
go
函数支持返回多个值,举例说明:
// 多个返回值需要将类型用括号括起来
func add(a, b int) (int, string) {
c := a b
return c, "This is a string"
}
命名返回值
go
函数返回值支持命名,一旦返回值命名,相当于在函数首行就声明了这些变量,举例说明:
// sum和str都是返回值,相当于在函数首行声明了这些变量;另外return无需显式返回它们了;
func add(a, b int) (sum int, str string) {
sum = a b
str = "This is a string"
return
}
空白符(占位符)
go
中单独一个下划线(_
)有特定的功能,即作为空白符(占位符),而不能像其它语言那样作为一个变量名。当我们遇到这样的场景:函数有多个返回值,而我们仅需要其中一个,不想定义变量接收其它返回值。这时候,其它返回值就可以用_
接收,而go
不会因返回值_
没被使用而报错。比如在第4节 类型转换的示例代码中,string转int的代码就用到了占位符_
。
举例说明,当我调用上面add
函数,并且只需要第一个返回值时,可以这样调用:
result, _ := add(2, 3) // result变量必须在后续被使用,而_没要求
6. 包
什么是包,为什么使用包?
到目前为止,我们一直在一个go代码文件中进行学习,文件里包含一个 main 函数和一些其它的函数。在实际应用中,整个项目不可能都塞到一个文件里,否则代码重用和维护都会很困难。而包(Package)解决了这个问题,我们可以把一些通用的功能封装在一个包中,当需要使用时,只需导入包并调用即可。
main函数和main包
我们回顾一下第2节Hello World
程序
package main //定义包名,声明当前文件属于main包
import "fmt" //引入包fmt
func main() { //main函数是程序的入口
fmt.Println("Hello, world~")
}
可以看到,我们把当前文件声明为main
包,并定义了main
函数,这个文件才能够被直接执行。
创建自定义包
在VS Code中创建如下项目:
这样我们就自定义了一个包rectangle
,并在hello.go
中导入了它。
创建自定义包时,牢记以下规则:
- 文件夹名必须作为包名,而文件名无关紧要。比如上面的项目截图,
rectangle/
下的文件都属于rectangle
包,每个文件开头都应当声明package rectangle
,不同文件内不能出现重复函数名、全局变量名。 - 以相对路径
./包名
调用包;如果直接写包名(即绝对路径),编译器将在go
标准库中寻找包,会因找不到而报错。 - 包名不要与
golang
标准库同名,否则引发冲突。比如math
标准包已存在,不要自定义包名为math
; - 包中定义的变量或函数以大写字母开头时,可以在外部调用,否则仅允许包内调用;以大写字母开头的变量或函数名,称为导出名字,意为可以让外部代码(本包之外)调用。另外,同一个包内,不同文件之间的变量或函数,不论是不是导出名字,都可以随意调用,就好像同一个包内的所有文件都在同一个文件内。
init函数
每个包可以定义一个或多个init
函数,它将在包被导入时被执行一次,一般用来初始化包内的一些变量值等。
init
函数不能有参数,不能有返回值;init
函数将在其他位置导入所属包时,被执行一次。被导入多次时,也仅执行一次;init
函数可以定义在包内任意文件内,且可以多次重复定义,编译器将按照解析顺序挨个执行;
init
函数示例:
package main
import (
"./rectangle" // 1. 导入自定义包
"fmt"
)
// 2. 包级别变量(全局变量)
var rectLen, rectWidth int = 6, 7
// 3. main 包的 init 函数
func init() {
println("rectLen=", rectLen, "rectWidth=", rectWidth)
println("main package initialized.")
}
func main() {
fmt.Printf("area of rectangle: %d\n", rectangle.Area(rectLen, rectWidth))
}
运行结果:
This is init() of package rectangle.
rectLen= 6 rectWidth= 7
main package initialized.
area of rectangle: 42
可以发现,程序的执行顺序是:
- 首先在导
rectangle
包时执行了包内的init
函数; - 然后初始化了包级别的全局变量
rectLen
,rectWidth
; - 随后执行了
main
包中定义的init
函数; - 最后执行了
main
函数。
在该代码中我们认识了一个内置函数println
,它可以直接代替fmt.Println
从而无需导入fmt
包。
使用空白标识符
在上文我们已经认识过_
,它有占位符的作用(见第5节最后),那么我们可以利用它来屏蔽【导入了包但未使用】的错误,只需在代码中随意调用一句包内的变量或函数,并用_
接收。示例;
package main
import (
"./rectangle" // 导入自定义包
)
//导入rectangle包但未使用,可用该语句屏蔽报错
var _ = rectangle.Area
func main() {
println("main")
}
另外,还有一种办法可以屏蔽报错,而且不妨碍导包时执行包内的init
函数。就是在导入包时,前面加下划线:
package main
import (
_ "./rectangle" // 导入自定义包
)
func main() {
println("main")
}
7. if-else语句
package main
import "fmt"
func main() {
var a int
// 输入整数
fmt.Scanf("%d", a) // 等同于fmt.Scan(a)
// 普通if-else语句
if a < 10 {
println("a<10")
} else if a >= 10 && a < 20 {
println("10<=a<20")
} else {
println("a>=20")
}
// 含声明的if-else语句
if b := 10; b%2 == 0 {
println("b is a even.")
}
}
- 语句体必须包含在
{}
内,且{
不可以换行,且else
必须跟在{
后面 - 条件不支持连续比较,即
10<=a<20
这样的语句是不合法的; - 含声明的
if-else
语句是go
特有的;声明的变量只能在if-else
语句块内使用
8. 循环
在go
中,只有for
一种循环,用法等同于C语言不加括号的for
循环。
package main
import "fmt"
func main() {
// 输出整数1到10之间的偶数
for i := 1; i <= 100; i {
if i%2 == 1 {
continue
}
if i > 10 {
break
}
fmt.Printf("%d ", i)
}
}
i
只能在循环体内使用;continue
和break
分别代表跳过、结束,用法与C语言/Python一致;- 两个分号所隔离的语句均可省略,但分号不可省略,一种情况除外——死循环,如下;
for {
println("dead")
}
9. switch语句
switch
是一个条件语句,可以和if-else
语句相互代替。推荐使用if-else
。
package main
func main() {
finger := 4
switch finger {
case 1:
println("Thumb")
case 2:
println("Index")
case 3:
println("Middle")
case 4:
println("Ring")
case 5:
println("Pinky")
case 6, 7, 8, 9, 10: //go switch支持多项匹配
println("6,7,8,9,10")
default: // 默认情况
println("incorrect finger number")
}
}
运行结果:
Ring
case
不允许出现重复项;go
中switch
支持多项匹配;- 所有
case
均未匹配时,执行default
; - 与
if-else
类似,switch
也支持声明变量,如下;
package main
func main() {
switch n := 10; { // 表达式被省略了
case n < 50:
println("num is less than 50")
case n < 200:
println("num is less than 200")
}
}
运行结果
num is less than 50
fallthrough语句
fallthrough
可以让命中的case
执行后不立即退出switch
,而是继续向下查找其它满足条件的case
。
package main
func main() {
switch n := 10; { // 表达式被省略了
case n < 50:
println("num is less than 50")
fallthrough // 本条case执行后,不退出switch,而是继续对比下面的case
case n < 200:
println("num is less than 200")
}
}
运行结果:
num is less than 50
num is less than 200
fallthrough
必须放在当前case
的最后面,如上。
10. 数组和切片
待更新
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhieigig
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
excel图片置于文字下方的方法
PHP中文网 06-27