前言
因为最近这段时间被拉去打一个程序开发的比赛,所以花几天学了一下go语言。
接下来打算把学习go的笔记更一下,后面再写一些关于go的安全编程的内容。
基本结构
包和函数
package main()
import (
"fmt"
)
func main(){
fmt.Println("hello world")
}
package关键字声明了代码所属的包。
在package关键字之后,代码使用了import关键字来导入自己将要用到的包。一个包可以包含任意数量的函数。
fmt包提供了用于格式化输入和输出的函数。
func关键字用于声明函数,在本例中这个函数的名字就是main。每个函数的体都需要使用大括号包围,这样go才能知道每个函数从何处开始,又在何处结束。
当我们运行一个程序的时候,它总是从 main 包的 main 函数开始运行。如果 main 不存在,编译器将报错。
每次用到被导入包的某个函数时,我们都需要在函数的名字前面加上包的名字以及一个点号作为前缀。
唯一允许的大括号放置风格
go对于大括号({})的摆放非常挑剔。左大括号 { 与func关键字位于同一行,而右大括号 } 则独占一行。这是go语言唯一允许的大括号放置风格,除此之外的其他大括号放置风格都是不被允许的。
如果用户尝试将左大括号和 func 关键字放在不同的行里面,那么go编译器将报告一个语法错误。
被美化的计算器
执行计算
注释
go语言的注释和C语言一样
单行注释
//这是单行注释
多行注释
/*
这是多行注释
*/
算术运算符
编程语言中一般通用的常规运算符。
运算符 | 功能 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 模 |
格式化输出
Println输出函数
这个函数输出的内容会在后面加一个换行,也就是\n
。
fmt.Println("hello world")
Printf格式化输出函数
fmt.Printf("number: %v",10)
Printf 接收的第一个参数总是文本,第二个参数则是表达式,而文本中包含的格式化变量%v
则会在之后被替换成表达式的值。
这个和C语言中的printf
很相似。
%v是通用类型的意思
虽然Println会自动将输出的内容推进至下一行,但是Printf和Print却不会那么做。对于后面这两个函数,用户可以通过在文本里面放置换行符\n
来将输出内容推进至下一行。
如果用户指定多个格式化变量,那么Printf函数将按顺序把它们替换成相应的值。
fmt.Printf("string: %[0]v \n number: %[1]v \n","Earth",10)
Printf除可以在句子的任意位置将格式化变量替换成指定的值之外,还能够调整文本的对齐位置。
用户可以通过指定带有宽度的格式化变量%4v,将文本的输出宽度填充至4个字符。
当宽度为正数时,空格将被填充至文本左边,而当宽度为负数时,空格将被填充至文本右边。
fmt.Printf("%10v\n",10)
//输出结果
//左边被以空格填充
10
fmt.Printf("%-10v1\n",10)
//输出结果
//右边被以空格填充
10 1
常量和变量
常量
//基本声明
const host=24
变量
//基本声明
var dis=9633
走捷径
一次声明多个变量
每一行单独声明一个变量
var dis =560
一次声明一组变量
var(
dis=560
speed=1008
)
同一行声明多个变量
var dis,speed=560,1008
需要注意的是,为了保证代码的可读性,我们在一次声明一组变量或者在同一行声明多个变量之前,应该先考虑这些变量是否相关。
增量并赋值操作符
有几种快捷方式可以让我们在赋值的同时执行以下操作。
var weight=129.0
weigth*=0.37
//等效于:weight=weight*0.37
常见的有,这些概念一般其他编程语言中也有
符号 | 功能 |
---|---|
+= | 加上符号右边的值后再赋值 |
-= | 减上符号右边的值后再赋值 |
*= | 乘以符号右边的值后再赋值 |
/= | 除以符号右边的值后再赋值 |
%= | 模运算符号右边的值后再赋值 |
自增运算符、
用户可以使用i++
执行加1操作
但是go并不支持++i
这种C语言中的操作。
var i=0
i++
i--
数字游戏
使用rand包来生成伪随机数
下列代码中,会显示一个110之间的数字。这个程序会先向Intn函数传入数字10以返回一个09的随机数,然后把这个数字加一并将其结果赋值给变量num。
传入给Intn函数的10会让rand生成从零开始的十个数字之间的随机数,即一个09的数字,所以如果我们要求是110则需加上一个1。
package main
import(
"fmt"
"math/rand"
)
func main(){
var num = rand.Intn(10)+1
fmt.Println(num)
}
虽然 rand 包的导入路径为math/rand,但是我们在调用 Intn 函数的时候只需要使用包名 rand 作为前缀即可,不需要使用整个导入路径。
循环和分支
真或假
布尔变量
注意,go中0和1不能作为布尔值使用。
//真
var z=ture
//假
var j=false
go语言标准库里面有好多函数都会返回布尔值。
例如如下代码
代码中使用了strings包的Contais函数来检查command变量是否包含单词"outsize",如果包含则返回为true,否则返回false
package main
import (
"fmt"
"strings"
)
func main(){
var command="hello world"
var exit=strings.Contains(command,"hello")
fmt.Println("结果",exit)
}
//运行结果
结果 true
比较
比较运算符
符号 | 含义 |
---|---|
== | 相等 |
!= | 不相等 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
表中的运算符既可以比较文本,又可以比较数值。
比较结果返回的是布尔值。
package main
import (
"fmt"
)
func main(){
var num=33
var age=num<10
fmt.Printf("%v是否小于10:%v(true/false)\n",num,age)
}
//运行结果
33是否小于10:false(true/false)
使用if实现分支判断
如下所示,计算机可以使用布尔值或者比较条件在if语句中选择不同的执行路径。
package main
import (
"fmt"
)
func main(){
var command="go"
if command=="no"{
fmt.Println("1")
}else if command=="go"{
fmt.Println("2")
}else{
fmt.Println("3")
}
}
//运行结果
2
这里要注意,else if 和 else 都要紧跟在前一个大括号后面,否则会报错。
else if语句和else语句都是可选的。当有多个分支路径可选时,可以重复使用else if直到满足需要为止。
逻辑运算符
基本都是编程语言通用的。
符号 | 含义 |
---|---|
|| | 逻辑或 |
&& | 逻辑与 |
! | 逻辑非 |
package main
import (
"fmt"
)
func main(){
var num=1000
var bin=num>500 || (num/2==500 && num%100==10)
if bin{
fmt.Println("真")
}else{
fmt.Println("假")
}
}
//运行结果
真
跟大多数编程语言一样,go也采用了短路逻辑:如果位于 || 运算符之前的第一个条件为真,那么位于 || 运算符之后的条件就可以被忽略,没有必要再对其进行求值。
&& 运算符的行为与 || 运算符正好相反:只有在两个条件都为真的情况下,运算结果才为真。
逻辑非运算符 ! 可以将一个布尔值从 false 变为 true,或者将 true 变为 false。
使用switch实现分支判断
go提供了 switch 语句,它可以将单个值和多个值进行比较。
package main
import (
"fmt"
)
func main(){
var command="b"
//将命令和给定的多个分支进行比较
switch command {
case "a":
fmt.Println("a")
//使用逗号分隔多个可选值
case "b","c":
fmt.Println("b/c")
//没有匹配的分支则执行
default:
fmt.Println("d")
}
}
switch 语句的另一种用法是像 if…else 那样,在每个分支中单独设置比较条件。
switch 还拥有独特的 fallthrough
关键字,它可以用于执行下一个分支的代码。
与 c 和 java 等编程语言不同,go的 switch 语句执行一个条件分支后默认是不执行下一行的
package main
import (
"fmt"
)
func main(){
var command="b"
//比较表达式放置到单独的分支里
switch command {
case "a":
fmt.Println("a")
case "b":
fmt.Println("b")
//下降至下一分支
fallthrough
case "c":
fmt.Println("c")
default:
fmt.Println("d")
}
}
使用循环实现重复执行
当需要重复执行同一段代码的时候,与一遍又一遍键入相同的代码相比,更好的办法是使用 for 关键字。
go语言是没有 while 循环关键字的,不过如下所示我们可以通过 for 关键字实现 while 循环。
后面会讲解 for 关键字如何实现类似于C语言中的那种形式。
package main
import (
"fmt"
)
func main(){
var count=10
for count>0{
fmt.Println(count)
count--
}
fmt.Println("结束")
}
//运行结果
10
9
8
7
6
5
4
3
2
1
结束
在每次迭代开始之前,表达式 count>0 都会被求值并产生一个布尔值。当该值为 false也就是 count 变量等于 0 的时候,循环就会终止。反之,如果该值为真,那么程序将继续执行循环体,也就是被 { 和 } 包围的那部分代码。
此外我们还可以通过不为 for 语句设置任何条件来产生无限循环,然后在有需要的时候通过在循环体内使用 break 语句来跳出循环。
package main
import (
"fmt"
)
func main(){
var count=10
for count>0{
fmt.Println(count)
count++
if count>20{
break
}
}
fmt.Println("结束")
}
//运行结果
10
11
12
13
14
15
16
17
18
19
20
结束
变量作用域
审视作用域
变量从声明之时开始就处于作用域当中,换句话说,变量就是从那时开始变为可见的。
只要变量仍然存在于作用域当中,程序就可以访问它。然而在作用域之外访问变量就会报错。
变量作用域的一个好处是我们可以为不同的变量复用相同的名字。因为除极少数小型程序之外,程序的变量几乎不可能不出现重名。
go 的作用域通常会随着大括号 {} 的出现而开启和结束。
在下面的代码清单中,main函数开启了一个作用域,而for循环则开启了一个嵌套作用域
package main
import (
"fmt"
"math/rand"
)
func pmain(){
var count = 0
//开启新的作用域
for count <10 {
var num = rand.Intn(10)+1
fmt.Println(num)
count++
}
//作用域结束
}
因为对 count 变量的声明用于 main 函数的函数作用域之内,所以它在 main 函数结束之前将一致可见。
而在循环结束之后访问num变量将会引发编译器报错。
简短声明
简短声明为 var 关键字提供了另一种备选语法。
以下两行代码是完全等效的:
var count = 10
count := 10
简短声明不仅仅是简化了声明语句,而且可以在无法使用 var 关键字的地方使用。
如下
未使用简短声明
var count=0
for count=10;count>0;count--{
fmt.Println(count)
}
fmt.Println(count)
在不使用简短声明的情况下,count 变量的声明必须被放置在循环之外,这意味着在循环解释之后 count 变量将继续存在于作用域。
在for循环中使用简短声明
for count := 10; count > 0; count--{
fmt.Println(count)
}
//随着循环结束,count变量将不再处于作用域之内
简短声明还可以在 if 语句中声明新的变量
if num := rand.Intn(3);num==0{
fmt.Println("Space Adventures")
}else if num ==1{
fmt.Println("SpaceX")
}else{
fmt.Println("Virgin Galactic")
}//随着if语句结束,变量将不再处于作用域之内
在switch语句中使用
switch num := rand.Intn(10);num{
case 0:
fmt.Println("Space Adventures")
case 1:
fmt.Println("SpaceX")
case 2:
fmt.Println("Virgin Galactic")
default:
fmt.Println("Random spaceline #",num)
}
作用域的范围
package main
import {
"fmt"
"math/rand"
}
//era变量在整个包都是可用的,相当于全局变量
var era="AD"
func main(){
//era变量和year变量都处于作用域之内
year := 2018
//变量era、year和month都处于作用域之内
switch month := rand.Intn(12)+1;month{
case 2:
//变量era、year、month和day都处于作用域之内
day := rand.Intn(28)+1
fmt.Println(era,year,month,day)
//上面那个day和下面的day变量是全新声明的变量,跟上面生成的同名变量并不相同
case 4,6,9,11:
day := rand.Intn(30)+1
fmt.Println(era,year,month,day)
default:
day := rand.Intn(31)+1
fmt.Println(era,year,month,day)
}//month变量和day变量不再处于作用域之内
}//year变量不再处于作用域之内
因为对 era 变量的声明位于 main 函数之外的包作用域中,所以它对于 main 包中的所有函数都是可见的。
注意:因为包作用域在声明变量时不允许使用简短声明,所以我们无法在这个作用域中使用
后言
参考书籍:Go语言趣学指南
参考课程:Go语言编程快速入门(Golang)