3

Go程序设计语言读书笔记

 2 years ago
source link: https://youngxhui.top/2021/05/go%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E8%AF%AD%E8%A8%80%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
package main

i|

重新系统学习 Golang。之前学习Golang都是用什么学什么,不系统,不全面,很多知识点一知半解。这次通过阅读 《Go程序设计语言》这本书来系统的学习一下。

命令行参数

这是我第一次知道 Golang 其实是可以从启动是直接输入参数的,之前看到很多库都是使用 flag,也让我一度认为启动时传入参数必须使用 flag,比较这也好理解,谁让 Golang 的 main 函数和 java 或者 c 不太一样呢?

// java
public static void main(String[] args)

// c
int main(int argc, char *argv[])

Golang 直接 func main() ,想传入参数也不知道如何进行。

原来 Golang 是有个 os.Args 这个方法,Args的底层数据结构是一个字符串切片 var Args []string,这样就方便的获取到从命令行输入的参数。但是这里的 Args[0] 并不是传入的第一个元素,而是该程序的名字。

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println(os.Args[0]) 
    // 输出结果 C:\Users\YOUNGX~1\AppData\Local\Temp\go-build2749142914\b001\exe\echo1.exe
}

所以要获得该程序从命令行传入的参数应该为 Args[1:],这样可以获取全部传入的值。

首先指针未赋值初始化为 nil

官方对其他类型做出了规定 The Zero value

类型 初始值

bool false

numeric 0

string ""

pointers,functions,interfaces nil

slices,channels,map nil

同时指针是可比较的,当且仅当指向同一个变量或者都为 nil 的时候才相同

new()

new() 函数通过传入一个类型,得到该类型的指针。同时每次执行new() 返回的指针值是不一样的。但是有一些例外:两个变量的类型不携带任何信息且是零值,它们有相同的地址,例如struct{}1.16 版本实现好像已经更改。

基本数据类型

Go 的基本数据类型分为四类:基本类型,聚合类型,引用类型和接口类型。

  • 基本类型:数字,字符串,布尔
  • 聚合类型:数组,结构体
  • 引用类型:指针,slice,map,function,函数,通道

字符串是不可变类型。这一点和 Java 是一致的,但是为什么呢?不可变意味着两个字符串可以安全地公用同一段底层内存,使得复制任何长度字符串的开销都低廉。

字符串可以与字节 slice 相互转换。

s := "abc"
b := []byte(s)
s2  := string(b)

字符串操作包

之前听过这样一句话,一个语言的成熟与否要看他对字符串的方法多不多。Golang 提供了以下包对字符串进行处理。bytesstringsstrconvunicode

对于数组来说, [3]int[4]int 是两个不同的数组类型。数组的长度必须是常量表达式(要你有何用?)。

Golang 的数组还挺有意思的,例如下面这样

symbol := [...]int{99:-1}

表示长度为 100 的数组,其中前99个都是 0,最后一个为 -1。

Golang 在数组和其他类型上都是值传递。需要引用传递使用指针。

func zero(ptr *[32]byte) {
	*prt = [32]byte{}
}

Golang 中的数组和 PHP 的数组还有点相似,可以作为k-v使用,但是 k 只能是数。arr := []string{0: "1", 2: "2"} ,这个数组遍历后的结果为如下:

0 1
1 
2 2

会多出一个新的索引 1

Slice

Slice 的底层为一个可变数组。

// src/runtime/slice.go
type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

slice 无法用 == 比较,与 nil 可以,标准库中只有 bytes.Equal 比较两个 byte 的slice。 其他的就要自己实现了。(难道是因为没有泛型,作者要写多个实现?)

Struct

struct 结构体嵌套,而且匿名嵌套

type Point struct {
	x int
	y int
}

type Circle struct {
	Point
	Radius int
}

type Wheel struct {
	Circle
	Spokes int
}

这里的 PointCircle 可以采用这种匿名方式。而调用可以直接调用,像下面这种情况,第3行和第4行为相同的调用。

func main() {
	var w Wheel
	w.x = 5
	w.Circle.Point.x = 5
}

但是不允许在同一个结构体中定义两个相同类型的匿名成员。

匿名函数可以作为函数的参数和返回值。

package main

import "fmt"

func squares() func() int {
	var x int
	return func() int {
		x++
		return x * x
	}
}

func main() {
	f := squares()
	fmt.Println(f())
	fmt.Println(f())
	fmt.Println(f())
	fmt.Println(f())
}

defer

之前只用来作为资源关闭的操作,没想到还有 其他sao 操作。

defer后面可以为一个函数,通过函数调用来实现更多功能。

func bigSlowOperation() {
	defer trace("bigSlowOperation")()
	time.Sleep(10 * time.Second)
}

func trace(msg string) func() {
	start := time.Now()
	log.Printf("enter %s", msg)
	return func() {
		log.Printf("exit %s (%s)", msg, time.Since(start))
	}
}

使用 defer 也有很多需要注意的地方。例如在循环中使用 defer。这个是无效的,并不能及时的回收资源,最好是将循环体和 defer 封装为一个函数,每次调用函数后会执行 defer。

宕机与恢复

宕机会引起程序异常退出。宕机代表的程序执行的终止,但是 Golang 提供了宕机恢复函数 recoverrecover 会终止当前的宕机状态,并且返回宕机值。

方法与函数

Golang 中对方法和函数有定义,emmmm🦤,方法是特殊的函数。

// 函数
func name(parameter-list) (result-list) {
    body
}

// 方法
func (t Type)name(parameter-list) (result-list) {
    body
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK