go

本文介绍了Go语言的基础概念,包括开发效率、并行机制、类型系统等特性。详细讲解了Go的基本语法,如包管理、变量定义与初始化、函数定义、控制结构等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

go

go 简介

有开发效率:有表达力,清晰,简洁,有效率

并行机制容易开发多核和网络应用

新奇的类型系统: 构建有弹性的模块化编程

便利的垃圾回收和强大的运行时反射

是静态的,但看起来像动态的

基础

package

每个go程序都是由包组成的

程序的入口是包main

包名与导入路径的最后一个目录保持一致 “math/rand”

包导入

import “fmt”

import “math/rand”

<==>

import (
“fmt”
“math/rand”
)

包导出

即包的私有属性和公有属性

在包中,首字母大写是public的,小写是private的。
Foo/FOO都是公有的,foo是私有的

eg

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}
函数
  • 类型

变量和函数都是类型后置

当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略

  • 返回值
多值返回
func func_name(x, y int) (int, int, string){}
返回值命名
Go 的返回值可以被命名,并且就像在函数体开头声明的变量那样使用。
裸返回

eg

func add(x int, y int) int {
    return x+y
}

func add(x, y int) int {
    return x+y
}
变量
  • 变量定义

var x int

  • 变量初始化

var x, y int = 1, 2

var x, y, z = true, 1, “haha”

  • 短声明变量

在函数中, := 简洁赋值语句在明确类型的地方,可以用于替代 var 定义。

函数外的每个语句都必须以关键字开始( var 、 func 、等等), := 结构不能使用在函数外。

类型
bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 的别名

rune // int32 的别名
     // 代表一个Unicode码

float32 float64

complex64 complex128
变量组

var (
    ToBe   bool       = false
    MaxInt uint64     = 1<<64 - 1
    z      complex128 = cmplx.Sqrt(-5 + 12i)
)

变量的格式化输出

%T 表示类型
%v 表示数值

变量的零值

变量在定义时候,没有被初始化会被复制为零值

字符串 ""

数字 0

bool false
类型转换
var b = T(a)

需要显示的指定T的类型
类型推导

定义一个变量不显示指定其类型,会自动根据右侧变量的类型进行推导

var b = a
则b的类型和a的类型的一致的

常量

常量定义与变量类似,使用const关键字

const str [string] = “haha”

循环控制

for

go只有一个循环控制for

for i:=1; i<100; i++ {
    sum += i
}

for条件不需要用()括起来,但是主体必须用{}

初始化和后置项都是可选的

for ; sum < 100; {

}

for sum < 100 {

}

死循环 for {}

判断条件

if

if x < 0 {
    fmt.Println(x)
}

if x := var_b {}
if {
} else {
}
switch
    switch os := runtime.GOOS; os {
    case "darwin":
        fmt.Println("OS X.")
    case "linux":
        fmt.Println("Linux.")
    default:
        // freebsd, openbsd,
        // plan9, windows...
        fmt.Printf("%s.", os)
    }

没有条件的switch <==> switch true

switch {
    case bool1:
    case bool2:
    default:

}

switch会匹配第一个为true的数值

这种结构最好不要用,用if else 比较合适
defer

defer会延迟函数的执行,直到上层函数返回

延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。

复杂类型

指针
var p *int
var a int = aa
p = &a

go不指针指针运算

结构体

type student struct {
    id int
    name string
}

使用

v := student{1,1}

v.id = 10

结构体指针的引用和结构体对象一样,都是ptr.item

结构体文法

使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。)

type Vertex struct {
    X, Y int
}

var (
    v1 = Vertex{1, 2}  // 类型为 Vertex
    v2 = Vertex{X: 1}  // Y:0 被省略
    v3 = Vertex{}      // X:0 和 Y:0
    p  = &Vertex{1, 2} // 类型为 *Vertex
)

func main() {
    fmt.Println(v1, p, v2, v3)
}

显示结果
{1 2} &{1 2} {1 0} {0 0}
数组

var arr_int [10]int

有10个整数的数组,不能改变大小,从0开始

数组初始化

slice

slice的零值是nil,长度和容量都是0

s := []int{2, 3, 5, 7, 11, 13}

len(s)
slice中包含slice
game := [][]string{
        []string{"abcd", "haha"}
}

strings.join(game[0], " ")
slice切片

s[a,b]

a<=

构造slice

func make(type, size IntergerType) type

指定长度是5的slice

s := make([]int , 5)

还可以指定容量

s:=make([]int, 0, 5)
len(s) = 0
cap(s) = 5

s=s[:cap(s)] len(s)=5, cap(s)=5
向slice中追加元素
func append(s []T, vs ...T) []T

s=append(s, 1,2,3,4)
range

for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。

每次迭代 range 将返回两个值,下标和拷贝

    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }

通过”_”来忽略返回值。

go的多值返回和_忽略返回值和lua比较像似。

map

map必须由make创造

定义
var mp map[string] int;

赋值
mp = make(map[string] int)

mp["wang"]=1


var m = map[string]Vertex{
    "Bell Labs": Vertex{
        40.68433, -74.39967,
    },
    "Google": Vertex{
        37.42202, -122.08408,
    },
}

go语言的赋值都是用的{}

map操作

插入元素
map[key]=v

获得元素
a=map[ke]

删除元素
delete(m, key)

检查key是否存在
a, ok = m[k]

函数值

函数值也可以作为参数进行传递,相当于函数指针

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}
函数闭包

Go 函数可以是一个闭包。闭包是一个函数值,它引用了函数体之外的变量。 这个函数可以对这个引用的变量进行访问和赋值;换句话说这个函数被“绑定”在这个变量上。

对同一个闭包函数引用两次,实际上会生成两个函数闭包对象,互补干扰,如果函数闭包的对象不是公有属性的话。

方法和接口

方法

Go 没有类。然而,仍然可以在结构体类型上定义方法。


type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}

可以对包中的任意类型定义方法,不仅仅是结构体

使用方式如下

func (v type) func_name() type_ret{
    return v+1
}

选择(v type)或者(v *type)作为接收者

使用指针接收者的情况:

  • 避免在每个方法中使用拷贝值

  • 方法可以修改接收者指向的值

接口

接口是一组方法定义的集合


定义

type Abser interface {
    Abs() int
}


使用

var a Abser

f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}

a = f  // a MyFloat 实现了 Abser
a = &v // a *Vertex 实现了 Abser
a = v // error 因为没有定义v的实现

type MyFloat float64  //宏定义

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

错误

Go 程序使用 error 值来表示错误状态。

error是go的一个内建接口,fmt在输出的时候会尝试匹配error

package main

import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}


func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}
Reader接口
Web服务器
图片

并发

goroutine

go的轻量级线程

go f(x,y,z)

goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。sync 提供了这种可能,不过在 Go 中并不经常用到,因为有其他的办法。

channel

channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。

1. 创建channel

channel必须使用make创建

var ch channel = make(chan type)  // type是指管道的类型,可以是int/string等

2. 数据流方向

ch <- v    // 将 v 送入 channel ch。
v := <-ch  // 从 ch 接收,并且赋值给 v。

3. 阻塞情况

默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。
缓冲channel

channel带缓冲,由make第二个数值确定

make(chan int, 100)

    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    fmt.Println(<-ch)
    fmt.Println(<-ch)
channel的遍历和关闭

发送端调用close(c)关闭channel

接收端 v, ok = <-c
通过判断ok的数值来判断发送端是否关闭,ok=false如果发送端关闭了

for i := range c 会循环读取channel直到channel被关闭

只有发送端才能close channel,向一个已经关闭的channel发送数据会造成panic。通常情况下channel不需要关闭,关闭的场景经常是在要终止接收端的range循环的时候使用

select

select使得go的线程在多通道通信的情况下挂起,直到select的子用例可以执行。 当多个子用例可以同时执行的时候,会算计选择一个用例执行.


func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

select 可以增加default选项,来处理其他的case没有准备好的情况下的处理情况。

select {
case <- c:
default:
    todo default
}
互斥

go标准处理互斥使用的是sync.Mutex库

import “sync”

sync.Mutex下有两个方法,Lock和Unlock


// SafeCounter is safe to use concurrently.
type SafeCounter struct {
    v   map[string]int
    mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    c.v[key]++
    c.mux.Unlock()
}

others

自省和反射
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值