break default func interface select
case defer go map struct
chan else goto package switch
const fallthough if range type
continue for import return var
预定义标识符
append bool byte cap close complex complex64 complex128 uint16
copy false imag int int8 int16 uint32 float32 float64
int32 int64 iota len make new nil panic uint64
print println real recover string true nint uint8 uintptr
var xx, yy type /* 可以一次声明多个变量 */
1,变量声明,如果没有初始化,默认为零值(系统默认值)
var a int
2,根据值自行判断
var xx = true
3,省略var, :=左侧如果没有声明新的变量,会编译错误
f := 'xx' 等同于 var f string = 'xx' 该形式只能用在函数体内
package main
import "fmt"
func main() {
var a int = 21
var b int = 10
if ( a == b ) {
fmt.Printf("1,a==b")
} else {
fmt.Printf("2,a!=b")
}
if ( a < b ) {
fmt.Printf("3,a<b")
} else {
fmt.Print("4,a!<b")
}
if ( a > b ) {
fmt.Printf("5,a>b")
} else {
fmt.Printf("6,a!>b")
}
a = 5
b = 20
if ( a <= b) {
fmt.Printf("7,a<=b")
}
if ( a >= b) {
fmt.Printf("8,a>=b")
}
}
逻辑运算符
运算符
描述
实例(a=10,b=20)
&&
AND,两边都是True,条件为True
(a && b) False
OR,两边有一个True,条件为true
!
NOT,如果条件为True,则逻辑条件为False
!(a && b) True
示例
package main
import "fmt"
func main() {
var a bool = true
var b bool = false
if ( a && b ) {
fmt.Printf("1,true\n")
}
if ( a || b ) {
fmt.Printf("2,true\n")
}
a = false
b = true
if ( a && b ) {
fmt.Printf("3,true\n")
} else {
fmt.Printf("3,false\n")
}
if ( !(a && b) ) {
fmt.Printf("4,true\n")
}
}
package main
import "fmt"
func main() {
var a int = 4
var b int32
var c float32
var ptr *int
fmt.Printf("变量类型为 %T\n", a)
fmt.Printf("变量类型为 %T\n", b)
fmt.Printf("变量类型为 %T\n", c)
ptr = &a
fmt.Printf("a的值 %d\n", a)
fmt.Printf("*ptr为 %d\n", *ptr)
}
# 判断数值大小
package main
import "fmt"
func main() {
var a int = 10
if a < 20 {
fmt.Printf("a小于20")
}
fmt.Printf("a的值为%d\n", a)
}
# 判断偶数
package main
import "fmt"
func main() {
var a int
fmt.Print("输入一个数字")
fmt.Scan(&a)
if a%2 == 0 {
fmt.Printf("a是偶数\n", a)
} else {
fmt.Printf("a不是偶数", a)
}
}
(1) 不需使用括号将条件包含起来
(2) 大括号{}必须存在,即使只有一行语句
(3) 左括号必须在if或else的同一行
(4) 在if之后,条件语句之前,可以添加变量初始化语句,使用;进行分隔
(5) 在有返回值的函数中,最终的return不能在条件语句中
if else语句
if 嵌套语句
# 判断输入
package main
import "fmt"
func main() {
var a, b int
fmt.Printf("请输入密码")
fmt.Scan(&a)
if a == 534221 {
fmt.Printf("请再次输入密码")
fmt.Scan(&b)
if b == 534221 {
fmt.Printf("密码正确")
} else {
fmt.Printf("密码错误")
}
} else {
fmt.Printf("密码错误")
}
}
switch语句
witch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough
package main
import "fmt"
func main() {
var grade string = "B"
var marks int = 90
/* 可以同时测试多个可能符合条件的值,使用逗号分割它们 case val1, val2, val3 */
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70: grade = "C"
default: grade = "D"
}
switch {
case grade == "A" :
fmt.Printf("优秀\n")
case grade == "B", grade == "C" :
fmt.Printf("良好\n")
case grade == "D" :
fmt.Printf("及格\n")
case grade == "F" :
fmt.Print("不及格\n")
default:
fmt.Printf("差\n")
}
fmt.Printf("等级为 %s",grade)
}
package main
import "fmt"
func main() {
var x interface{}
switch i := x.(type) {
case nil:
fmt.Printf("x类型",i)
case int:
fmt.Printf("xint类型")
case float64:
fmt.Printf("x是float64")
case func(int) float64:
fmt.Printf("是func(int)")
case bool, string:
fmt.Printf("是bool或string型")
default:
fmt.Printf("未知型")
}
}
package main
import "fmt"
func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received", i1)
case c2 <- i2:
fmt.Printf("sent", i2)
case i3, ok := (<-c3):
if ok {
fmt.Printf("received", i3)
} else {
fmt.Printf("c3 is close")
}
default:
fmt.Printf("no communication\n")
}
}
select 会循环检测条件,如果有满足则执行并退出,否则一直循环检测。
循环语句
for循环
for循环是一个循环控制结构,可以执行指定次数的循环
Go的for循环有三种形式,只有其中一种使用分号
for init; condition; post {}
init 一般为赋值表达式,给控制变量赋初始值
condition 关系表达式/逻辑表达式
post 赋值表达式,给控制变量增量减量
# for语句执行过程如下:
1,先对表达式1赋值
2,判断赋值表达式init是否满足给定条件,若值为真满足循环条件,执行循环体内语句,然后执行post,进入第二次村换,再判断condition;否则判断condition值为假,终止循环
for condition {}
for {}
# 计算1-10数字之和
package main
import "fmt"
func main() {
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
fmt.Printf(sum)
}
# 无限循环
package main
import "fmt"
func main() {
sum := 0
for {
sum++ //无限循环
}
fmt.Printf(dum)
}
# For-each range循环,可以对字符串、数组、切片等进行迭代输出元素
package main
import "fmt"
func main() {
string := []string{"google", "runoob"}
for i, s := range string {
fmt.Printf(i, s)
}
numbers := [6]int{1, 2, 3, 5}
for i,x := range numbers {
fmt.Printf('第 %d 位值 %d\n', i, x)
}
}
循环嵌套
循环控制语句
break # 用于中断当前for循环或跳出switch语句
用于循环语句中跳出循环,并开始执行循环之后的语句
break在switch(开关语句)中执行一条case后跳出语句作用
在多重循环中,可以用引号label标出想break的循环
# 示例
package main
import "fmt"
func main() {
var a int = 10
for a < 20 {
fmt.Printf("a的值为%d\n", a);
a++;
if a > 15 {
break;
}
}
}
# 使用标记
func main() {
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
}
}
}
将控制转移到被标记的语句
可以无条件转移到过程中指定的行
goto语句通常和条件语句配合使用,可用来实现条件转移,构成循环,跳出循环体等功能
但是,结构化程序设计中一啊不能不主张使用goto语句,以免造成程序流程的混乱
func main() {
var a int = 10
LOOP: for a < 20 {
if a == 15 {
a = a + 1
goto LOOP # 当a==15时跳过迭代回到开始LOOP处
}
fmt.Printf(a)
a++
}
}
示例
(9*9乘法表)
package main
import "fmt"
func main() {
// i := 1 > var i int = 1
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d * %d = %d", j, i, j*i)
}
fmt.Printf("")
}
}
(输出1-100素数)
package main
import "fmt"
func main() {
var a, b int
for a = 2; a <= 100; a++ {
for b = 2; b <= (a / b); b++ {
if a%b == 0 {
break
}
}
if b > (a / b) {
fmt.Printf("%d\t是素数\n", a)
}
}
}
语言函数
Go最少有一个main()函数,用函数划分不同的功能,逻辑上每个函数执行的是指定的任务
函数声明告诉了编译器函数的名称,返回类型和参数
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
}
package main
import "fmt"
var g int # 声明全局变量,可以在整个包甚至外部包(导出后)使用
func main() {
var a, b, c int # 声明局部变量,作用域只在函数体内,参数和返回值也是局部变量
a = 10
b = 20
c = a + b
fmt.Printf("结果",c)
}
Go中全局变量和局部变量名称可以相同,但是函数内局部变量会被优先考虑
形式参数会作为函数的局部变量来使用
不同类型的局部和全局变量默认值为
int 0
float32 0
poniter nil
var balance = [5]float32{100.0, 2.0, 3.4, 7.0, 50.0}
初始化数组{}中元素个不能大于[]中数字
如果忽略[]中数字不设置数组大小,Go会根据元素个数来设置数组大小
访问数组元素
var salary float32 = balance[9] # 格式为数组名加中括号
数组完整操作
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])
}
}
多维数组
常用的多维数组声明方式
var threedim [5][10][4]int
二维数组,是最简单的多维数组,二维数组本质上是由一维数组组成的
二维数组可认为是一个表格,x为行,y为列
a = [3][4]int{{0,1,2,3},{4,5,6,7},{8,9,10,11}} # 3行4列的一个数组
访问二维数组,可以通过指定坐标来访问,如数组中行索引和列索引
var value int = a[2][3]
可以使用循环嵌套来输出元素
维数组初始化或赋值时需要注意 Go 语法规范,该写在一行就写在一行,一行一条语句
package main
import "fmt"
func main() {
var a = [5][2]int{{0,0},{1,2},{2,4},{3,6},{4,8}}
var i, j int
for i = 0; i < 5; i++ {
for j = 0; j < 2; j++ {
fmt.Printf("a[%d][%d] = %d\n", i, j, a[i][j])
}
}
}
向函数传递数组
如果想向函数传递数组参数,需要在函数定义时,声明形参为数组,可以通过两种方式来声明
形参设定数组大小
void myfunc(param [10]int)
形参未设定数组大小
void myfunc(param []int)
示例
package main
import "fmt"
func main() {
var balance = [5]int {1000, 2, 3, 17, 50}
var avg float32
avg = getAverage( balance, 5 )
fmt.Printf("平均值 %f", avg)
/* 转整形设置精度 */
fmt.Printf(float64(avg))
}
# 函数一个参数指定接收整数型数组参数,另一个参数指定了数组元素个数
func getAverage(arr [5]int, size int) float32 {
/* 使用形参并未设置数组大小 arr []int */
/* 未定义长度的数组只能传给不限制数组长度的函数 */
/* 定义长度的数组只能传给限制了相同数组长度的函数 */
var i, sum int
var avg float32
for i = 0; i < size; i++ {
sum += arr[i] # 求合
}
avg = float32(sum) / float32(size)
return avg;
}
# 多维数组传参
package main
func prt(arr [][] float32) {
for i := 0; i < 3; i++ {
Println(arr[i][0])
}
}
func main() {
var arr = [][]float32 {{-1,-2}, {-3, -4}, {-5}}
prt(arr)
}
# 杨辉三角
package main
import "fmt"
func main() {
triangle(12)
}
func triangle(rows int) {
for i := 0; i < rows; i++ { # 打印行数
number := 1
for k := 0; k < rows-i; k++ { # 打印空白处,随着行数增加空白减少
fmt.Print(" ")
}
for j := 0; j <= i; j++ { # 打印数,随着行数增加,数字也增加
fmt.Printf("%5d", number)
number = number * (i -j) / (j + 1)
}
fmt.Println()
}
}
语言指针
Go语言的取地址符是&,放到一个变量前使用就会返回相应变量的内存地址
package main
import "fmt"
func main() {
var a int = 10
fmt.Printf("变量地址 %x\n", &a )
}
什么是指针
一个指针指向了一个值的内存地址
类似于变量和常量,在使用指针前需要声明指针
var var_name *var-type
var ip int / 指向整形 */
var fp float32 / 指向浮点型 */
指针使用流程
定义指针变量
为指针变量赋值
访问指针变量中指向地址的值
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)
}
Go空指针
当一个指针被定义后没有分配任何变量时,它的值为nil,也叫做空指针
一个指针变量通常缩写为 ptr
指针数组
需要保存数组,可以使用指针
整形指针数组:var ptr [MAX]*int;
ptr为整形数组,每个元素都指向一个值
# 指针数组中存储三个整数
package main
import "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])
}
}
创建指针数组时,不适合用range循环
for i, x := range &number {
ptrs[i] = &x
}
# 这样循环出来,数组中值一样,内存地址也相同
是因为range循环逻辑造成的,和for不同的是range循环中x变量是临时变量,range循环只是将值拷贝到x变量中,因此内存地址都是一样的
number = [max]int{5,6,7}
var ptrs [max]*int # 指针数组
for i := 0, i < max, i++ {
ptrs[i] = &number[i] # 将number数组的值的地址赋给ptrs
}
Go语言指向指针的指针
如果一个指针变量存放的有时另一个指针变量的地址,则称这个指针变量为指向指针的指针变量
当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址
指向指针的指针变量声明格式如下
var ptr **int;
访问指向指针的指针变量值需要使用两个*号
package main
import "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)
}
三重指针及其对应关系
pt3 - > pto - > ptr - >变量a
Go语言指针作为函数参数
Go允许向函数传递指针,只需要在函数定义的参数上设置为指针类型即可
package main
import "fmt"
func main() {
var a int = 100
var b int = 200
fmt.Printf("交换前\n", a)
fmt.Printf("交换前\n", b)
swap(&a, &b)
fmt.Printf("交换后\n", a)
fmt.Printf("交换后\n", b)
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存x地址的值 */
*x = *y
*y = temp
}
# 交换函数简写
func swap(x *int, y *int) {
*y, *y = *y, *x
}
语言结构体
数组可以存储同一类型的数据,但是结构体中我们可以为不同定义不同的数据类型
结构体是由一系列具有相同类型或不同类型数据构成的数据集合
定义结构体
type variable struct {member definition }
一旦定义了结构体,它就能用于变量的声明
variable := variable {value1, value2…}
示例
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", "教程", 6479})
/* 可以使用key => value格式,忽略的字段为0或者为空 */
fmt.Println(Books{title: "语言"})
}
访问结构体成员
如果要访问结构体成员,需要使用点号.操作符
Books.title
结构体作为函数参数
可以像其他数据类型一样将结构体类型作为参数传递给函数
package main
import "fmt"
type Books struct {
title string
author string
subject string
}
func main() {
var Book1 Books
var Book2 Books
Book1.title = "go语言"
Book1.author = "mm"
Book2.subject = "教程"
printBook(Book1)
}
func printBook(book Books) {
fmt.Printf("book title %s\n", book.title)
fmt.Printf("book author %s\n", book.author)
}
结构体指针
可以定义指向结构体的指针类似于其他指针变量
var struct_pointer *Books
查看结构体变量地址,可以将&符号放置于结构变量前
struct_pointer = &Book1
使用结构体指针访问结构体成员,使用 . 操作符
struct_pointer.title
package main
import "fmt"
type Books struct {
title string
author string
subject string
}
func main() {
var Book1 Books
var Book2 Books
Book1.title = "go语言"
Book1.author = "mm"
Book2.subject = "教程"
printBook(&Book1)
}
func printBook(book *Books) {
fmt.Printf("book title %s\n", book.title)
fmt.Printf("book author %s\n", book.author)
}
结构体是作为参数的值传递
如果想在函数中改变结构体数据内容,需要传入指针
struct类似于java中的类,可以在struct总定义成员变量,要访问成员变量,有两种方式
struct 变量.成员
struct 指针.成员
不需要通过过getter, setter设置访问权限
type Rect struct { //定义类
x, y float64 //类型只包含数属性,并没有方法
width, height float64
}
func (r *Rect) Area() float64 { //为类绑定Area方法,*Rect为指针引用可以修改传入参数的值
return r.width*r.height //方法归属类型,不归属具体的对象,声明该类型的对象即可调用该类型方法
}
package main
import "fmt"
func main() {
nums := []int{2,3,4}
sum := 0
for _, num := range nums {
sum += num /* 适用range去求slice和 */
}
fmt.Println("sum", sum)
for i, num := range nums {
if num == 3 {
fmt.Println("index", i) //在数组上适用range传入index和值两个变量,_适用空白符省略序号
}
}
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s %s\n", k, v)
}
}
var map_variable map[type]type / map_variable := make(map[type]type)
如果不初始化map,就会创建一个nil map,nil map不能用来存放键值对
package main
import "fmt"
func main() {
var countMap map[string]string /* 创建集合 */
countMap = make(map[string]string)
/* map插入key-value */
countMap ["France"] = "巴黎"
countMap ["Japan"] = "东京"
for country := range countMap {
fmt.Println(country, "首都为", countMap [country])
}
capital, ok := countMap ["American"]
if (ok) {
fmt.Println("American首都为", capital)
} else {
fmt.Println("American首都不存在")
}
}
delete()函数
delete()函数用于删除集合的元素,参数为Map和其对应的key
delete(countMap, “France”)
递归函数
递归就是运行过程中调用自己
使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中
示例
# 阶乘
package main
import "fmt"
func Factorial(n uint64)(result uint64){
if (n > 0){
result = n * Factorial(n-1)
return result
}
return 1
}
func main() {
var i int = 15
fmt.Printf("%d阶乘是%d\n", i, Factorial(uint64(i)))
}
# 斐波那契数列
package main
import "fmt"
func fib(n int) int {
if n < 2{
return n
}
return fib(n-2) + fib(n-1)
}
func main() {
var i int
for i = 0; i < 10; i++{
fmt.Printf("%d\t", fib(i))
}
}
语言类型转换
类型转换用于将一种数据类型的变量转换为另一种类型的变量
type_name(expression) # 类型(表达式)
示例
package main
import "fmt"
func main() {
var sum int = 17
var count int = 5
var mean float32
mean = float32(sum)/float32(count)
fmt.Printf("mean值为 %f\n", mean)
}
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s{
sum += v
}
c <- sum //定义sum函数,计算完成后,将sum发送到通道c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0} //定义数组
c := make(chan int) //定义通道c
go sum(s[:len(s)/2], c) //s[:3] => 7,2,8
go sum(s[len(s)/2:], c) // s[3:] =>-9,4,0
x, y := <-c, <-c //从通道c中接收,先进先出
fmt.Println(x, y, x+y)
}
package main
import (
"fmt"
)
func fib(n int, c chan int){
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x //将x值赋值c
x, y = y, x+y
}
close(c) //计算完毕关闭通道
}
func main() {
c := make(chan int, 10) //定义通道,缓存区为10
go fib(cap(c), c)
for i := range c{ //运行10次后通道关闭
fmt.Println(i)
}
}
channel是可以控制读写权限的
go func(c chan int) 读写均可
go func(c <- chan int) 只读
go func(c chan <- int) 只写
有缓冲和无缓冲的区别
无缓冲是同步的,如make(chan int),消息必须被接收后
有缓冲是异步的,如make(chan int, 2),消息放入缓冲区,除非满了,否则不会阻塞
主进程和子进程
package main
import (
"fmt"
"time"
)
func say(s string){
for i := 0; i < 5; i++{
time.Sleep(100 * time.Millisecond)
fmt.Println(s, (i+1)*100)
}
}
func say2(s string){
for i := 0; i < 5; i++{
time.Sleep(150 * time.Millisecond)
fmt.Println(s, (i+1)*150)
}
}
func say2(s string, ch chan int){
for i := 0; i < 5; i++{
time.Sleep(150 * time.Millisecond)
fmt.Println(s, (i+1)*150)
}
ch <- 0 //引入通道,当程序结束时,发送消息0,然后关闭通道
chose(ch)
}
func main(){
go say2("world") //这里say2只执行3次,而不是5次,原因是子函数没结束,主函数结束后退出了
ch := make(chan int)
go say2("world", ch) //引入一个通道,默认通道的存消息和取消息都是阻塞的,当主函数完成后,会一直等待通道中的值,一旦通道有值,关闭通道后主函数才结束
say("hello")
fmt.Println(<-ch)
}