Go教程
Go菜鸟教程
Go语法结构
包声明
引入包
函数
变量
语句 & 表达式
注释
1 2 3 4 5 6 7 package main import "fmt" func main () { fmt.Println("Hello, World!" ) }
执行go程序go run main.go 直接编译运行go build main.go 编译成二进制文件
tips: 需要注意的是 {不能单独放在一行。
Go语法基础 Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号。如以下 GO 语句由 6 个标记组成:
fmt.Println("Hello, World!") 6 个标记是(每行一个):
1 2 3 4 5 6 1. fmt 2. . 3. Println 4. ( 5. "Hello, World!" 6. )
行分隔符 Go程序不需要以分号结尾,一行一个语句结束
注释
标识符 标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(AZ和az)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
以下是有效的标识符:
1 2 mahesh kumar abc move_name a_123 myname50 _temp j a23b9 retVal
字符串连接 Go 语言的字符串连接可以通过 + 实现:
关键字 下面列举了 Go 代码中会使用到的 25 个关键字或保留字:
Go关键字 (25个)
关键字
关键字
关键字
关键字
关键字
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
copy
false
float32
float64
imag
int
int8
int16
int32
int64
iota
len
make
new
nil
panic
print
println
real
recover
string
true
uint
uint8
uint16
uint32
uint64
uintptr
Go语言的空格 在 Go 语言中,空格通常用于分隔标识符、关键字、运算符和表达式,以提高代码的可读性。
Go 语言中变量的声明必须使用空格隔开。
格式化字符串 Go 语言中使用 fmt.Sprintf 或 fmt.Printf 格式化字符串并赋值给新串:
fmt.Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。fmt.Printf 根据格式化参数生成格式化的字符串并写入标准输出。
Sprintf 实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" ) func main () { var stockcode=123 var enddate="2020-12-31" var url="Code=%d&endDate=%s" var target_url=fmt.Sprintf(url,stockcode,enddate) fmt.Println(target_url) }
Printf 实例
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport ( "fmt" ) func main () { var stockcode=123 var enddate="2020-12-31" var url="Code=%d&endDate=%s" fmt.Printf(url,stockcode,enddate) }
输出结果都为
1 Code=123&endDate=2020-12-31
Go语言数据类型 在 Go 编程语言中,数据类型用于声明函数和变量。数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
Go 语言按类别可以分为以下几种数据类型:
基本数据类型
类别
类型
描述
示例
布尔型
bool
值只能是 true 或 false
var b bool = true
数字类型
int, float32, float64
支持整型、浮点型数字,支持复数,采用补码运算
var i int = 42
字符串类型
string
固定长度的字符序列,使用UTF-8编码
var s string = "hello"
派生类型
类型
描述
指针类型(Pointer)
存储变量内存地址的类型
数组类型
固定长度的相同类型元素的集合
结构化类型(struct)
用户自定义的复合数据类型
Channel 类型
用于goroutine之间的通信
函数类型
函数也是一种数据类型
切片类型
动态数组,可以自动扩容
接口类型(interface)
定义方法集合的抽象类型
Map 类型
键值对的集合
数字类型 整型
类型
描述
范围
uint8
无符号8位整型
0 到 255
uint16
无符号16位整型
0 到 65535
uint32
无符号32位整型
0 到 4294967295
uint64
无符号64位整型
0 到 18446744073709551615
int8
有符号8位整型
-128 到 127
int16
有符号16位整型
-32768 到 32767
int32
有符号32位整型
-2147483648 到 2147483647
int64
有符号64位整型
-9223372036854775808 到 9223372036854775807
浮点型
类型
描述
位数
float32
IEEE-754 32位浮点型数
32位
float64
IEEE-754 64位浮点型数
64位
complex64
32位实数和虚数
32位
complex128
64位实数和虚数
64位
其他类型
类型
描述
byte
类似 uint8
rune
类似 int32
uint
32位或64位无符号整型
int
与uint一样大小的有符号整型
uintptr
无符号整型,用于存放指针
Go语言变量 变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。
变量可以通过变量名访问。
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字:
var 变量名 类型 = 值
1 2 3 4 5 6 7 8 9 package mainimport "fmt" func main () { var a string = "Runoob" fmt.Println(a) var b, c int = 1 , 2 fmt.Println(b, c) }
输出
变量声明 指定变量类型 第一种,指定变量类型,如果没有初始化,则变量默认为零值 。
零值就是变量没有做初始化时系统默认设置的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func main () { var a = "RUNOOB" fmt.Println(a) var b int fmt.Println(b) var c bool fmt.Println(c) }
以上实例执行结果为:
数值类型(包括complex64/128)为 0
布尔类型为 false
字符串为 “”(空字符串)
以下几种类型为 nil:
1 2 3 4 5 6 var a *int var a []int var a map [string ] int var a chan int var a func (string ) int var a error
自动推断类型 第二种,根据值自行判定变量类型。
var v_name = value
1 2 3 4 5 6 package mainimport "fmt" func main () { var d = true fmt.Println(d) }
输出结果是:
:=编译错误1 2 var intVal int intVal :=1
直接使用下面的语句即可:
intVal := 1 相等于:
1 2 var intVal int intVal =1
可以将 var f string = "Runoob" 简写为 f := "Runoob":
1 2 3 4 5 6 7 package mainimport "fmt" func main () { f := "Runoob" fmt.Println(f) }
输出结果是:
多变量声明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var vname1, vname2, vname3 type vname1, vname2, vname3 = v1, v2, v3 var vname1, vname2, vname3 = v1, v2, v3 vname1, vname2, vname3 := v1, v2, v3 var ( vname1 v_type1 vname2 v_type2 )
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" var x, y int var ( a int b bool ) var c, d int = 1 , 2 var e, f = 123 , "hello" func main () { g, h := 123 , "hello" fmt.Println(x, y, a, b, c, d, e, f, g, h) }
输出
1 0 0 0 false 1 2 123 hello 123 hello
值类型和引用类型 值类型
所有像 int、float、bool 和 stri`ng 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值。
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝。
可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。
值类型变量通常存储在栈 中,尤其是当它们是局部变量 时。当值类型变量的值需要在函数作用域之外 使用时,Go 会将其分配到堆内存 中。
引用类型
更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。
这个内存地址称之为指针 ,这个指针实际上也被存在另外的某一个值中。
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。
当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。
如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。
简短形式,使用:=进行赋值操作 我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。
a 和 b 的类型(int 和 bool)将由编译器自动推断 。
这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值 。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
注意事项:
如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如:a := 20 就是不被允许的,编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a。
如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a:
1 2 3 4 5 6 7 8 package mainimport "fmt" func main () { var a string = "abc" fmt.Println("hello, world" ) }
尝试编译这段代码将得到错误 a declared but not used。
此外,单纯地给 a 赋值也是不够的,这个值必须被使用。
但是全局变量允许声明但是不去使用。
多变量可以在同一行进行赋值,如:
1 2 3 var a, b int var c string a, b, c = 5 , 7 , "abc"
上面这行假设了变量 a,b 和 c 都已经被声明,否则的话应该这样使用:
右边的这些值以相同的顺序赋值给左边的变量,所以 a 的值是 5, b 的值是 7,c 的值是 “abc”。
这被称为 并行 或 同时赋值。
如果你想要交换 两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同 。
空白标识符 _ 也被用于抛弃值 ,如值 5 在:_, b = 5, 7 中被抛弃。
_ 实际上是一个只写变量 ,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值 。
并行赋值 也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:val, err = Func1(var1)。
Go 语言常量 常量是一个简单值的标识符,在程序运行时,不会被修改的量 。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
1 const identifier [type ] = value
你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。
显式类型定义: const b string = "abc"
隐式类型定义: const b = "abc"
多个相同类型的声明可以简写为:
1 const c_name1, c_name2 = value1, value2
以下实例演示了常量的应用:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func main () { const LENGTH int = 10 const WIDTH int = 5 var area int const a, b, c = 1 , false , "str" area = LENGTH * WIDTH fmt.Printf("面积为 : %d" , area) println () println (a, b, c) }
以上实例运行结果为:
常量还可以用作枚举 :
1 2 3 4 5 const ( Unknown = 0 Female = 1 Male = 2 )
数字 0、1 和 2 分别代表未知性别、女性和男性。
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数 ,否则编译不过:
实例
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "unsafe" const ( a = "abc" b = len (a) c = unsafe.Sizeof(a) ) func main () { println (a, b, c) }
以上实例运行结果为:
iota iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
iota 可以被用作枚举值:
1 2 3 4 5 const ( a = iota b = iota c = iota )
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
1 2 3 4 5 const ( a = iota b c )
iota 用法 实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { const ( a = iota b c d = "ha" e f = 100 g h = iota i ) fmt.Println(a,b,c,d,e,f,g,h,i) }
以上实例运行结果为:
再看个有趣的的 iota 实例:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" const ( i=1 <<iota j=3 <<iota k l ) func main () { fmt.Println("i=" ,i) fmt.Println("j=" ,j) fmt.Println("k=" ,k) fmt.Println("l=" ,l) }
以上实例运行结果为:
iota 表示从 0 开始自动加 1,所以 i=1<<0, j=3<<1(**<<** 表示左移的意思),即:i=1, j=6,这没问题,关键在 k 和 l,从输出结果看 k=3<<2,l=3<<3。
简单表述:
i=1 :左移 0 位,不变仍为 1。
j=3 :左移 1 位,变为二进制 110 ,即 6。
k=3 :左移 2 位,变为二进制 1100 ,即 12。
l=3 :左移 3 位,变为二进制 11000 ,即 24。
注:<<n==*(2^n)
Go 语言运算符 运算符用于在程序运行时执行数学或逻辑运算。
Go 语言内置的运算符有:
算术运算符
关系运算符
逻辑运算符
位运算符
赋值运算符
其他运算符
接下来让我们来详细看看各个运算符的介绍。
算术运算符 下表列出了所有Go语言的算术运算符。假定 A 值为 10,B 值为 20。
运算符
描述
实例
+
相加
A + B 输出结果 30
-
相减
A - B 输出结果 -10
*
相乘
A * B 输出结果 200
/
相除
B / A 输出结果 2
%
求余
B % A 输出结果 0
++
自增
A++ 输出结果 11
–
自减
A– 输出结果 9
以下实例演示了各个算术运算符的用法:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package main import "fmt" func main () { var a int = 21 var b int = 10 var c int c = a + b fmt.Printf("第一行 - c 的值为 %d\n" , c ) c = a - b fmt.Printf("第二行 - c 的值为 %d\n" , c ) c = a * b fmt.Printf("第三行 - c 的值为 %d\n" , c ) c = a / b fmt.Printf("第四行 - c 的值为 %d\n" , c ) c = a % b fmt.Printf("第五行 - c 的值为 %d\n" , c ) a++ fmt.Printf("第六行 - a 的值为 %d\n" , a ) a=21 a-- fmt.Printf("第七行 - a 的值为 %d\n" , a ) }
以上实例运行结果:
1 2 3 4 5 6 7 第一行 - c 的值为 31 第二行 - c 的值为 11 第三行 - c 的值为 210 第四行 - c 的值为 2 第五行 - c 的值为 1 第六行 - a 的值为 22 第七行 - a 的值为 20
关系运算符 下表列出了所有Go语言的关系运算符。假定 A 值为 10,B 值为 20。
运算符
描述
实例
==
检查两个值是否相等,如果相等返回 True 否则返回 False。
(A == B) 为 False
!=
检查两个值是否不相等,如果不相等返回 True 否则返回 False。
(A != B) 为 True
>
检查左边值是否大于右边值,如果是返回 True 否则返回 False。
(A > B) 为 False
<
检查左边值是否小于右边值,如果是返回 True 否则返回 False。
(A < B) 为 True
>=
检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。
(A >= B) 为 False
<=
检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。
(A <= B) 为 True
以下实例演示了关系运算符的用法:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package main import "fmt" func main () { var a int = 21 var b int = 10 if ( a == b ) { fmt.Printf("第一行 - a 等于 b\n" ) } else { fmt.Printf("第一行 - a 不等于 b\n" ) } if ( a < b ) { fmt.Printf("第二行 - a 小于 b\n" ) } else { fmt.Printf("第二行 - a 不小于 b\n" ) } if ( a > b ) { fmt.Printf("第三行 - a 大于 b\n" ) } else { fmt.Printf("第三行 - a 不大于 b\n" ) } a = 5 b = 20 if ( a <= b ) { fmt.Printf("第四行 - a 小于等于 b\n" ) } if ( b >= a ) { fmt.Printf("第五行 - b 大于等于 a\n" ) } }
以上实例运行结果:
1 2 3 4 5 第一行 - a 不等于 b 第二行 - a 不小于 b 第三行 - a 大于 b 第四行 - a 小于等于 b 第五行 - b 大于等于 a
逻辑运算符 下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
运算符
描述
实例
&&
逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。
(A && B) 为 False
||
逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。
(A || B) 为 True
!
逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。
!(A && B) 为 True
以下实例演示了逻辑运算符的用法:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package main import "fmt" func main () { var a bool = true var b bool = false if ( a && b ) { fmt.Printf("第一行 - 条件为 true\n" ) } if ( a || b ) { fmt.Printf("第二行 - 条件为 true\n" ) } a = false b = true if ( a && b ) { fmt.Printf("第三行 - 条件为 true\n" ) } else { fmt.Printf("第三行 - 条件为 false\n" ) } if ( !(a && b) ) { fmt.Printf("第四行 - 条件为 true\n" ) } }
以上实例运行结果:
1 2 3 第二行 - 条件为 true 第三行 - 条件为 false 第四行 - 条件为 true
位运算符 位运算符对整数在内存中的二进制位进行操作。
下表列出了位运算符 &, |, 和 ^ 的计算:
p
q
p & q
p | q
p ^ q
0
0
0
0
0
0
1
0
1
1
1
1
1
1
0
1
0
0
1
1
假定 A = 60; B = 13; 其二进制数转换为:
1 2 3 4 5 6 7 8 9 10 11 A = 0011 1100 B = 0000 1101 ----------------- A&B = 0000 1100 A|B = 0011 1101 A^B = 0011 0001
Go 语言支持的位运算符如下表所示。假定 A 为60,B 为13:
运算符
描述
实例
&
按位与运算符”&”是双目运算符。 其功能是参与运算的两数各对应的二进位相与。
(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
以下实例演示了位运算符的用法:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package main import "fmt" func main () { var a uint = 60 var b uint = 13 var c uint = 0 c = a & b fmt.Printf("第一行 - c 的值为 %d\n" , c ) c = a | b fmt.Printf("第二行 - c 的值为 %d\n" , c ) c = a ^ b fmt.Printf("第三行 - c 的值为 %d\n" , c ) c = a << 2 fmt.Printf("第四行 - c 的值为 %d\n" , c ) c = a >> 2 fmt.Printf("第五行 - c 的值为 %d\n" , c ) }
以上实例运行结果:
1 2 3 4 5 第一行 - c 的值为 12 第二行 - c 的值为 61 第三行 - c 的值为 49 第四行 - c 的值为 240 第五行 - c 的值为 15
赋值运算符 下表列出了所有Go语言的赋值运算符。
运算符
描述
实例
=
简单的赋值运算符,将一个表达式的值赋给一个左值
C = A + B 将 A + B 表达式结果赋值给 C
+=
相加后再赋值
C += A 等于 C = C + A
-=
相减后再赋值
C -= A 等于 C = C - A
*=
相乘后再赋值
C *= A 等于 C = C * A
/=
相除后再赋值
C /= A 等于 C = C / A
%=
求余后再赋值
C %= A 等于 C = C % A
<<=
左移后赋值
C <<= 2 等于 C = C << 2
>>=
右移后赋值
C >>= 2 等于 C = C >> 2
&=
按位与后赋值
C &= 2 等于 C = C & 2
^=
按位异或后赋值
C ^= 2 等于 C = C ^ 2
|=
按位或后赋值
C |= 2 等于 C = C | 2
以下实例演示了赋值运算符的用法:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package main import "fmt" func main () { var a int = 21 var c int c = a fmt.Printf("第 1 行 - = 运算符实例,c 值为 = %d\n" , c ) c += a fmt.Printf("第 2 行 - += 运算符实例,c 值为 = %d\n" , c ) c -= a fmt.Printf("第 3 行 - -= 运算符实例,c 值为 = %d\n" , c ) c *= a fmt.Printf("第 4 行 - *= 运算符实例,c 值为 = %d\n" , c ) c /= a fmt.Printf("第 5 行 - /= 运算符实例,c 值为 = %d\n" , c ) c = 200 ; c <<= 2 fmt.Printf("第 6行 - <<= 运算符实例,c 值为 = %d\n" , c ) c >>= 2 fmt.Printf("第 7 行 - >>= 运算符实例,c 值为 = %d\n" , c ) c &= 2 fmt.Printf("第 8 行 - &= 运算符实例,c 值为 = %d\n" , c ) c ^= 2 fmt.Printf("第 9 行 - ^= 运算符实例,c 值为 = %d\n" , c ) c |= 2 fmt.Printf("第 10 行 - |= 运算符实例,c 值为 = %d\n" , c ) }
以上实例运行结果:
1 2 3 4 5 6 7 8 9 10 第 1 行 - = 运算符实例,c 值为 = 21 第 2 行 - += 运算符实例,c 值为 = 42 第 3 行 - -= 运算符实例,c 值为 = 21 第 4 行 - *= 运算符实例,c 值为 = 441 第 5 行 - /= 运算符实例,c 值为 = 21 第 6行 - <<= 运算符实例,c 值为 = 800 第 7 行 - >>= 运算符实例,c 值为 = 200 第 8 行 - &= 运算符实例,c 值为 = 0 第 9 行 - ^= 运算符实例,c 值为 = 2 第 10 行 - |= 运算符实例,c 值为 = 2
其他运算符 下表列出了Go语言的其他运算符。
运算符
描述
实例
&
返回变量存储地址
&a; 将给出变量的实际地址。
*
指针变量。
*a; 是一个指针变量
以下实例演示了其他运算符的用法:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package main import "fmt" func main () { var a int = 4 var b int32 var c float32 var ptr *int fmt.Printf("第 1 行 - a 变量类型为 = %T\n" , a ); fmt.Printf("第 2 行 - b 变量类型为 = %T\n" , b ); fmt.Printf("第 3 行 - c 变量类型为 = %T\n" , c ); ptr = &a fmt.Printf("a 的值为 %d\n" , a); fmt.Printf("*ptr 为 %d\n" , *ptr); }
以上实例运行结果:
1 2 3 4 5 第 1 行 - a 变量类型为 = int 第 2 行 - b 变量类型为 = int32 第 3 行 - c 变量类型为 = float32 a 的值为 4 *ptr 为 4
运算符优先级 有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低 :
优先级
运算符
5
* / % << >> & &^
4
+ -
3
== != < <= > >=
2
&&
1
当然,你可以通过使用括号来临时提升某个表达式的整体运算优先级。
以上实例运行结果:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package main import "fmt" func main () { var a int = 20 var b int = 10 var c int = 15 var d int = 5 var e int ; e = (a + b) * c / d; fmt.Printf("(a + b) * c / d 的值为 : %d\n" , e ); e = ((a + b) * c) / d; fmt.Printf("((a + b) * c) / d 的值为 : %d\n" , e ); e = (a + b) * (c / d); fmt.Printf("(a + b) * (c / d) 的值为 : %d\n" , e ); e = a + (b * c) / d; fmt.Printf("a + (b * c) / d 的值为 : %d\n" , e ); }
以上实例运行结果:
1 2 3 4 (a + b) * c / d 的值为 : 90 ((a + b) * c) / d 的值为 : 90 (a + b) * (c / d) 的值为 : 90 a + (b * c) / d 的值为 : 50
Go 语言条件语句 条件语句需要开发者通过指定一个或多个条件,并通过测试条件是否为 true 来决定是否执行指定语句,并在条件为 false 的情况在执行另外的语句。
Go 语言提供了以下几种条件判断语句:
语句
描述
if 语句
if 语句 由一个布尔表达式后紧跟一个或多个语句组成。
if…else 语句
if 语句 后可以使用可选的 else 语句 , else 语句中的表达式在布尔表达式为 false 时执行。
if 嵌套语句
你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。
switch 语句
switch 语句用于基于不同条件执行不同动作。
select 语句
select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
注意:Go 没有三目运算符,所以不支持 ?: 形式的条件判断。
if else 1 2 3 4 5 6 if 布尔表达式 { } else { }
switch 语句 1 2 3 4 5 6 7 8 switch var1 { case val1: ... case val2,val3: ... default : ... }
Type Switch
1 2 3 4 5 6 7 8 9 10 switch x.(type ){ case type : statement(s); case type : statement(s); default : statement(s); }
fallthrough
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" func main () { 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" ) } }
select 语句 select 是 Go 中的一个控制结构,类似于 switch 语句。
select 语句只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收。
select 语句会监听所有指定的通道上的操作,一旦其中一个通道准备好就会执行相应的代码块。
如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。
语法:
1 2 3 4 5 6 7 8 9 10 11 select { case <- channel1: case value := <- channel2: case channel3 <- value: default : }
以下描述了 select 语句的语法:
每个 case 都必须是一个通道
所有 channel 表达式都会被求值
所有被发送的表达式都会被求值
如果任意某个通道可以进行,它就执行,其他被忽略。
如果有多个 case 都可以运行,select 会随机公平地选出一个执行,其他不会执行。
否则:
如果有 default 子句,则执行该语句。
如果没有 default 子句,select 将阻塞,直到某个通道可以运行;Go 不会重新对 channel 或值进行求值。
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "fmt" "time" ) func main () { c1 := make (chan string ) c2 := make (chan string ) go func () { time.Sleep(1 * time.Second) c1 <- "one" }() go func () { time.Sleep(2 * time.Second) c2 <- "two" }() for i := 0 ; i < 2 ; i++ { select { case msg1 := <-c1: fmt.Println("received" , msg1) case msg2 := <-c2: fmt.Println("received" , msg2) } } }
以上实例运行结果:
1 2 received one received two
Go 语言循环语句 循环控制语句 break 语句 1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { for i := 0 ; i < 10 ; i++ { if i == 5 { break } fmt.Println(i) } }
使用标记
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport "fmt" func main () { fmt.Println("---- break ----" ) for i := 1 ; i <= 3 ; i++ { fmt.Printf("i: %d\n" , i) for i2 := 11 ; i2 <= 13 ; i2++ { fmt.Printf("i2: %d\n" , i2) break } } fmt.Println("---- break label ----" ) re: for i := 1 ; i <= 3 ; i++ { fmt.Printf("i: %d\n" , i) for i2 := 11 ; i2 <= 13 ; i2++ { fmt.Printf("i2: %d\n" , i2) break re } } }
在 Go 语言中,break 语句在 select 语句中的应用是相对特殊的。由于 select 语句的特性,break 语句并不能直接用于跳出 select 语句本身,因为 select 语句是非阻塞的,它会一直等待所有的通信操作都准备就绪。如果需要提前结束 select 语句的执行,可以使用 return 或者 goto 语句来达到相同的效果。
以下实例,展示了在 select 语句中使用 return 来提前结束执行的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package mainimport ( "fmt" "time" ) func process (ch chan int ) { for { select { case val := <-ch: fmt.Println("Received value:" , val) if val == 5 { return } default : fmt.Println("No value received yet." ) time.Sleep(500 * time.Millisecond) } } } func main () { ch := make (chan int ) go process(ch) time.Sleep(2 * time.Second) ch <- 1 time.Sleep(1 * time.Second) ch <- 3 time.Sleep(1 * time.Second) ch <- 5 time.Sleep(1 * time.Second) ch <- 7 time.Sleep(2 * time.Second) }
1 2 3 4 5 6 7 No value received yet. No value received yet. Received value: 1 No value received yet. Received value: 3 No value received yet. Received value: 5
通过使用 return,我们可以在 select 语句中提前终止执行,并返回到调用者的代码中。
需要注意的是,使用 return 语句会立即终止当前的函数执行,所以请根据实际需求来决定在 select 语句中使用何种方式来提前结束执行。
continue 语句 Go 语言的 continue 语句 有点像 break 语句。但是 continue 不是跳出循环,而是跳过当前循环执行下一次循环语句。
for 循环中,执行 continue 语句会触发 for 增量语句的执行。
在多重循环中,可以用标号 label 标出想 continue 的循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport "fmt" func main () { fmt.Println("---- continue ---- " ) for i := 1 ; i <= 3 ; i++ { fmt.Printf("i: %d\n" , i) for i2 := 11 ; i2 <= 13 ; i2++ { fmt.Printf("i2: %d\n" , i2) continue } } fmt.Println("---- continue label ----" ) re: for i := 1 ; i <= 3 ; i++ { fmt.Printf("i: %d\n" , i) for i2 := 11 ; i2 <= 13 ; i2++ { fmt.Printf("i2: %d\n" , i2) continue re } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ---- continue ---- i: 1 i2: 11 i2: 12 i2: 13 i: 2 i2: 11 i2: 12 i2: 13 i: 3 i2: 11 i2: 12 i2: 13 ---- continue label ---- i: 1 i2: 11 i: 2 i2: 11 i: 3 i2: 11
goto 语句 Go 语言的 goto 语句可以无条件地转移到过程中指定的行。
goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
但是,在结构化程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。
1 2 3 4 goto label;.. . label: statement;
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" 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++ } }
1 2 3 4 5 6 7 8 9 a的值为 : 10 a的值为 : 11 a的值为 : 12 a的值为 : 13 a的值为 : 14 a的值为 : 16 a的值为 : 17 a的值为 : 18 a的值为 : 19
Go 语言函数 函数是基本的代码块,用于执行一个任务。
Go 语言最少有个 main() 函数。
你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务。
函数声明告诉了编译器函数的名称,返回类型,和参数。
Go 语言标准库提供了多种可动用的内置的函数 。例如,len() 函数可以接受不同类型参数并返回该类型的长度。如果我们传入的是字符串则返回字符串的长度,如果传入的是数组,则返回数组中包含的元素个数。
函数定义 Go 语言函数定义格式如下:
1 2 3 func function_name ( [parameter list] ) [return_types] { 函数体 }
函数定义解析:
func:函数由 func 开始声明
function_name:函数名称,参数列表和返回值类型构成了函数签名。
parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
函数体:函数定义的代码集合。
实例: 以下实例为 max() 函数的代码,该函数传入两个整型参数 num1 和 num2,并返回这两个参数的最大值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func max (num1, num2 int ) int { var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
函数调用 当创建函数时,你定义了函数需要做什么,通过调用该函数来执行指定任务。
调用函数,向函数传递参数,并返回值,例如:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package main import "fmt" func main () { var a int = 100 var b int = 200 var ret int ret = max(a, b) fmt.Printf( "最大值是 : %d\n" , ret ) } func max (num1, num2 int ) int { var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
以上实例在 main() 函数中调用 max()函数,执行结果为:
函数返回多个值 Go 函数可以返回多个值,例如:
实例
1 2 3 4 5 6 7 8 9 10 11 12 package main import "fmt" func swap (x, y string ) (string , string ) { return y, x } func main () { a, b := swap("Google" , "Runoob" ) fmt.Println(a, b) }
以上实例执行结果为:
函数参数 函数如果使用参数,该变量可称为函数的形参。
形参就像定义在函数体内的局部变量。
调用函数,可以通过两种方式来传递参数:
传递类型
描述
值传递
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递
引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
默认情况下,Go 语言使用的是值传递 ,即在调用过程中不会影响到实际参数。
函数值传递 1 2 3 4 5 6 7 8 9 10 func swap (x, y int ) int { var temp int temp = x x = y y = temp return temp; }
这个swap这样的话不会改变a和b的值,因为x和y是值传递.
函数引用传递 1 2 3 4 5 6 7 func swap (x *int , y *int ) { var temp int temp = *x *x = *y *y = temp }
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport "fmt" func main () { var a int = 100 var b int = 200 fmt.Printf("交换前,a 的值 : %d\n" , a ) fmt.Printf("交换前,b 的值 : %d\n" , b ) swap(&a, &b) fmt.Printf("交换后,a 的值 : %d\n" , a ) fmt.Printf("交换后,b 的值 : %d\n" , b ) } func swap (x *int , y *int ) { var temp int temp = *x *x = *y *y = temp }
以上代码执行结果为:
1 2 3 4 交换前,a 的值 : 100 交换前,b 的值 : 200 交换后,a 的值 : 200 交换后,b 的值 : 100
函数用法
函数用法
描述
函数作为另外一个函数的实参
函数定义后可作为另外一个函数的实参数传入
闭包
闭包是匿名函数,可在动态编程中使用
方法
方法就是一个包含了接受者的函数
Go 语言函数作为实参 Go 语言可以很灵活的创建函数,并作为另外一个函数的实参。以下实例中我们在定义的函数中初始化一个变量,该函数仅仅是为了使用内置函数 math.sqrt(),实例为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport ( "fmt" "math" ) func main () { getSquareRoot := func (x float64 ) float64 { return math.Sqrt(x) } fmt.Println(getSquareRoot(9 )) }
Go 语言闭包 Go 语言支持匿名函数 ,可作为闭包。匿名函数是一个”内联”语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必声明。
匿名函数是一种没有函数名的函数,通常用于在函数内部定义函数,或者作为函数参数进行传递。
以下实例中,我们创建了函数 getSequence() ,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport "fmt" func getSequence () func () int { i:=0 return func () int { i+=1 return i } } func main () { nextNumber := getSequence() fmt.Println(nextNumber()) fmt.Println(nextNumber()) fmt.Println(nextNumber()) nextNumber1 := getSequence() fmt.Println(nextNumber1()) fmt.Println(nextNumber1()) }
以上代码执行结果为:
以下实例我们定义了多个匿名函数,并展示了如何将匿名函数赋值给变量、在函数内部使用匿名函数以及将匿名函数作为参数传递给其他函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package mainimport "fmt" func main () { add := func (a, b int ) int { return a + b } result := add(3 , 5 ) fmt.Println("3 + 5 =" , result) multiply := func (x, y int ) int { return x * y } product := multiply(4 , 6 ) fmt.Println("4 * 6 =" , product) calculate := func (operation func (int , int ) int , x, y int ) int { return operation(x, y) } sum := calculate(add, 2 , 8 ) fmt.Println("2 + 8 =" , sum) difference := calculate(func (a, b int ) int { return a - b }, 10 , 4 ) fmt.Println("10 - 4 =" , difference) }
以上代码执行结果为:
1 2 3 4 3 + 5 = 8 4 * 6 = 24 2 + 8 = 10 10 - 4 = 6
Go 语言函数方法 Go 语言中同时有函数和方法 。一个方法就是一个包含了接受者的函数 ,接受者可以是命名类型 或者结构体类型 的一个值 或者是一个指针 。所有给定类型的方法属于该类型的方法集。语法格式如下:
1 2 3 func (variable_name variable_data_type) function_name() [return_type]{ }
下面定义一个结构体类型和该类型的一个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "fmt" ) type Circle struct { radius float64 } func main () { var c1 Circle c1.radius = 10.00 fmt.Println("圆的面积 = " , c1.getArea()) } func (c Circle) getArea() float64 { return 3.14 * c.radius * c.radius }
以上代码执行结果为:
Go 语言变量作用域 作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。
Go 语言中变量可以在三个地方声明:
函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数
接下来让我们具体了解局部变量、全局变量和形式参数。
局部变量 在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
以下实例中 main() 函数使用了局部变量 a, b, c:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package main import "fmt" func main () { var a, b, c int a = 10 b = 20 c = a + b fmt.Printf ("结果: a = %d, b = %d and c = %d\n" , a, b, c) } `` ` 以上实例执行输出结果为:
结果: a = 10, b = 20 and c = 30
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 *** ## 全局变量 在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。 全局变量可以在任何函数中使用,以下实例演示了如何使用全局变量: 实例 ```go package main import "fmt" /* 声明全局变量 */ var g int func main() { /* 声明局部变量 */ var a, b int /* 初始化参数 */ a = 10 b = 20 g = a + b fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g) }
以上实例执行输出结果为:
1 结果: a = 10, b = 20 and g = 30
Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑。实例如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package main import "fmt" var g int = 20 func main () { var g int = 10 fmt.Printf ("结果: g = %d\n" , g) }
以上实例执行输出结果为:
形式参数 形式参数会作为函数的局部变量来使用。实例如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package main import "fmt" var a int = 20 ; func main () { var a int = 10 var b int = 20 var c int = 0 fmt.Printf("main()函数中 a = %d\n" , a); c = sum( a, b); fmt.Printf("main()函数中 c = %d\n" , c); } func sum (a, b int ) int { fmt.Printf("sum() 函数中 a = %d\n" , a); fmt.Printf("sum() 函数中 b = %d\n" , b); return a + b; }
以上实例执行输出结果为:
1 2 3 4 main()函数中 a = 10 sum() 函数中 a = 10 sum() 函数中 b = 20 main()函数中 c = 30
初始化局部和全局变量 不同类型的局部和全局变量默认值为:
数据类型
初始化默认值
int
0
float32
0
pointer
nil
Go 语言数组 Go 语言提供了数组类型的数据结构。
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
相对于去声明 number0, number1, …, number99 的变量,使用数组形式 numbers[0], numbers[1] …, numbers[99] 更加方便且易于扩展。
数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
声明数组 Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
1 var arrayName [size]dataType
其中,arrayName 是数组的名称,size 是数组的大小,dataType 是数组中元素的数据类型。
以下定义了数组 balance 长度为 10 类型为 float32:
初始化数组 以下演示了数组初始化:
以下实例声明一个名为 numbers 的整数数组,其大小为 5,在声明时,数组中的每个元素都会根据其数据类型进行默认初始化,对于整数类型,初始值为 0。
还可以使用初始化列表来初始化数组的元素:
1 var numbers = [5 ]int {1 , 2 , 3 , 4 , 5 }
以上代码声明一个大小为 5 的整数数组,并将其中的元素分别初始化为 1、2、3、4 和 5。
另外,还可以使用 := 简短声明语法来声明和初始化数组:
1 numbers := [5 ]int {1 , 2 , 3 , 4 , 5 }
以上代码创建一个名为 numbers 的整数数组,并将其大小设置为 5,并初始化元素的值。
注意 :在 Go 语言中,数组的大小是类型的一部分,因此不同大小的数组是不兼容的,也就是说 [5]int 和 [10]int 是不同的类型 。
以下定义了数组 balance 长度为 5 类型为 float32,并初始化数组的元素:
1 var balance = [5 ]float32 {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
我们也可以通过字面量在声明数组的同时快速初始化数组:
1 balance := [5 ]float32 {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
1 var balance = [...]float32 {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
或
1 balance := [...]float32 {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 }
如果设置了数组的长度,我们还可以通过指定下标来初始化元素:
1 2 balance := [5 ]float32 {1 :2.0 ,3 :7.0 }
初始化数组中 {} 中的元素个数不能大于 [] 中的数字。
如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:
以上实例读取了第五个元素。数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
访问数组元素 数组元素可以通过索引(位置)来读取。格式为数组名后加中括号,中括号中为索引的值。例如:
1 var salary float32 = balance[9 ]
以上实例读取了数组 balance 第 10 个元素的值。
以下演示了数组完整操作(声明、赋值、访问)的实例:
实例 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package main import "fmt" func main () { var n [10 ]int var i,j int for i = 0 ; i < 10 ; i++ { n[i] = i + 100 } for j = 0 ; j < 10 ; j++ { fmt.Printf("Element[%d] = %d\n" , j, n[j] ) } } `` ` 以上实例执行结果如下:
Element[0] = 100 Element[1] = 101 Element[2] = 102 Element[3] = 103 Element[4] = 104 Element[5] = 105 Element[6] = 106 Element[7] = 107 Element[8] = 108 Element[9] = 109
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 **实例 2** ```go package main import "fmt" func main() { var i,j,k int // 声明数组的同时快速初始化数组 balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} /* 输出数组元素 */ for i = 0; i < 5; i++ { fmt.Printf("balance[%d] = %f\n", i, balance[i] ) } balance2 := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0} /* 输出每个数组元素的值 */ for j = 0; j < 5; j++ { fmt.Printf("balance2[%d] = %f\n", j, balance2[j] ) } // 将索引为 1 和 3 的元素初始化 balance3 := [5]float32{1:2.0,3:7.0} for k = 0; k < 5; k++ { fmt.Printf("balance3[%d] = %f\n", k, balance3[k] ) } }
以上实例执行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 balance[0] = 1000.000000 balance[1] = 2.000000 balance[2] = 3.400000 balance[3] = 7.000000 balance[4] = 50.000000 balance2[0] = 1000.000000 balance2[1] = 2.000000 balance2[2] = 3.400000 balance2[3] = 7.000000 balance2[4] = 50.000000 balance3[0] = 0.000000 balance3[1] = 2.000000 balance3[2] = 0.000000 balance3[3] = 7.000000 balance3[4] = 0.000000
数组更多内容 数组对 Go 语言来说是非常重要的,以下我们将介绍数组更多的内容:
内容
描述
多维数组
Go 语言支持多维数组,最简单的多维数组是二维数组
向函数传递数组
你可以向函数传递数组参数
Go 语言多维数组 Go 语言支持多维数组,以下为常用的多维数组声明方式:
1 var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
以下实例声明了三维的整型数组:
1 var threedim [5 ][10 ][4 ]int
二维数组 二维数组是最简单的多维数组,二维数组本质上是由一维数组组成的。二维数组定义方式如下:
1 var arrayName [ x ][ y ] variable_type
variable_type 为 Go 语言的数据类型,arrayName 为数组名,二维数组可认为是一个表格,x 为行,y 为列.
二维数组中的元素可通过 a[i][j] 来访问。
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport "fmt" func main () { values := [][]int {} row1 := []int {1 , 2 , 3 } row2 := []int {4 , 5 , 6 } values = append (values, row1) values = append (values, row2) fmt.Println("Row 1" ) fmt.Println(values[0 ]) fmt.Println("Row 2" ) fmt.Println(values[1 ]) fmt.Println("第一个元素为:" ) fmt.Println(values[0 ][0 ]) }
输出:
1 2 3 4 5 6 Row 1 [1 2 3] Row 2 [4 5 6] 第一个元素为: 1
初始化二维数组 多维数组可通过大括号来初始值。以下实例为一个 3 行 4 列的二维数组:
1 2 3 4 5 a := [3 ][4 ]int { {0 , 1 , 2 , 3 } , {4 , 5 , 6 , 7 } , {8 , 9 , 10 , 11 }, }
注意 :以上代码中倒数第二行的 } 必须要有逗号,因为最后一行的 } 不能单独一行,否则会编译错误。也可以写成这样:
1 2 3 4 a := [3 ][4 ]int { {0 , 1 , 2 , 3 } , {4 , 5 , 6 , 7 } , {8 , 9 , 10 , 11 }}
以下实例初始化一个 2 行 2 列 的二维数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { sites := [2 ][2 ]string {} sites[0 ][0 ] = "Google" sites[0 ][1 ] = "Runoob" sites[1 ][0 ] = "Taobao" sites[1 ][1 ] = "Weibo" fmt.Println(sites) }
以上实例运行输出结果为:
1 [[Google Runoob] [Taobao Weibo]]
访问二维数组 二维数组通过指定坐标来访问。如数组中的行索引与列索引,例如
或
Go 语言向函数传递数组 Go 语言中的数组是值类型,因此在将数组传递给函数时,实际上是传递数组的副本。
如果你想向函数传递数组参数,你需要在函数定义时,声明形参为数组,我们可以通过以下两种方式来声明.
方式一 形参设定数组大小:
1 2 3 func myFunction (param [10]int ) { .... }
方式二
1 2 3 4 5 形参未设定数组大小: func myFunction (param []int ) { .... }
如果你想要在函数内修改原始数组,可以通过传递数组的指针来实现。
实例: 让我们看下以下实例,实例中函数接收整型数组参数,另一个参数指定了数组元素的个数,并返回平均值:
1 2 3 4 5 6 7 8 9 10 11 12 13 func getAverage (arr []int , size int ) float32 { var i int var avg, sum float32 for i = 0 ; i < size; ++i { sum += arr[i] } avg = sum / size return avg; }
接下来调用这个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport "fmt" func main () { var balance = [5 ]int {1000 , 2 , 3 , 17 , 50 } var avg float32 avg = getAverage( balance, 5 ) ; fmt.Printf( "平均值为: %f " , avg ); } func getAverage (arr [5]int , size int ) float32 { var i,sum int var avg float32 for i = 0 ; i < size;i++ { sum += arr[i] } avg = float32 (sum) / float32 (size) return avg; }
以上实例执行结果为:
以上实例中我们使用的形参并未设定数组大小。 浮点数计算输出有一定的偏差,你也可以转整型来设置精度。
1 2 3 4 5 6 7 8 9 10 package mainimport ( "fmt" ) func main () { a := 1.69 b := 1.7 c := a * b fmt.Println(c) }
设置固定精度
1 2 3 4 5 6 7 8 9 10 11 package mainimport ( "fmt" ) func main () { a := 1690 b := 1700 c := a * b fmt.Println(c) fmt.Println(float64 (c) / 1000000 ) }
如果你想要在函数内修改原始数组,可以通过传递数组的指针来实现。
以下实例演示如何向函数传递数组,函数接受一个数组和数组的指针作为参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package mainimport "fmt" func modifyArray (arr [5]int ) { for i := 0 ; i < len (arr); i++ { arr[i] = arr[i] * 2 } } func modifyArrayWithPointer (arr *[5]int ) { for i := 0 ; i < len (*arr); i++ { (*arr)[i] = (*arr)[i] * 2 } } func main () { myArray := [5 ]int {1 , 2 , 3 , 4 , 5 } fmt.Println("Original Array:" , myArray) modifyArray(myArray) fmt.Println("Array after modifyArray:" , myArray) modifyArrayWithPointer(&myArray) fmt.Println("Array after modifyArrayWithPointer:" , myArray) }
在上面的例子中,modifyArray 函数接受一个数组,并尝试修改数组的值,但在主函数中调用后,原始数组并未被修改。相反,modifyArrayWithPointer 函数接受一个数组的指针,并通过指针修改了原始数组的值。
以上实例执行输出结果为:
1 2 3 Original Array: [1 2 3 4 5] Array after modifyArray: [1 2 3 4 5] Array after modifyArrayWithPointer: [2 4 6 8 10]
Go 语言指针 Go 语言中指针是很容易学习的,Go 语言中使用指针可以更简单的执行一些任务。
接下来让我们来一步步学习 Go 语言指针。
我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。
Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。
以下实例演示了变量在内存中地址:
实例
1 2 3 4 5 6 7 8 9 package main import "fmt" func main () { var a int = 10 fmt.Printf("变量的地址: %x\n" , &a ) }
执行以上代码输出结果为:
现在我们已经了解了什么是内存地址和如何去访问它。接下来我们将具体介绍指针。
什么是指针 一个指针变量指向了一个值的内存地址。
类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:
1 2 var ip *int var fp *float32
本例中这是一个指向 int 和 float32 的指针。
如何使用指针 指针使用流程:
定义指针变量。
为指针变量赋值。
访问指针变量中指向地址的值。
在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package main import "fmt" 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 ) }
以上实例执行输出结果为:
1 2 3 a 变量的地址是: 20818a220 ip 变量储存的指针地址: 20818a220 *ip 变量的值: 20
Go 空指针 当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr。
查看以下实例:
实例
1 2 3 4 5 6 7 8 9 package main import "fmt" func main () { var ptr *int fmt.Printf("ptr 的值为 : %x\n" , ptr ) }
以上实例输出结果为:
空指针判断:
1 2 if (ptr != nil ) if (ptr == nil )
Go指针更多内容 接下来我们将为大家介绍Go语言中更多的指针应用:
内容
描述
Go 指针数组
你可以定义一个指针数组来存储地址
Go 指向指针的指针
Go 支持指向指针的指针
Go 向函数传递指针参数
通过引用或地址传参,在函数调用时可以改变其值
Go 语言指针数组 在我们了解指针数组前,先看个实例,定义了长度为 3 的整型数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" const MAX int = 3 func main () { a := []int {10 ,100 ,200 } var i int for i = 0 ; i < MAX; i++ { fmt.Printf("a[%d] = %d\n" , i, a[i] ) } }
以上实例执行结果为:
1 2 3 a[0] = 10 a[1] = 100 a[2] = 200
有一种情况,我们可能需要保存数组,这样我们就需要使用到指针。
以下声明了整型指针数组:
ptr 为整型指针数组。因此每个元素都指向了一个值。以下实例的三个整数将存储在指针数组中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" const MAX int = 3 func main () { a := []int {10 ,100 ,200 } var i int var ptr [MAX]*int ; for i = 0 ; i < MAX; i++ { ptr[i] = &a[i] } for i = 0 ; i < MAX; i++ { fmt.Printf("a[%d] = %d\n" , i,*ptr[i] ) } }
以上代码结果:
1 2 3 a[0] = 10 a[1] = 100 a[2] = 200
Go 语言指向指针的指针 如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址.
指向指针的指针变量声明格式如下:
以上指向指针的指针变量为整型。
访问指向指针的指针变量值需要使用两个 * 号,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport "fmt" func main () { var a int var ptr *int var pptr **int a = 3000 ptr = &a pptr = &ptr fmt.Printf("变量 a = %d\n" , a ) fmt.Printf("指针变量 *ptr = %d\n" , *ptr ) fmt.Printf("指向指针的指针变量 **pptr = %d\n" , **pptr) }
以上实例执行输出结果为:
1 2 3 变量 a = 3000 指针变量 *ptr = 3000 指向指针的指针变量 **pptr = 3000
Go 语言指针作为函数参数 Go 语言允许向函数传递指针,只需要在函数定义的参数上设置为指针类型即可。
以下实例演示了如何向函数传递指针,并在函数调用后修改函数内的值.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport "fmt" func main () { var a int = 100 var b int = 200 fmt.Printf("交换前 a 的值 : %d\n" , a ) fmt.Printf("交换前 b 的值 : %d\n" , b ) swap(&a, &b); fmt.Printf("交换后 a 的值 : %d\n" , a ) fmt.Printf("交换后 b 的值 : %d\n" , b ) } func swap (x *int , y *int ) { *x, *y = *y, *x } func swap (x *int , y *int ) { var temp int temp = *x *x = *y *y = temp }
以上实例执行输出结果为:
1 2 3 4 交换前 a 的值 : 100 交换前 b 的值 : 200 交换后 a 的值 : 200 交换后 b 的值 : 100
Go 语言结构体 Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:
Title :标题
Author : 作者
Subject:学科
ID:书籍ID
定义结构体 结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
1 2 3 4 5 6 type struct_variable_type struct { member definition member definition ... member definition }
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
1 variable_name := structure_variable_type {value1, value2...valuen}
或
1 variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
实例如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package main import "fmt" type Books struct { title string author string subject string book_id int } func main () { fmt.Println(Books{"Go 语言" , "www.runoob.com" , "Go 语言教程" , 6495407 }) fmt.Println(Books{title: "Go 语言" , author: "www.runoob.com" , subject: "Go 语言教程" , book_id: 6495407 }) fmt.Println(Books{title: "Go 语言" , author: "www.runoob.com" }) }
输出结果为:
1 2 3 {Go 语言 www.runoob.com Go 语言教程 6495407} {Go 语言 www.runoob.com Go 语言教程 6495407} {Go 语言 www.runoob.com 0}
访问结构体成员 如果要访问结构体成员,需要使用点号 . 操作符,格式为:
结构体.成员名
结构体类型变量使用 struct 关键字定义,实例如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package main import "fmt" type Books struct { title string author string subject string book_id int } func main () { var Book1 Books var Book2 Books Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 fmt.Printf( "Book 1 title : %s\n" , Book1.title) fmt.Printf( "Book 1 author : %s\n" , Book1.author) fmt.Printf( "Book 1 subject : %s\n" , Book1.subject) fmt.Printf( "Book 1 book_id : %d\n" , Book1.book_id) fmt.Printf( "Book 2 title : %s\n" , Book2.title) fmt.Printf( "Book 2 author : %s\n" , Book2.author) fmt.Printf( "Book 2 subject : %s\n" , Book2.subject) fmt.Printf( "Book 2 book_id : %d\n" , Book2.book_id) }
以上实例执行运行结果为:
1 2 3 4 5 6 7 8 Book 1 title : Go 语言 Book 1 author : www.runoob.com Book 1 subject : Go 语言教程 Book 1 book_id : 6495407 Book 2 title : Python 教程 Book 2 author : www.runoob.com Book 2 subject : Python 语言教程 Book 2 book_id : 6495700
结构体作为函数参数 你可以像其他数据类型一样将结构体类型作为参数传递给函数。并以以上实例的方式访问结构体变量:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package main import "fmt" type Books struct { title string author string subject string book_id int } func main () { var Book1 Books var Book2 Books Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 printBook(Book1) printBook(Book2) } func printBook ( book Books ) { fmt.Printf( "Book title : %s\n" , book.title) fmt.Printf( "Book author : %s\n" , book.author) fmt.Printf( "Book subject : %s\n" , book.subject) fmt.Printf( "Book book_id : %d\n" , book.book_id) }
以上实例执行运行结果为:
1 2 3 4 5 6 7 8 Book title : Go 语言 Book author : www.runoob.com Book subject : Go 语言教程 Book book_id : 6495407 Book title : Python 教程 Book author : www.runoob.com Book subject : Python 语言教程 Book book_id : 6495700
结构体指针 你可以定义指向结构体的指针类似于其他指针变量,格式如下:
var struct_pointer *Books
以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:
struct_pointer = &Book1
使用结构体指针访问结构体成员,使用 “.” 操作符:
struct_pointer.title
接下来让我们使用结构体指针重写以上实例,代码如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package main import "fmt" type Books struct { title string author string subject string book_id int } func main () { var Book1 Books var Book2 Books Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 printBook(&Book1) printBook(&Book2) } func printBook ( book *Books ) { fmt.Printf( "Book title : %s\n" , book.title) fmt.Printf( "Book author : %s\n" , book.author) fmt.Printf( "Book subject : %s\n" , book.subject) fmt.Printf( "Book book_id : %d\n" , book.book_id) }
以上实例执行运行结果为:
1 2 3 4 5 6 7 8 Book title : Go 语言 Book author : www.runoob.com Book subject : Go 语言教程 Book book_id : 6495407 Book title : Python 教程 Book author : www.runoob.com Book subject : Python 语言教程 Book book_id : 6495700
Go 语言切片(Slice) Go 语言切片是对数组的抽象。
Go 数组的长度不可改变 ,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
定义切片 你可以声明一个未指定大小 的数组来定义切片:
切片不需要说明长度。
或使用 make() 函数来创建切片:
1 var slice1 []type = make ([]type , len )
也可以简写为
1 slice1 := make ([]type , len )
也可以指定容量,其中 capacity 为可选参数。
1 make ([]T, length, capacity)
这里 len 是数组的长度并且也是切片的初始长度。
切片初始化
直接初始化切片,[] 表示是切片类型,**{1,2,3}** 初始化值依次是 1,2,3,其 cap=len=3 。
初始化切片 s ,是数组 arr 的引用。 ps:左闭右开区间
1 s := arr[startIndex:endIndex]
将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。
默认 endIndex 时将表示一直到arr的最后一个元素。
默认 startIndex 时将表示从 arr 的第一个元素开始。
1 s1 := s[startIndex:endIndex]
通过切片 s 初始化切片 s1。
通过内置函数 make() 初始化切片s ,**[]int** 标识为其元素类型为 int 的切片。
len() 和 cap() 函数 切片是可索引的,并且可以由 len() 方法获取长度。
切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
以下为具体实例:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 package main import "fmt" func main () { var numbers = make ([]int ,3 ,5 ) printSlice(numbers) } func printSlice (x []int ) { fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) }
以上实例运行输出结果为:
1 len=3 cap=5 slice=[0 0 0]
空(nil)切片 一个切片在未初始化之前默认为 nil,长度为 0,实例如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package main import "fmt" func main () { var numbers []int printSlice(numbers) if (numbers == nil ){ fmt.Printf("切片是空的" ) } } func printSlice (x []int ) { fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) } 以上实例运行输出结果为: len =0 cap =0 slice=[]切片是空的
切片截取 可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound],实例如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package main import "fmt" func main () { numbers := []int {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 } printSlice(numbers) fmt.Println("numbers ==" , numbers) fmt.Println("numbers[1:4] ==" , numbers[1 :4 ]) fmt.Println("numbers[:3] ==" , numbers[:3 ]) fmt.Println("numbers[4:] ==" , numbers[4 :]) numbers1 := make ([]int ,0 ,5 ) printSlice(numbers1) number2 := numbers[:2 ] printSlice(number2) number3 := numbers[2 :5 ] printSlice(number3) } func printSlice (x []int ) { fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) }
执行以上代码输出结果为:
1 2 3 4 5 6 7 8 len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8] numbers == [0 1 2 3 4 5 6 7 8] numbers[1:4] == [1 2 3] numbers[:3] == [0 1 2] numbers[4:] == [4 5 6 7 8] len=0 cap=5 slice=[] len=2 cap=9 slice=[0 1] len=3 cap=7 slice=[2 3 4]
append() 和 copy() 函数 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package main import "fmt" func main () { var numbers []int printSlice(numbers) numbers = append (numbers, 0 ) printSlice(numbers) numbers = append (numbers, 1 ) printSlice(numbers) numbers = append (numbers, 2 ,3 ,4 ) printSlice(numbers) numbers1 := make ([]int , len (numbers), (cap (numbers))*2 ) copy (numbers1,numbers) printSlice(numbers1) } func printSlice (x []int ) { fmt.Printf("len=%d cap=%d slice=%v\n" ,len (x),cap (x),x) }
以上代码执行输出结果为:
1 2 3 4 5 len=0 cap=0 slice=[] len=1 cap=1 slice=[0] len=2 cap=2 slice=[0 1] len=5 cap=6 slice=[0 1 2 3 4] len=5 cap=12 slice=[0 1 2 3 4]
Go 语言范围(Range) Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。
for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:
1 2 3 for key, value := range oldMap { newMap[key] = value }
以上代码中的 key 和 value 是可以省略。
如果只想读取 key,格式如下:
或者这样:
1 for key, _ := range oldMap
如果只想读取 value,格式如下:
1 for _, value := range oldMap
数组和切片 遍历简单的切片,2%d 的结果为 2 对应的次方数:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package main import "fmt" var pow = []int {1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 } func main () { for i, v := range pow { fmt.Printf("2**%d = %d\n" , i, v) } }
以上实例运行输出结果为:
1 2 3 4 5 6 7 8 2**0 = 1 2**1 = 2 2**2 = 4 2**3 = 8 2**4 = 16 2**5 = 32 2**6 = 64 2**7 = 128
字符串 range 迭代字符串时,返回每个字符的字节索引 和 Unicode 代码点(rune)。注意:对于多字节字符(如中文),字节索引和字符位置可能不同。
实例
1 2 3 4 5 6 7 8 9 package main import "fmt" func main () { for i, c := range "hello" { fmt.Printf("index: %d, char: %c\n" , i, c) } }
以上实例运行输出结果为:
1 2 3 4 5 index: 0, char: h index: 1, char: e index: 2, char: l index: 3, char: l index: 4, char: o
映射(Map) for 循环的 range 格式可以省略 key 和 value,如下实例:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package main import "fmt" func main () { map1 := make (map [int ]float32 ) map1[1 ] = 1.0 map1[2 ] = 2.0 map1[3 ] = 3.0 map1[4 ] = 4.0 for key, value := range map1 { fmt.Printf("key is: %d - value is: %f\n" , key, value) } for key := range map1 { fmt.Printf("key is: %d\n" , key) } for _, value := range map1 { fmt.Printf("value is: %f\n" , value) } }
以上实例运行输出结果为:
1 2 3 4 5 6 7 8 9 10 11 12 key is: 4 - value is: 4.000000 key is: 1 - value is: 1.000000 key is: 2 - value is: 2.000000 key is: 3 - value is: 3.000000 key is: 1 key is: 2 key is: 3 key is: 4 value is: 1.000000 value is: 2.000000 value is: 3.000000 value is: 4.000000
通道(Channel) range 遍历从通道接收的值,直到通道关闭。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package main import "fmt" func main () { ch := make (chan int , 2 ) ch <\- 1 ch <\- 2 close (ch) for v := range ch { fmt.Println(v) } }
以上实例运行输出结果为:
忽略值 在遍历时可以使用 _ 来忽略索引或值。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package main import "fmt" func main () { nums := []int {2 , 3 , 4 } for _, num := range nums { fmt.Println("value:" , num) } for i := range nums { fmt.Println("index:" , i) } }
以上实例运行输出结果为:
1 2 3 4 5 6 value: 2 value: 3 value: 4 index: 0 index: 1 index: 2
其他 range 遍历其他数据结构:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package main import "fmt" func main () { nums := []int {2 , 3 , 4 } sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:" , sum) for i, num := range nums { if num == 3 { fmt.Println("index:" , i) } } kvs := map [string ]string {"a" : "apple" , "b" : "banana" } for k, v := range kvs { fmt.Printf("%s -> %s\n" , k, v) } for i, c := range "go" { fmt.Println(i, c) } }
以上实例运行输出结果为:
1 2 3 4 5 6 sum: 9 index: 1 a -> apple b -> banana 0 103 1 111
Go 语言Map(集合) Map 是一种无序的键值对的集合。
Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。
在获取 Map 的值时,如果键不存在,返回该类型的零值 ,例如 int 类型的零值是 0,string 类型的零值是 “”。
Map 是引用类型 ,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。
定义 Map 可以使用内建函数 make 或使用 map 关键字来定义 Map:
1 2 map_variable := make (map [KeyType]ValueType, initialCapacity)
其中 KeyType 是键的类型,ValueType 是值的类型,initialCapacity 是可选的参数,用于指定 Map 的初始容量。Map 的容量是指 Map 中可以保存的键值对的数量,当 Map 中的键值对数量达到容量时,Map 会自动扩容。如果不指定 initialCapacity,Go 语言会根据实际情况选择一个合适的值。
实例
1 2 3 4 5 m := make (map [string ]int ) m := make (map [string ]int , 10 )
也可以使用字面量创建 Map:
1 2 3 4 5 6 m := map [string ]int { "apple" : 1 , "banana" : 2 , "orange" : 3 , }
获取元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 v1 := m["apple" ] v2, ok := m["pear" ] if ok { fmt.Println("键存在,值为:" , v2) } else { fmt.Println("键不存在" ) } if value, ok := m["apple" ]; ok { fmt.Println("apple 存在,值为:" , value) } else { fmt.Println("apple 不存在" ) } if _, ok := m["banana" ]; ok { fmt.Println("banana 存在" ) }
修改元素:
获取 Map 的长度:
遍历 Map:
1 2 3 4 for k, v := range m { fmt.Printf("key=%s, value=%d\n" , k, v) }
删除元素:
下面实例演示了创建和使用map:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package main import "fmt" func main () { var siteMap map [string ]string siteMap = make (map [string ]string ) siteMap [ "Google" ] = "谷歌" siteMap [ "Runoob" ] = "菜鸟教程" siteMap [ "Baidu" ] = "百度" siteMap [ "Wiki" ] = "维基百科" for site := range siteMap { fmt.Println(site, "首都是" , siteMap [site]) } name, ok := siteMap [ "Facebook" ] if (ok) { fmt.Println("Facebook 的 站点是" , name) } else { fmt.Println("Facebook 站点不存在" ) } }
以上实例运行结果为:
1 2 3 4 5 Wiki 首都是 维基百科 Google 首都是 谷歌 Runoob 首都是 菜鸟教程 Baidu 首都是 百度 Facebook 站点不存在
delete() 函数 delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下:
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package main import "fmt" func main () { countryCapitalMap := map [string ]string {"France" : "Paris" , "Italy" : "Rome" , "Japan" : "Tokyo" , "India" : "New delhi" } fmt.Println("原始地图" ) for country := range countryCapitalMap { fmt.Println(country, "首都是" , countryCapitalMap [ country ]) } delete (countryCapitalMap, "France" ) fmt.Println("法国条目被删除" ) fmt.Println("删除元素后地图" ) for country := range countryCapitalMap { fmt.Println(country, "首都是" , countryCapitalMap [ country ]) } }
以上实例运行结果为:
1 2 3 4 5 6 7 8 9 10 原始地图 India 首都是 New delhi France 首都是 Paris Italy 首都是 Rome Japan 首都是 Tokyo 法国条目被删除 删除元素后地图 Italy 首都是 Rome Japan 首都是 Tokyo India 首都是 New delhi
Go 语言递归函数 递归是一种函数直接或间接调用自身的编程技术。
递归函数通常包含两个部分:
基准条件(Base Case) :这是递归的终止条件,防止函数无限调用自身。
递归条件(Recursive Case) :这是函数调用自身的部分,用于将问题分解为更小的子问题。
在 Go 语言中,递归的使用与其他语言类似,但需要注意 Go 的一些特性。
语法格式如下:
1 2 3 4 5 6 7 func recursion () { recursion() } func main () { recursion() }
Go 语言支持递归,但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。
递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。
阶乘 阶乘是一个正整数的乘积,表示为 n!。例如:
5! = 5 * 4 * 3 * 2 * 1 = 120
以下实例通过 Go 语言的递归函数实例阶乘:
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package main import "fmt" func factorial (n int ) int { if n == 0 { return 1 } return n * factorial(n-1 ) } func main () { fmt.Println(factorial(5 )) }
代码解释
基准条件 :当 n 等于 0 时,函数返回 1,因为 0! 定义为 1。
递归条件 :函数返回 n 乘以 factorial(n-1) 的结果,逐步将问题分解为更小的子问题。
以上实例执行输出结果为:
120
斐波那契数列 以下实例通过 Go 语言的递归函数实现斐波那契数列:
实例 package main
import “fmt”
func fibonacci(n int) int { if n < 2 { return n } return fibonacci(n-2) + fibonacci(n-1) }
func main() { var i int for i = 0; i < 10; i++ { fmt.Printf(“%d\t”, fibonacci(i)) } }
以上实例执行输出结果为:
0 1 1 2 3 5 8 13 21 34
求平方根 以下实例通过 Go 语言使用递归方法实现求平方根的代码:
实例 package main
import ( “fmt” )
func sqrtRecursive(x, guess, prevGuess, epsilon float64) float64 { if diff := guess*guess - x; diff < epsilon && -diff < epsilon { return guess }
newGuess := (guess + x/guess) / 2 if newGuess == prevGuess { return guess }
return sqrtRecursive(x, newGuess, guess, epsilon) }
func sqrt(x float64) float64 { return sqrtRecursive(x, 1.0, 0.0, 1e-9) }
func main() { x := 25.0 result := sqrt(x) fmt.Printf(“%.2f 的平方根为 %.6f\n”, x, result) }
以上实例中,sqrtRecursive 函数使用递归方式实现平方根的计算。
sqrtRecursive 函数接受四个参数:
x 表示待求平方根的数
guess 表示当前猜测的平方根值
prevGuess 表示上一次的猜测值
epsilon 表示精度要求(即接近平方根的程度)
递归的终止条件是当前猜测的平方根与上一次猜测的平方根非常接近,差值小于给定的精度 epsilon。
在 sqrt 函数中,我们调用 sqrtRecursive 来计算平方根,并传入初始值和精度要求,然后在 main 函数中,我们调用 sqrt 函数来求解平方根,并将结果打印出来。
执行以上代码输出结果为:
25.00 的平方根为 5.000000
递归的优缺点 优点
简洁性 :递归代码通常比迭代代码更简洁,易于理解。
问题分解 :递归天然适合解决可以分解为相似子问题的问题,如树遍历、分治算法等。
缺点
性能开销 :递归调用会占用栈空间,可能导致栈溢出,尤其是在深度递归时。
调试困难 :递归代码可能较难调试,尤其是在递归深度较大时。
递归与迭代 递归和迭代是解决问题的两种不同方法。递归通过函数调用自身来解决问题,而迭代则通过循环结构(如 for 循环)来重复执行代码块。
递归 vs 迭代
特性
递归
迭代
代码简洁性
通常更简洁
可能更冗长
性能
可能较慢,占用栈空间
通常更快,占用较少内存
适用场景
适合分解为子问题的问题
适合线性或简单重复的问题
递归的常见应用 递归在许多算法和数据结构中都有广泛应用,例如:
树和图的遍历 :如深度优先搜索(DFS)。
分治算法 :如归并排序、快速排序。
动态规划 :如斐波那契数列的计算。
文件目录遍历 实例 import ( “fmt” “os” “path/filepath” )
func walkDir(dir string, indent string) { entries, err := os.ReadDir(dir) if err != nil { return }
for _, entry := range entries { fmt.Println(indent + entry.Name()) if entry.IsDir() { walkDir(filepath.Join(dir, entry.Name()), indent+” “) } } }
func main() { walkDir(“.”, “”) }
在 Go 中使用递归时,应特别注意基线条件(终止条件)的正确性,避免无限递归。对于性能敏感或可能深度递归的场景,建议考虑迭代实现或使用 channel/goroutine 等 Go 特有机制。