Go简明教程
语言结构
Go语言的基础组成有以下几个部分:
包声明
引入包
函数
变量
语句&表达式
注释
以Go的Hello World为例,了解Go应用程序的各个部分
package main
//package main 定义了包名,必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包
import "fmt"
//import "fmt" 告诉Go编译器这个程序需要使用 fmt 包,fmt 包实现了格式化IO的函数
func main(){
/**
*func main()函数开始执行的函数,main函数是每一个可执行程序必须包含的
*需要注意的是 { 不能单独放在一行,否则代码在运行时会产生错误
*/
fmt.Println("Hello World!")
//一行为一个语句,结尾不需要';'
}
执行Go程序
#若我们将上述代码保存为hello.go,可以在命令行中输入下列命令运行
$ go run hello.go
Hello World!
#还可以使用go build来生成二进制文件
$ ls
hello.go
$ go build hello.go
$ ls
hello hello.go
$ ./hello
Hello World!
基础语法
行分隔符:一行代表一个语句结束
注释:单行注释 // 多行注释 /* 注释内容*/
标识符:用来命名变量、类型等程序的实体;由字母数字和下划线组成,第一个字符必须是字母或者下划线
字符串连接:Go语言字符串可以通过 + 实现
关键字
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
36 个预定义标识符
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
数据类型
布尔型:var b bool = true
数字类型
uint8 无符号8位整型(0 - 255)
uint16
uint32
uint64
int8 有符号8位整型(-128 - 127)
int6
int32
int64
float32
float64
complex64 (32位实数和虚数)
complex128
byte (类似uint8)
rune (int32)
uint (32位或64位)
int (与uint一样大小)
uintptr (无符号整型,用于存放一个指针)
变量
变量名由字母、数字、下划线组成,首个字符不能为数字
声明变量的一般形式是使用 var 关键字,可以一次声明多个变量
变量声明:在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明
指定变量类型,如果没有初始化,则变量默认为0,布尔值则为false,字符串则为空
var v_name v_type
v_name = value
根据值自行判断变量类型
var v_name = value
使用 := 声明变量
val := 1
//相当于
var val int
val = 1
多变量声明
var vname1, vname2 = v1, v2
//或
vname1, vname2 := v1, v2
//因式分解关键词的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
常量
常量是一个简单值得标识符,在程序运行时,不会被修改的量
常量的数据类型只能是布尔型、数字型、字符串型
const vname [type] = value
//显式类型定义
const b string = "abc"
//隐式类型定义
const b = "abc"
//常量还可以用作枚举
const (
Female = 1
Male = 2
)
iota 特殊常量,可以认为是一个可以被编译器修改的常量
iota 在 const 关键词出现时将被重置为0,const 中每新增一行常量声明将使 iota 计数一次
const (
a = iota
b = iota
c = iota
)
//也可以简写为
const (
a = iota
b
c
)
//a = 0, b = 1, c = 2
//一个有趣的例子
const (
i=1<<iota
j=3<<iota
k
l
)
//ota 表示从 0 开始自动加 1,所以 i=1<<0, j=3<<1(<< 表示左移的意思),即:i=1, j=6,这没问题,关键在 k 和 l,从输出结果看 k=3<<2,l=3<<3。
//注:value<<n==value*(2^n)。
运算符
算术运算符、关系运算符、逻辑运算符、赋值运算符与 C 一样
位运算符:对整数在内存中的二进制位进行操作
运算符 | 描述:假定 A = 60; B = 13; 其二进制数转换为 A = 0011 1100 B = 0000 1101 | 实例 |
& | 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 | (A & B) 结果为 12, 二进制为 0000 1100 |
| | 按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或 | (A | B) 结果为 61, 二进制为 0011 1101 |
^ | 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 | (A ^ B) 结果为 49, 二进制为 0011 0001 |
<< | 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 | A << 2 结果为 240 ,二进制为 1111 0000 |
>> | 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。 | A >> 2 结果为 15 ,二进制为 0000 1111 |
运算符优先级
优先级 | 运算符 |
5 | * / % << >> & &^ |
4 | + - | ^ |
3 | == != < <= > >= |
2 | && |
1 | || |
条件语句
if 语句
/**
* if 布尔表达式 {
* 执行语句
*}
*/
if a < 20 {
fmt.Println("a 小于 20\n")
}
switch 语句
/**
switch var1 {
case value1 :
...
case value2 :
...
default :
...
}
*/
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"
default: grade = "D"
}
//不同的 case 之间不使用 break 分隔,默认只会执行一个 case。如果想要执行多个 case,需要使用 fallthrough 关键字,也可用 break 终止。
switch {
case false:
fmt.Println("1、case 条件语句为 false")
fallthrough
case true:
fmt.Println("2、case 条件语句为 true")
fallthrough
case false:
fmt.Println("3、case 条件语句为 false")
fallthrough
case true:
fmt.Println("4、case 条件语句为 true")
case false:
fmt.Println("5、case 条件语句为 false")
fallthrough
default:
fmt.Println("6、默认 case")
}
/**
*执行结果为:
*2、case 条件语句为 true
*3、case 条件语句为 false
*4、case 条件语句为 true
*/
//switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型
switch i := x.(type) {
case nil:
fmt.Printf(" x 的类型 :%T",i)
case int:
fmt.Printf("x 是 int 型")
case float64:
fmt.Printf("x 是 float64 型")
case func(int) float64:
fmt.Printf("x 是 func(int) 型")
case bool, string:
fmt.Printf("x 是 bool 或 string 型" )
default:
fmt.Printf("未知型")
}
//执行结果为:x 的类型 :<nil>
select 语句:select 是 Go 中的一个控制结构,类似于 switch 语句。select 语句只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收。select 语句会监听所有指定的通道上的操作,一旦其中一个通道准备好就会执行相应的代码块。如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。
select {
case <- channel1:
// 执行的代码
case value := <- channel2:
// 执行的代码
case channel3 <- value:
// 执行的代码
// 你可以定义任意数量的 case
default:
// 所有通道都没有准备好,执行的代码
}
// 如果任意某个通道可以进行,它就执行,其他被忽略。如果有多个 case 都可以运行,select 会随机公平地选出一个执行,其他不会执行。否则:如果有 default 子句,则执行该语句。如果没有 default 子句,select 将阻塞,直到某个通道可以运行;Go 不会重新对 channel 或值进行求值。
循环语句
for循环
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
//无限循环
for {
sum++
}
//for-each range 循环 这种格式的循环可以对字符串、数组、切片等进行迭代输出元素
numbers := [6]int{1, 2, 3, 5}
for i,x:= range numbers {
fmt.Printf("第 %d 位 x 的值 = %d\n", i,x)
}
//for 循环的 range 格式可以省略 key 和 value
map1 := make(map[int]float32)
map1[1] = 1.0
map1[2] = 2.0
map1[3] = 3.0
map1[4] = 4.0
// 读取 key 和 value
for key, value := range map1 {
fmt.Printf("key is: %d - value is: %f\n", key, value)
}
// 读取 key
for key := range map1 {
fmt.Printf("key is: %d\n", key)
}
// 读取 value
for _, value := range map1 {
fmt.Printf("value is: %f\n", value)
}
goto 语句:Go 语言的 goto 语句可以无条件地转移到过程中指定的行
func main() {
/* 定义局部变量 */
var a int = 10
/* 循环 */
LOOP: for a < 20 {
if a == 15 {
/* 跳过迭代 */
a = a + 1
goto LOOP
}
fmt.Printf("a的值为 : %d\n", a)
a++
}
函数
Go 语言最少有个 main() 函数。
//函数定义格式
func function_name( [parameter list] ) [return_types] {
函数体
}
/**
* func:函数由 func 开始声明
* function_name:函数名称,参数列表和返回值类型构成了函数签名。
* parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参 数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
* return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
* 函数体:函数定义的代码集合
*/
函数可以返回多个值
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Google", "Runoob")
fmt.Println(a, b)
}
数组
/* 数组声明 */
var variable_name [SIZE] variable_type
var balance [10] float32
/* 数组初始化 */
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//或
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
/* 如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度 */
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
/* 如果设置了数组的长度,我们还可以通过指定下标来初始化元素 */
/* 如:将索引为 1 和 3 的元素初始化 */
balance := [5]float32{1:2.0,3:7.0}
指针
一个指针变量指向了一个值的内存地址,类似于变量和常量,在使用指针前你需要声明指针
/* var var_name *var-type */
var ip *int /* 指向整型 */
var fp *float32 /* 指向浮点型 */
如何使用指针
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
/**
a 变量的地址是: 20818a220
ip 变量储存的指针地址: 20818a220
*ip 变量的值: 20
*/
结构体
/**
定义
type struct_variable_type struct {
member definition
member definition
...
member definition
}
*/
type Books struct {
title string
author string
subject string
book_id int
}