go基础语法

Last updated on 6 months ago

go语法基础

初始化

在需要编写go的目录(假设是go_code),在go_code下

1
go mod init go_code(目录名)

会在go_code目录下生成一个go.mod的初始化文件,在go_code目录下再创建一个目录.在该目录里编写代码

位与进制

1
2
3
4
二进制    BIN 0 , 1, 10
八进制 OCT 0,1,2,3,4,5,6,7,10(一零,不是十)
十进制 DEC
十六进制 HEX 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10
1
2
3
4
5
位:(内存地址的宽度)
BYTE: 8位 一个字节
WORD: 16位
DWORD: 32位
QWORD: 64位

规范

1
2
3
4
5
6
7
package main              //引入包

import fmt //引入fmt的库,在go中导入了什么库,就必须使用,否则报错

func main(){
fmt.Println("hello world") //不需要分号
}
1
2
3
4
5
6
7
8
package main

import fmt

func main(){
var num int = 5 //var跟php类似的意思,赋值号必须有空格
fmt.Println(num) //创建了num的变量就必须使用,否则报错
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//初始化
var a int = 5
//默认值(0)
var b int
//自动化推导(auto)
c := 10
//多变量定义
var d,e int
d = 20
e = 30
//多变量定义
var(
f int = 2
g int = 3
)

//在定义过变量之后i,要改变值直接改就可以
a = 6 //a原本是5

数据类型

基本类型 复合类型
int array
folat slice
string map
bool struct
pointer
function
chan

按照特性来分

值类型 引用类型(传递地址,多变量指向同一内存地址)
int slice
float
string
bool
struct

深浅拷贝

深拷贝是把数据都复制一份在传递,被传递的跟原有数据已经没关系了

浅拷贝是传址,一个改变另一个也改变

整型

有符号整型 无符号整型 字符(ASCII) 字符(unicode)
int (默认64bit) uint byte rune
int8 (8bit) uint8
int16 (16bit) uint16
int32 (32bit) uint32
int64 (64bit) uint64

符号位是从左往右数第一位(有符号整型),0是正数,1是负数,所以int8是-128到127

1
如果求的数是负数,那么则要求该数对应的补码,即把所有数取反(0换1,1换0)那么本来最大的127(0111 1111)就变成了(1000 0000)的-128

int16是-32768到32767,uint16是0到65535

ASCII可以等于成uint8

在go中的unicode版本是UTF-32,4字节,32bit

浮点

float32 4字节

float64 8字节

类型转换

数值类型转换

1
2
3
var numa int64 = 12138
var numb int8 = int8(numa)
fmt.Println(numb) //106

字符串

1
2
3
4
5
6
var numa int64 = 12138
var numb int8 = int8(numa)
var numc int32 =int32(numb)
var str string = fmt.Sprintf("%d %d %d",numa,numb,numc)
//fmt.Sprintf是吧其它类型的值转化成字符串的专用函数
//输出12138 106 106

常量

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"

const (
numA = 11
numB = 22
)

const (
a = 1 //在前一个常量定义后,如果后一个常量未被定义,则跟前一个常量相等
b
c = 33
d
)

const (
numC = iota //自带的枚举量,会从0开始计数
numD
numE
numF
)

func main() {
const pi = 3.1415 //既可以定义在main函数内也可以在外部定义
//3.1415
fmt.Println(pi)
//11 22
fmt.Println(numA, numB)
//1 1 33 33
fmt.Println(a, b, c, d)
//0 1 2 3
fmt.Println(numC, numD, numE, numF)
}

关系与逻辑运算

1
2
3
4
fmt.Println((1>2))     //输出false
&& //左右两侧均为真则结果为真
|| //或
! //非

位运算

与运算(按位与)

1
2
3
4
var h int = 189         //128+32+16+8+4+1    1011 1101
var i int = 245 //128+64+32+16+4+1 1111 0101
var m int = h & i
fmt.Println(m) //181 1011 0101

位一样的变为1,不一样的为0

&

或运算(按位或)

都是0为0,其余为1

|

异或运算

两位一样则为0,不一样则为1

^

运算符

左移运算符 <<

高位抛弃,低位补零

1
2
3
var h int = 189         //1011 1101
var j int = h << 3 //1011110 1000 -> 1110 1000
fmt.Println(j) //232

右移运算符 >>

符号位是什么就补什么

int默认是64位,正数的符号位是0,则右移之后原来的位置补0

选择结构

if

1
2
3
4
5
6
7
8
9
var flag int
fmt.Scanf("%d", &flag)
if flag == 10 { //go的if中没有()
fmt.Println("yes")
} else if flag >= 11 && flag <= 20 {
fmt.Println(15)
} else {
fmt.Println(21)
}

switch

1
2
3
4
5
6
7
8
9
10
switch flag {
case 1: //如果flag是1
fmt.Println(1)
case 2:
fmt.Println(2)
case 3, 4: //如果flag是3或4
fmt.Println("3 | 4")
default:
fmt.Println("大于4") //else
}

循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for i:=1; i<=10; i+=4{
fmt.Println(i)
}

for x := 1; x <= 9; x++ {
for y := 1; y <= x; y++ {
fmt.Printf("%d*%d=%d", y, y, y * y)
}
fmt.Println()
}

//这种主要用于循环数组和字符串等
var str string = "abcdefg"
for index, value := range str {
fmt.Printf("%d %c", index, value)
fmt.Println()
}
//index和value不是默认的,只是两个接收内容的变量,也可以用占位符_替代,名字是什么都可以,比如inde和valu
//for _,_ := range 变量名{}
//如果只想获得index,后面的可以不写,占位符也不用写,但如果指向获得后面的,则前面的必须写占位符
1
2
3
4
5
6
7
8
//continue
for i:= 1;i<=10;i++{
if i==5{
continue
}
fmt.Println(i)
}
//结果是1 2 3 4 6 7 8 9 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//break
for i:= 1;i<=10;i++{
if i==5{
break
}
fmt.Println(i)
}
//结果是1 2 3 4
//如果是嵌套循环,则
for x := 1; x <= 2; x++ {
for i := 1; i <= 10; i++ {
if i == 5 {
break
}
fmt.Println(i)
}
}
//结果是1 2 3 4 1 2 3 4,只跳出一层循环

指针和数组

指针

1
2
3
4
5
6
7
var num * int = nil       //初始化一个空指针,nil就是null的意思
var numa int = 123
num = &numa
fmt.Println(num) //输出0xc0000a6058
fmt.Pritln(*num) //输出123
*num = 234
fmt.Println(*num, numa) //输出234 234
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//空指针
var n *int = nil
fmt.Println(n, *n)
*n = 500
fmt.Println(n, *n)
//会报错
/*panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x9adf7c]

goroutine 1 [running]:
main.main()
d:/桌面/go/first/hello.go:15 +0x15c
exit status 2*/
//报错原因是本来n是空指针,所以没有占用内存空间,不能进行直接赋值

var n *int = nil
n = new(int)
fmt.Println(n, *n)
*n = 500
fmt.Println(n, *n)
//输出
//0xc0000160d8 0
//0xc0000160d8 500

数组

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
	var arr = [4]string{"apple", "orange", "pear", "banana"}
arr[3] = "123"
fmt.Println(arr[3])
//结果是123
//可以直接打印
fmt.Println(arr)
//打印数组
for index, value := range arr {
fmt.Println(value, index)
}
/*
apple 0
orange 1
pear 2
123 3
*/

//多维数组
var arr = [3][2]string{
{"11", "12"}, //用逗号分隔
{"21", "22"},
{"31", "32"}} //最后一个括号一定要在同一行否则会报错
fmt.Println(arr[2][1])
//打印多维数组
for _, value := range arr { //这里的每个value都是一个一维数组
for _, va := range value {
fmt.Println(va) //这里的va是一个值
}
}

切片(slice)

可以理解成可变长度的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s1 := []int{1,2,3,4}    //数组不写len,默认为切片
s1 := make([]int,2,4) //make(slice(slice就是[])/map/chan,初始时有多少个元素(len),最大有多少个元素(cap))
//甚至可以建立一个内部元素是map的切片 s1:=make([]map[string]string,2,6)
fmt.Println(s1) //结果是[0 0 0 0]
s1[1] = 5 //直接修改只能在初始范围内修改,想增加需要额外函数
fmt.Println(s1) //[0,5]
//append函数可以对切片进行增加/扩容操作
s1 = append(s1, 2, 3)
fmt.Println(s1) //[0 5 2 3]
s1 = append(s1, 55)
fmt.Println(s1) //本来s1最大容纳元素是5,但append函数可以自动扩容(cap翻倍),在上一步直接append(s1,2,3,55)也可以,后面新添加的元素数量没有限制
s2 := make([]int, 1, 2)
s1 = append(s1, s2...) //也可以在一个切片后直接添加一个切片,但后面的切片必须带上...
fmt.Println(s1) //[0 5 2 3 55 0]
fmt.Printf("%d %d", len(s1), cap(s1)) //6 8

切片直接引用数组

1
2
3
4
5
6
7
8
9
10
var arr = [4]int{1, 2, 3, 4}
fmt.Println(arr)
s1 := arr[:2]
fmt.Printf("%d %d", cap(s1), s1) //4 [1 2] cap默认是原数组长度
s2 := arr[:] //必须加[:],否则s2将是数组而不是切片
fmt.Println(s2) //默认是全取 [1 2 3 4]
s3 := arr[2:4] //左闭右开实际是第2和3个元素(0 1 2 3的顺序)
fmt.Println(s3) //[3 4]
//对数组的直接引用,会使切片指向数组,数组改变,切片也会改变,但当切片扩容之后,会自动指向新的数组,原数组改变,切片不再改变
//切片引用数组,本质是获取了数组的指针,所以切片改变,数组也会改变

copy函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	s1 := []int{1, 2, 3, 4}
fmt.Println(s1)
s2 := []int{7, 8}
//情况1
copy(s1, s2) //s1是被赋值的(待操作),s2是进行赋值的
fmt.Println(s1, s2) //[7 8 3 4] [3 4]
//情况2
copy(s2,s1) //s1长度大于s2时,s2会被全部修改,但不会被扩容
fmt.Println(s2) //[1 2]
//情况3
//可以手动选择从哪里开始赋值
copy(s1, s2[:1])
fmt.Println(s1) //[7 2 3 4]
//情况4
copy(s1[2:], s2[:1])
fmt.Println(s1) //[1 2 7 4]

map

就是键值对

使用map过程中需要注意的几点:

  • map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
  • map的长度是不固定的,也就是和slice一样,也是一种引用类型
  • 内置的len函数同样适用于map,返回map拥有的key的数量
  • map的key可以是所有可比较的类型,如布尔型、整数型、浮点型、复杂型、字符串型……

创建map

1
2
3
4
5
6
//直接创建,当不赋值时为nil,不能直接更改
var map1 map[int]string //map是类型,int是键名的变量类型,string是键值的变量类型
//用make创建,即使没赋值也不是nil,可以直接使用
map2 := make(map[int]string) //只有map可以这样,slice必须加len和cap,chan可以加cap也可以不加cap
//直接创建并赋值
map1 := map[string]int{"html": 60, "css": 70, "js": 80} //中间用逗号分隔,赋值用冒号

操作

1
2
3
4
5
6
7
8
9
//可以直接进行增加/修改键值对
map1 := map[string]int{"html": 60, "css": 70, "js": 80}
fmt.Println(map1) //map[css:70 html:60 js:80]
map1["py"] = 50
fmt.Println(map1) //map[css:70 html:60 js:80 py:50]

//删除需要用到delete函数
delete(map1,"py") //如果键名是int就不用加引号,这里是因为是字符串才加的引号
fmt.Println(map1) //map[css:70 html:60 js:80]

map遍历(map是无序的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//int:string型
map1 := map[int]string{1: "太极", 2: "两仪", 3: "四象", 4: "八卦"}
keys := make([]int, 0, len(map1))
fmt.Println(keys)
for index := range map1 {
keys = append(keys, index)
}
fmt.Println(keys)
sort.Ints(keys)
for _, k := range keys {
fmt.Println(k, map1[k])
}
//string:int型
map1 := map[string]int{"html": 60, "css": 70, "js": 80}
for k, v := range map1 {
fmt.Println(k, v)
}

闭包

在一个函数中,如果有内层函数,则在外层函数定义的局部变量可以被内层函数不经调用直接使用,并且如果变量被内层函数使用后,在外层函数也结束后,该局部变量不会被释放(类似c的const)

指针

数组指针

本质是指针,指向数组的地址

1
2
3
var arr := [4]int{1,2,3,4}
var p *[4]int
p=&arr //p有自己的地址,但是p指向数组的地址

指针数组

本质是数组,存储的内容是指针

1
2
3
4
5
6
7
8
9
10
11
a:=1
b:=2
c:=3
d:=4
arr1:=[4]int{a,b,c,d}
arr2:=[4]*int{&a,&b,&c,&d}
arr1[0] = 5
fmt.Println(a) //1
*arr2[0] = 5
fmt.Println(a) //5
//数组的直接赋值时深拷贝,如果时指针数组则是浅拷贝

总结

1
2
*[4] type  先指针再数组,所以是数组指针
[4]* type 先数组再指针,所以是指针数组

结构体

对象

结构体内部是结构体的属性,而在外部创建对象,并给对象的属性赋值

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
var p1 person //创建一个对象
p1.age = 18
p1.name = "小明"
p1.sex = "m"
fmt.Println(p1)
//第二种定义方法
p2 := person{}
p2.age = 19
fmt.Println(p2.age)
//第三种定义方法
p3 := person{age: 20, sex: "f"} //结构体的赋值是冒号,跟map一样
fmt.Println(p3)
}

type person struct { //type和struct是关键字
age int
name string
sex string
}

结构体指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
var p1 person
p1.age = 18
p1.name = "小明"
p1.sex = "m"
var p *person = &p1
fmt.Println(p, *p) //&{18 小明 m} {18 小明 m} 第一个输出带&
p.age = 29 //指针修改,则原来的也修改
fmt.Println(p1) //{29 小明 m}
}

type person struct {
age int
name string
sex string
}

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
w1 := worker{name: "小明", age: 19, sex: "m"}
w1.work() //小明 在工作
w2 := &worker{name: "小明", age: 19, sex: "m"} //&是取地址,这里是w2是结构体指针
w2.work() //小明 在工作
}

type worker struct {
name string
age int
sex string
}

func (w worker) work() { //这里建立一个局部的结构体变量的对象用于进行操作 这里是空格不是逗号,work()是方法名
fmt.Println(w.name, "在工作")
}

模拟继承性

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
package main

import "fmt"

func main() {
//创建pers父类对象
p1 := pers{name: "小明", age: 18}
fmt.Println(p1.name, p1.age)
p1.eat()
//创建子类对象
s1 := stu{pers{name: "小光", age: 19}, "qdyz"} // pers的结构体嵌套是匿名字段,所以后面的school也只能用匿名字段的方法赋值
fmt.Println(s1.school)
//子类对象调用父类方法
s1.eat()
//子类调用自定义方法,父类对象不能调用子类的方法
s1.study()
}

//父类
type pers struct {
name string
age int
}

//子类
type stu struct {
pers //结构体嵌套,模拟继承性
school string
}

//父类方法
func (p pers) eat() {
fmt.Println("父类方法,吃米饭")
}

//子类自定义方法
func (s stu) study() {
fmt.Println("子类自定义方法,在学习")
}

//子类重写方法
func (s stu) eat() { //方法中的局部变量的名字可以一样
fmt.Println("子类重写的吃饭方法")
}

接口

接口有点类似结构体的感觉,只要有别的结构体的对象调用接口,那么这些结构体都可以相当于包含在接口所在的大类里

接口可以创建接口类型的变量,而且调用了接口的结构体的对象可以算作接口创建的对象

但是接口创建的变量不能使用调用接口的结构体中的对象的信息

接口中的内容是方法,不是具体值

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
51
52
53
54
55
56
package main

import "fmt"

func main() {
m1 := mouse{"原厂"}
m1.start()
m1.end()
u1 := uDisck{"64g"}
u1.start()
u1.end()
test(m1)
test(u1)
var usb USB = m1
usb.start()
arr := [3]USB{m1, u1}
fmt.Println(arr)
}

//定义接口 usb
type USB interface {
start()
end()
}

//定义鼠标类对象
type mouse struct {
name string
}

//定义u盘类对象
type uDisck struct {
name string
}

//鼠标类对象的接口
func (m mouse) start() {
fmt.Println(m.name, "鼠标开始")
}
func (m mouse) end() {
fmt.Println(m.name, "鼠标结束")
}

//u盘类对象的接口
func (u uDisck) start() {
fmt.Println(u.name, "u盘ok")
}
func (u uDisck) end() {
fmt.Println(u.name, "可以拔出")
}

func test(usb USB) {
usb.start()
usb.end()
}

空接口

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
map1 := make(map[int]interface{})
map1[0] = "hahah"
map1[1] = 123
fmt.Println(map1)
}

接口断言

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package main

import (
"fmt"
"math"
)

func main() {
s1 := sanj{3, 4, 5}
fmt.Println(s1.area())
fmt.Println(s1.peri())
c1 := cir{2}
fmt.Println(c1.peri())
fmt.Println(c1.area())

var sh1 shape = s1
sh1.area()
test(s1)
test(c1)
test(sh1)
fmt.Print("\n")
fmt.Println("接口断言")
gettype(s1)
gettype(c1)
gettype(sh1)
fmt.Println("另一种接口断言")
gete(s1)
gete(c1)
gete(sh1)
}

func gettype(a shape) {
//接口断言,判断传入的数据属于哪个结构体
//判断是否属于三角类型
if v, ok := a.(sanj); ok {
fmt.Println("三角形,三边分别是", v.a, v.b, v.c)
} else if va, ok := a.(cir); ok {
fmt.Println("是圆形,半径是", va.r)
} else {
fmt.Println("no")
}
}
func gete(a shape) {
switch v := a.(type) { //type是关键字,括号里面就直接写type
case sanj:
fmt.Println("是三角,第一个边长是", v.a)
case cir:
fmt.Println("是圆,半径是", v.r)
}
}

type shape interface {
peri() float64
area() float64
}
type sanj struct {
a, b, c float64
}

func (s sanj) peri() float64 {
return s.a + s.b + s.c
}
func (s sanj) area() float64 {
p := s.peri() / 2
are := math.Sqrt(p * (p - s.a) * (p - s.b) * (p - s.c))
return are
}

type cir struct {
r float64
}

func (c cir) peri() float64 {
return 3 * c.r * 2
}
func (c cir) area() float64 {
return 3 * c.r * c.r
}

func test(s shape) {
fmt.Printf("面积是:%.2f,周长是%.2f", s.area(), s.peri())
}

错误和异常

处理异常需要调用os包(import os)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
"os"
)

func main() {
f, err := os.Open("test.txt")
if err != nil {
fmt.Println("不存在文件")
return
}
fmt.Println("成功读取文件", f.Name())
}
//只打印不存在文件

函数库

1
2
3
4
5
6
7
8
9
//sort排序(正序)  只能用于切片,不能用于数组
arr := [4]int{5, 3, 1, 6}
arr1 := arr[:]
sort.Ints(arr1)
fmt.Println(arr1) //1 3 5 6
//字符串按照首字母ascii排序
ar := []string{"abx", "A", "UK", "呃呃呃"}
sort.Strings(ar)
fmt.Println(ar) //[A UK abx 呃呃呃]

习题

典中典水仙花

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
var ge int
var sh int
var bai int
for i := 100; i < 1000; i++ {
ge = i % 10
sh = i / 10 % 10
bai = i / 100
if i == ge*ge*ge+sh*sh*sh+bai*bai*bai {
fmt.Println(i)
}
}
}

求素数

j要从2开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
for i := 2; i <= 100; i++ {
flag := true
for j := 2; j < i; j++ {
if i%j == 0 {
flag = false
break
}
}
if flag {
fmt.Println(i)
}
}
}

go基础语法
https://blog.yblue.top/2023/08/20/go基础语法/
Posted on
August 20, 2023
Licensed under