GO基础知识分享

GO基础知识分享

兵长:哟,最近在干啥呢

胖sir:在看我之前的go基础学习资料呢,回顾一下

兵长:那给我分享一下呗,我也想回顾回顾

胖sir:用你的小手指点开你的手机,我来传给你

兵长:你信不信我的小手指可以带你飞整个峡谷 . . .

go语言的基本事项

  1. go run hello.go 直接运行,输出结果(原理也是编译后执行)
  2. go build hello.go 生成可执行程序,运行可执行程序,输出结果
  3. 注意 go语言中花括号不能单独占一行,否则会报错
package main

import "fmt"

func main(){ //go语言中此处的花括号不能单独占一行,否则会报错
fmt.Println("hello world")
}
  1. go语言一条语句占一行,如果一行需要执行多个语句 使用 分号 隔开

  2. go语言的输出语句有3种方式

    1. import "fmt" 后适用fmt.Println(x) -- 输出
    2. println(x) -- 输出
    3. fmt.Printf("%d",x) -- 格式化输出

关键字

下面列举了 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

除了以上介绍的这些关键字,Go 语言还有 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
print println real recover string true uint uint8 uintptr

字符串的拼接和变量的定义方式

定义变量的三种方式

  1. 正常使用var定义变量
  2. 使用var定义变量,但是不定义类型,通过赋初值的方式,go编译器自动识别
  3. 使用:=的方式来进行 新变量的定义,仅限于新变量 -- 适用于定义在函数内部
//字符串 可以使用+ 进行拼接
fmt.Println("this is my func")
fmt.Println("hello ,wolrd" + "xiaozhuzhu") //定义变量
var name string="xiaomotong"
var age,tail int=24,170
fmt.Println(name, age , tail)
fmt.Println(name)
fmt.Println(age)
fmt.Println(tail) //定义变量的三种方式
//1
var a int = 1
fmt.Println(a) //2 使用var定义变量,但是不定义类型,通过赋初值的方式,go编译器自动识别
var b = "hello"
fmt.Println(b) //3 使用:=的方式来进行 新变量的定义,仅限于新变量
//:= 左侧如果没有声明新的变量,就产生编译错误
c := 20
fmt.Println(c)
//c:=30 //报错,因为c已经不是新变量的
c=30 //正确,是一个正常的赋值操作
fmt.Println(c) c,d:=40,90 //这样是合法的
fmt.Println(c,d)

因式分解的方式,仅仅适用于定义全局变量

//因式分解的方式,仅仅适用于定义全局变量
var
(
g_a int = 1
g_b,g_c int=1,2
)

空白符

空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。

_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。

//空白符
_,e := 2,3
fmt.Println(e)

const常量

  • 定义const常量
//定义const常量
const width,height = 10,5
var area int=width*height
fmt.Println("面积为", area) //50
  • const常量用作枚举
const(
unknow = 0
man = 1
woman = 2
) println(unknow,man,woman) //0 1 2
  • 常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:
const(
a = "hello"
b = len(a)
c = unsafe.Sizeof(a)
)
println(a,b,c) //hello 5 16

iota的用法

iota,特殊常量,可以认为是一个可以被编译器修改的常量。

iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

iota 可以被用作枚举值:

//itoa的用法
const(
g_a = iota
g_b
g_c
g_d
) const(
g_e = iota
g_f = "hello"
g_g
g_h = iota
g_i
) const(
g_j = 1<<iota
g_k
g_l
g_m
)
println(g_a,g_b,g_c,g_d)
println(g_e,g_f,g_g,g_h,g_i)
println(g_j,g_k,g_l,g_m)
//0 1 2 3
//0 hello hello 3 4
//1 2 4 8

运算符

go语言的运算符和C语言的运算符基本一致

Go 没有三目运算符,不能适用?:

算术运算符

关系运算符

逻辑运算符

位运算符

赋值运算符

其他运算符

语言条件语句

  • if xxx
if xxx {
...
}
  • if xxx {...} else{...}
if xxx{
...
}else{
...
}
  • if xxx{ ... if xxx { ...}}
if xxx{
if xxx {
...
}
...
}
  • switch
package main

import "fmt"

func main(){

	grade:= 90
if grade >= 90{
println("优秀")
}else if grade >=70 && grade <90{
println("良好")
}else{
println("差")
} var x interface{} //计算类型 switch i := x.(type){
case nil:
fmt.Printf(" x 的类型 :%T\n",i)
case int:
fmt.Printf("x 是 int 型")
default:
println("未知")
}
}
  • select

    类似于C语言中的select,用于多路IO复用

for循环的方式

  • 三种方式
  1. 类似C语言中的for
  2. 类似C语言中的while
  3. 死循环
package main

import "fmt"

func main(){

//类似C语言中的for
var sum int
for i:=1;i<=10;i++{
sum +=i
} fmt.Println(sum) //类似于while
for sum >30{
sum -= 10
fmt.Println(sum)
} //死循环 for {
...
}
  • For-each range 循环
//for-each  range 循环的方式
name := []string{"qqq","yyy"}
for i,str:= range name{
fmt.Printf("%d -- %s\n",i,str)
} //0 -- qqq
//1 -- yyy
------------------------------------------------------------------------
str := []string{"北京", "天津", "山东"} //可以默认丢掉第二个返回值
for i := range str {
fmt.Printf("%d -- %s\n", i, str[i])
}

函数

go语言的函数,可以有多个返回值,其余和C语言没有什么区别

作用域

与C语言一致

  • 局部变量
  • 全局变量
  • 函数形参

数组&切片

思想和C语言一致,数组是固定长度的

切片是动态扩容的,类似于C++的vector

切片写法如下:

name := []string{"xiaomotong","pangsir"}

nums :=[]int{1,2,3,4,5,6}

指针

var ptr1 *int

二级指针

var  a int
var ptr *int
var pptr **int ptr = &a
pptr = &ptr

指针数组

var ptr [5]*int

结构体

go语言中的结构体变量,和结构体指针,访问结构体成员的时候,都是使用 点(.)来进行访问,如下:

//定义一个结构体
type info struct{
name string
age int
height int
} //使用
var stu info
stu.name = "xiaomotong"
stu.age = 24
stu.height = 170 fmt.Println(stu.name,stu.age,stu.height) var stu2 *info = &stu
stu2.name = "pangsir"
stu2.age = 24
stu2.height = 160 fmt.Println(stu2.name,stu2.age,stu2.height)

切片slice

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

  • 使用var定义
  • 定义空slice
  • 使用:=定义
  • 使用make来定义 make([]type,len,cap)
  • apend 和 copy的使用
package main

/*
author:xiaomotong
file:slice
function:study slice for golang
*/
import "fmt" func main(){
//定义切片的方式
//1、使用var定义
var s1 = []int{1,2,3};
printInfo(s1);
//2、定义空slice
var s2 []int
printInfo(s2);
//3、使用:=定义
ss := []int{3,4,5,6}
printInfo(ss); //4、使用make来定义 make([]type,len,cap)
s3 := make([]int,2,3)
printInfo(s3);
//复制操作
s3[0] = 3
printInfo(s3);
//覆盖整个slice
s1 = s3
printInfo(s1); //apend 和 copy的使用
s3 = append(s3,6,7,8,9)
printInfo(s3);
//扩容
s4 := make([]int,len(s3),cap(s3) * 3)
copy(s4,s3)
printInfo(s4);
//s[2:]
println(s4[1:])
println(s4[:4])
println(s4[1:3])
fmt.Printf("s4[1:] = %v \n",s4[1:])
fmt.Printf("s4[:4] = %v \n",s4[:4])
fmt.Printf("s4[1:3] = %v \n",s4[1:3])
} func printInfo(s[]int){
fmt.Printf("len = %d, cap = %d, slic = %v\n",len(s),cap(s),s); }

范围Range

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。

在数组和切片中它返回元素的索引和索引对应的值在集合中返回 key-value 对

  • range 对于 数组、切片
  • 对于字符串
  • range对于map集合
  • 占位符_
package main

/*
author:xiaomotong
file:range
function:study range for golang
*/
import "fmt" func main(){ //1、range 对于 数组、切片 s := []string{"apple","pen"}
for i,value := range s{
fmt.Println(i,value)
} //2、对于字符串
for i,value := range "hello"{
fmt.Println(i,value)
} //3、range对于map集合
m := map[string]string{"name":"xiaopang","age":"25"}
for i,value := range m{
fmt.Println(i,value)
} //4、占位符_
sum := 0
nums := []int{1,2,3,4,5}
for _,value := range nums{
sum += value
}
fmt.Println(sum)
}

MAP集合

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

//类似于key-value的形式
map[string]string m := map[string]string{"name":"xiaozhu","age":"15"} mm := make(map[string]string) countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "罗马"
countryCapitalMap [ "Japan" ] = "东京"
countryCapitalMap [ "India " ] = "新德里"

delete() 函数

delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key

delete(countryCapitalMap,"France")

递归函数

Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。

递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。

递归算阶乘

package main

import "fmt"

func fabulaxiaomotong(n uint 64) (result uint64){
if n>0 {
return fabulaxiaomotong(n-1)*n
}
return 1
} func main(){
fmt.Println("result : ",fabulaxiaomotong(15))
}

菲波拉契数列

func fabolaxiaomotong(n uint64)(result utin64){
if n<2{
return n
}else{
return fabolaxiaomotong(n-2)+fabolaxiaomotong(n-1)
}
}

接口

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口

package main

import "fmt"

//接口
type phone interface {
call()
show()
} type xiaomi struct {
name string
ads string
} type huawei struct {
name string
ads string
} //接口实现
func (x xiaomi) call() {
fmt.Println("phoneName :", x.name)
} func (x xiaomi) show() {
fmt.Println("advertisement :", x.ads)
} func (h huawei) call() {
fmt.Println("phoneName :", h.name)
} func (h huawei) show() {
fmt.Println("advertisement :", h.ads)
} func main() {
x := xiaomi{"mi note2", "for fire"}
x.call()
x.show() h := huawei{"hw p40", "your better phone"}
h.call()
h.show()
}

错误

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。error类型是一个接口类型,这是它的定义:

package main

import "fmt"

//定义数据结构
type DivideError struct {
devidee int
devider int
} //错误处理实现Error()接口
func (de *DivideError) Error() string {
strdata := `
error,divide is zero
dividee is %d
divider is zero
` return fmt.Sprintf(strdata, de.devidee)
} //实现功能接口
func Divide(dividee int, divider int) (result int, errMsg string) {
if divider == 0 {
data := DivideError{dividee, divider}
errMsg = data.Error()
return
} else {
return dividee / divider, ""
}
} func main() {
a := 10
b := 0
result, err := Divide(a, b)
if err != "" {
fmt.Println(err)
return
}
fmt.Printf("%d / %d == %d \n", a, b, result) }

go语言的并发

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。goroutine 语法格式:

  • go的并发也是线程不安全的,需要加锁才安全
package main

import (
"fmt"
"time"
) func say(s string) {
var i int
for i = 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
} var num int = 0 //goroutine 是线程不安全的
func countNum() {
var i int
for i = 0; i < 10; i++ {
time.Sleep(5 * time.Millisecond)
num++
}
}
func main() {
//go say("hello")
//say("world") go countNum()
countNum()
fmt.Println(num)
}

通道(channel)

  • 通道(channel)是用来传递数据的一个数据结构。通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

    • 注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:
  • 通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小

  • Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片

package main

import "fmt"

//不带缓冲的 通道
func getSum(s []int, c chan int) {
sum := 0
for _, value := range s {
sum += value
}
c <- sum
} func getSum2(c chan int, n int) {
x, y := 0, 1
var i int
for i = 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c) //关闭通道
} func main() {
//不带缓冲的 通道
// s := []int{3, 5, -2, 3, 4, 7, 1, 1, 1}
// c := make(chan int)
// go getSum(s[:3], c)
// go getSum(s[3:6], c)
// go getSum(s[6:], c)
// x, y, z := <-c, <-c, <-c
// fmt.Println(x, y, z, x+y+z) //带缓冲的通道
c := make(chan int, 10)
go getSum2(c, cap(c)) for value := range c {
fmt.Println(value)
} }

自己调用别的包/自己的包

GO基础知识分享

自己调用别人的包或者自己的包,如上目录结构

  • 自己写的包名,要和目录名一样
  • 使用go mod 模块 ,执行 go mod init mystudy

mylib.go

package mylib

func Add(a, b int) int {
return a + b
}

main.go

package main

import (
"fmt"
"mystudy/mylib"
) func main() {
fmt.Println(mylib.Add(2, 3))
}

以上为本期全部内容,如有疑问可以在评论区或后台提出你的疑问,我们一起交流,一起成长。

好家伙要是文章对你还有点作用的话,请帮忙点个关注,分享到你的朋友圈,分享技术,分享快乐

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

作者:小魔童哪吒

上一篇:BZOJ 1500 维修数列【Splay】


下一篇:Vertex and FragmentShader顶点与片段着色器