第十二章: Go語(yǔ)言函數(shù)

golang-gopher.png

1. 概述

函數(shù)是將實(shí)現(xiàn)單一或者相關(guān)聯(lián)功能的代碼組織起來(lái),內(nèi)部實(shí)現(xiàn)具有封閉性的代碼集合,函數(shù)可以提高應(yīng)用程序的模塊性和功能代碼的復(fù)用性.對(duì)大多數(shù)編程語(yǔ)言而言,函數(shù)是很重要的部分.

2. 聲明函數(shù)

Go語(yǔ)言中函數(shù)聲明用 func標(biāo)識(shí)這是一個(gè)函數(shù) 后面跟著 函數(shù)名 參數(shù)列表 返回參數(shù)列表 函數(shù)主體內(nèi)容

// 形式如下
func 函數(shù)名稱(參數(shù)列表) (返回參數(shù)列表) {
    函數(shù)執(zhí)行體
}

對(duì)函數(shù)簡(jiǎn)單說(shuō)明 :

  • 函數(shù)名的命名和我們聲明一個(gè)變量是一樣的,由字母數(shù)字下劃線組成.函數(shù)名不能以數(shù)字開(kāi)頭,同一個(gè)包中函數(shù)名不能重復(fù)

  • 函數(shù)名盡量做到見(jiàn)名知意,函數(shù)名中字母的大小寫(xiě)不同,會(huì)被認(rèn)為是不同的函數(shù)

  • 一個(gè)包中的函數(shù)名首字母大寫(xiě),表示該函數(shù)能被外部包訪問(wèn)到

    //函數(shù)add 和 函數(shù) Add 是兩個(gè)不同的函數(shù)
    func add(){}
    func Add(){}
    
  • 參數(shù)列表 : 表示可以存在多個(gè)形式參數(shù), 每個(gè)形式參數(shù)都是由參數(shù)變量名參數(shù)數(shù)據(jù)類型 組成

    func demo(x int ,y float64, s string){}
    
  • 返回參數(shù)列表 : 可以是返回值的數(shù)據(jù)類型列表,也可以類似參數(shù)列表一樣 返回參數(shù)變量名數(shù)據(jù)類型 的組成,函數(shù)聲明了有返回值,那就必須用 return 語(yǔ)句 提供返回值列表

    func demo1(x int ,y float64)(int,error){
        ...
        ...
        return x+y error 
    }
    
  • 函數(shù)執(zhí)行體 : 能復(fù)用的代碼片段

2.1 函數(shù)基本形式

package main

import (
    "fmt"
    "math"
)
// 基本函數(shù)格式寫(xiě)法
func hypot(x float64 ,y float64) (res float64){
    res = math.Sqrt(x*x+y*y)
    // 因?yàn)榉祷刂德暶髁俗兞縭es ,所以return 默認(rèn)將res返回
    return 
}
// 如果參數(shù)列表中參數(shù)類型相同,可以簡(jiǎn)寫(xiě)
func swop(x,y int,s1,s2 string) (int,int,string,string){
    x,y = y,x
    s1 ,s2 = s1 ,s2
    // Go語(yǔ)言是支持多返回值得
    // 以為返回值列表中沒(méi)有聲明返回變量,所以return要將返回的值寫(xiě)清楚
    // 定義了多少返回值,就必須將它們都返回,否則編譯報(bào)錯(cuò)`not enough arguments to return`
    return x,y,s1,s2
}
func main(){
    // 此處都是函數(shù)調(diào)用
    fmt.Println(hypot(7,9))
    fmt.Println(swop(10,99,"hello","golang"))
}

go run main.go

11.40175425099138
99 10 hello golang

示例:

package main

import "fmt"

const(
    M = 60
    H = M*60
    D = H*24
)
func parseTime(s float64) (m ,h,d float64){
    m = s/M
    h = s/H
    d = s/D
    return
}
func main(){
    s := 1797979
    m,h,d := parseTime(float64(s))
    fmt.Printf("%d秒約等于%f分鐘,%f小時(shí),%f天",s,m,h,d)
}

go run main.go

1797979秒約等于29966.316667分鐘,499.438611小時(shí),20.809942天

2.2 值傳遞與引用傳遞

基本數(shù)據(jù)類型和數(shù)組默認(rèn)是值傳遞,在函數(shù)內(nèi)部修改,不影響原有的值

希望函數(shù)函數(shù)內(nèi)部修改,能影響到原有的變量,那就采用引用傳遞,將變量的地址 &傳給函數(shù),函數(shù)內(nèi)部以指針 形式操作

:star: 不論是值傳遞還是引用傳遞,其實(shí)給函數(shù)的都是變量副本,不同的是,值傳遞的是值得拷貝副本,而引用傳遞則是地址的拷貝,通常地址拷貝因?yàn)閿?shù)據(jù)量小,故效率更高.而值拷貝則是數(shù)據(jù)量越大,效率越差

值傳遞類型 : 所有int類型 , 所有 float類型 , bool類型 , 數(shù)組 , 結(jié)構(gòu)體

引用類型 : 指針 , slice, map , chan , interface 等類型

值傳遞的示例:

package main

import "fmt"

//都是值傳遞,拷貝了一個(gè)副本數(shù)據(jù)
func passValue(n int, f float64, s string, arr [3]int) (int, float64, string,[3]int) {
    arr[1] = arr[1] * 100
    return n * 2, f * f, s + "~~~~" ,arr
}
func main() {
    n := 10
    f := 99.9
    s := "hello"
    arr := [3]int{22, 33, 44}
    // 調(diào)用函數(shù)
    n1, f1, s1 ,arr1:= passValue(n, f, s, arr)
    fmt.Println(n1, f1, s1, arr1)
    // 原來(lái)變量的值沒(méi)有任何影響
    fmt.Println(n, f, s, arr)
}

go run main.go

20 9980.010000000002 hello~~~~ [22 3300 44]
10 99.9 hello [22 33 44]

引用傳遞示例 :

package main

import "fmt"

// 引用傳遞1 數(shù)據(jù)類型本身就時(shí)引用類型
func citePass(sli []string, m map[string]string) {
    sli[0] = "AAA"
    m["job"] = "programmer"
}

// 引用傳遞2 指針形式
func citePass2(n *int) {
    *n = *n +100
}
func main() {
    //定義一個(gè)slice
    var sli []string = []string{"aa", "bb", "cc", "dd"}
    //定義一個(gè)map
    var person = make(map[string]string)
    person["name"] = "tom"
    person["addr"] = "Provence"
    fmt.Printf("sli的len = %d,cap = %d value = %v\n", len(sli), cap(sli), sli)
    fmt.Println(person)
    //執(zhí)行函數(shù)citePass之后
    citePass(sli, person)
    fmt.Printf("sli的len = %d,cap = %d value = %v\n", len(sli), cap(sli), sli)
    fmt.Println(person)
    n := 10
    citePass2(&n)
    fmt.Println(n)
}

go run main.go

sli的len = 4,cap = 4 value = [aa bb cc dd]
map[addr:Provence name:tom]
sli的len = 4,cap = 4 value = [AAA bb cc dd]
map[addr:Provence job:programmer name:tom]
110

3. 函數(shù)變量

在Go語(yǔ)言中,函數(shù)像值一樣擁有類型,可以賦值給變量,傳遞給函數(shù),從函數(shù)中返回

package main

import (
    "fmt"
    "strings"
)

func demo1(n int) int {
    return n * n
}
func demo2(s string) string{
    return strings.ToUpper(s)
}
func main(){
    // 定義一個(gè)函數(shù)變量
    f := demo1
    fmt.Printf("f type = %T\n",f)
    fmt.Println(f(9))
    // 聲明一個(gè)函數(shù)類型的變量 s
    var s func(string)string
    s = demo2
    fmt.Printf("s type = %T\n s = %s",s,s("hello golang"))
}

go run main.go

f type = func(int) int
81
s type = func(string) string
 s = HELLO GOLANG

函數(shù)作為一個(gè)數(shù)據(jù)類型在其他函數(shù)中作為參數(shù)的數(shù)據(jù)類型

package main

import "fmt"

func getSum(x, y int) int {
    return x + y
}
// demo1 的參數(shù)類型是func()類型
func demo1(f func(int,int)int, a int,b int) int {
    res := f(a,b)
    return a+res

}
func main() {
    // 定義一個(gè)函數(shù)類型
    f := getSum
    // 函數(shù)類型作為實(shí)參傳遞
    res := demo1(f,20,80)
    fmt.Println(res)
}

go run main.go

120

函數(shù)作為返回類型

package main

import "fmt"

func getSum(x, y int) int {
    return x + y
}
// 函數(shù)作為返回類型
func demo2() (f func(int,int)int){
    return  getSum
}
func main() {
    rf := demo2()
    fmt.Printf("rf type = %T\n",rf)
    fmt.Println(rf(20,100))
}

go run main.go

rf type = func(int, int) int
120

4. 匿名函數(shù)

匿名函數(shù)就是沒(méi)有函數(shù)名的函數(shù),只有函數(shù)體,匿名函數(shù)具有函數(shù)的一般特性.可以被傳遞,賦值給變量等

4.1 定義形式1

定義匿名函數(shù)時(shí)直接使用

package main

import "fmt"

func main() {
    res := func(x,y int) int {
        return x+y
    }(90,10)
    fmt.Println(res)
}

4.2 定義形式2

將匿名函數(shù)賦值給一個(gè)變量

package main

import "fmt"

func main() {
    sum := func(x,y int) int {
        return x+y
    }
    fmt.Printf("sum type is %T\n",sum)
    fmt.Println(sum(90,90))
}

4.3 定義形式3

定義成全局匿名函數(shù)

package main

import "fmt"

var func1 = func(x,y int) int {
    max :=0
    if x>y{
        max = x
    }else if x<y{
        max = y
    }
    return max
}
func main() {
    fmt.Printf("func1 type is %T\n",func1)
    fmt.Println(func1(90,190))
}

4.4 匿名函數(shù)作為回調(diào)函數(shù)

package main

import "fmt"

func demo(s []int,f func(int)int) []int{
    for k,v:=range s{
         s[k] = f(v)
    }
    return s
}
func main() {
    res := demo([]int{1,2,3,4,5}, func(i int) int {
        return i*i
    })
    fmt.Println(res)
}

4.5 匿名函數(shù)示例

package main

import (
    "flag"
    "fmt"
)
var skillString = flag.String("skill","","programmer have skill")
func main(){
    flag.Parse()
    var skill = map[string]func(){
        "sing": func() {
            fmt.Println("會(huì)唱歌")
        },
        "dance":func(){
            fmt.Println("會(huì)跳舞")
        },
        "rap": func() {
            fmt.Println("會(huì)rap")
        },
        "code": func() {
            fmt.Println("會(huì)寫(xiě)code")
        },
    }
    if f,ok := skill[*skillString];ok{
        f()
    }else{
        fmt.Println("這個(gè)真沒(méi)有...")
    }
}

5. 閉包

閉包是引用了自由變量的函數(shù),被引用的自由變量和函數(shù)一同存在,即使己經(jīng)離開(kāi)了
自由變量的環(huán)境也不會(huì)被釋放或者刪除,在閉包中可以繼續(xù)使用這個(gè)自由變量。因此,簡(jiǎn)
單的說(shuō) :
函數(shù)+引用環(huán)境=閉包

package main

import "fmt"

func counter(n int) func() int{
    // 返回一個(gè)匿名函數(shù),該匿名函數(shù)內(nèi)引用了函數(shù)外的變量n,因此匿名函數(shù)和n構(gòu)成了一個(gè)閉包
    return func() int {
        n++
        return n
    }
}
func main(){
    f := counter(1)
    fmt.Printf("%p\n",f)
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Printf("%p\n",f)
    f1 := counter(10)
    fmt.Printf("%p\n",f1)
    fmt.Println(f1())
    fmt.Println(f1())
    fmt.Println(f1())
    fmt.Printf("%p\n",f1)

}

go run main.go

0x4932a0
2
3
4
0x4932a0
0x4932a0
11
12
13
0x4932a0

另外一個(gè)示例

package main

import (
    "fmt"
    "strings"
)

func demo(s string) func(string) string {
    return func(n string) string {
        if !strings.HasSuffix(n, s) {
            return n + s
        }
        return n
    }
}
func main() {
    d := demo(".png")
    fmt.Println(d("aaa"))
    fmt.Println(d("bbb.png"))
}

go run main.go

aaa.png
bbb.png

6. 函數(shù)的可變參數(shù)

函數(shù)的參數(shù)列表沒(méi)有固定的個(gè)數(shù)的參數(shù)

定義格式如下 :

func 函數(shù)名(固定參數(shù)列表,V ...T)(返回參數(shù)列表){
    函數(shù)執(zhí)行體
}
  • V表示可變參數(shù)變量,T表示參數(shù)的數(shù)據(jù)類型, 那么V的類型是 []T,數(shù)據(jù)類型為T的切片
  • ... 固定語(yǔ)法表示參數(shù)個(gè)數(shù)不確定
  • 可變參數(shù)一般放在參數(shù)列表最尾部

示例 1:

package main

import (
   "fmt"
)

func demo1(s string, sliString ...string) {
   // 可變參數(shù)是數(shù)據(jù)類型為string的切片
   fmt.Printf("%T\n",sliString)
   for _,v:= range  sliString{
       fmt.Println(s+" "+v)
   }
}
func demo2(s ...string) string{
   res := ""
   for _,v:= range s{
       res += v
   }
   return res
}
func main(){
   demo1("hello","ok","nice","yes","good")
   fmt.Println(demo2("Let ","Us ","Go"))
}

go run main.go

[]string
hello ok
hello nice
hello yes
hello good
Let Us Go

示例2: 可變參數(shù)函數(shù)之間參數(shù)傳遞

package main

import "fmt"

func pri (s ...interface{}){
    for _,v := range s{
        fmt.Println(v)
    }
}
func dump(s ...interface{}){
    pri(s...)
}
func main(){
    dump("golang",2.0)
}

示例3: (獲取數(shù)據(jù)類型)

package main

import (
    "bytes"
    "fmt"
)

func getType(s ...interface{}) string {
    var b bytes.Buffer
    for _, v := range s {
        str := fmt.Sprintf("%v", v)
        var typeS string
        switch v.(type) {
        case bool:
            typeS = "bool"
        case int:
            typeS = "int"
        case string:
            typeS = "string"
        case float64:
            typeS = "float64"
        case []string:
            typeS = "slice"
        default:
            typeS = "unknow"
        }
        b.WriteString("value : ")
        b.WriteString(str)
        b.WriteString(" , type : ")
        b.WriteString(typeS)
        b.WriteString("\n")
    }
    return b.String()
}
func main() {
    res := getType(90, "golang", true, 123.234, []string{"demo"})
    fmt.Println(res)
}

go run mian.go

value : 90 , type : int
value : golang , type : string
value : true , type : bool
value : 123.234 , type : float64
value : [demo] , type : slice

7.延遲調(diào)用defer

Go語(yǔ)言中關(guān)鍵字 defer 表示延遲跟在其后面的語(yǔ)句的執(zhí)行,被延遲的語(yǔ)句按照defer調(diào)用的順序逆向執(zhí)行,既:最后調(diào)用的defer語(yǔ)句,優(yōu)先執(zhí)行

  • defer 語(yǔ)句調(diào)用遵照先進(jìn)后出的原則

defer延遲機(jī)制,通常講是為了釋放資源,一些常見(jiàn)的創(chuàng)建資源的操作 打開(kāi)文件,連接數(shù)據(jù)庫(kù),枷鎖 等在最后都需要關(guān)閉資源句柄,斷開(kāi)連接 釋放鎖 等操作,這些都可以交給defer來(lái)做

7.1 defer的執(zhí)行順序

package main

import "fmt"
func demo(s string) {
    fmt.Println(s)
}
func main(){
    fmt.Println("program start")
    defer fmt.Println("one")
    defer demo("two")
    defer func() {
        fmt.Println("three")
    }()
    fmt.Println("program over")
}

go run main.go

program start
program over
three
two
one

7.2 defer 使用示例

示例1 : 文件復(fù)制

從A文件復(fù)制到B文件,B文件不存在就創(chuàng)建

package main

import (
    "fmt"
    "io"
    "os"
)

func copyFile(dstName, srcName string) (written int64, err error) {
    // open打開(kāi)一個(gè)文件用于讀取,讀取成功,返回文件對(duì)象
    src, err := os.Open(srcName)
    // open錯(cuò)誤就直接返回錯(cuò)誤
    if err != nil {
        return
    }
    // defer操作關(guān)閉文件
    defer src.Close()
    //openFile也是打開(kāi)文件的函數(shù),可以使用指定項(xiàng)(os.O_RDONLY 只讀 os.O_CREATE 不存在就創(chuàng)建新文件 )和指定模式打開(kāi)文件
    dst, err := os.OpenFile(dstName, os.O_RDONLY|os.O_CREATE, 0644)
    if err != nil {
        return
    }
    // defer操作關(guān)閉文件
    defer dst.Close()
    // io.copy()函數(shù)將src的數(shù)據(jù)拷貝到dst,直到src上到達(dá)EOF或者錯(cuò)誤
    return io.Copy(dst, src)
}
func main() {
    // 調(diào)用自定義函數(shù)copyFile
    // Args 保存用戶命令行參數(shù) []string 類型
    copyFile(os.Args[1], os.Args[2])
    fmt.Println("賦值完成~~")
}

go run main.go "demo.txt" "english.log"

|_main.go
|_demo.txt
|_english.log

示例2 : 獲取文件大小

package main

import (
    "fmt"
    "os"
)

func getFileSize(filename string) int64{
    f,err :=os.Open(filename)
    defer f.Close()
    if err != nil{
        return 0
    }
    info ,err := f.Stat()
    if err != nil{
        return 0
    }
    size := info.Size()
    return size

}
func main(){
    // 獲取文件的大小(byte)
    fmt.Println(getFileSize(os.Args[1]))
}

8. 錯(cuò)誤處理

錯(cuò)誤處理是幾乎所有編程語(yǔ)言都必須考慮的,Go語(yǔ)言中的錯(cuò)誤就是可能出現(xiàn)錯(cuò)誤的地方真就出現(xiàn)了錯(cuò)誤,我們需要對(duì)這樣的錯(cuò)誤進(jìn)行處理,這么看錯(cuò)誤也是我我們業(yè)務(wù)代碼的一部分,錯(cuò)誤處理也將是開(kāi)發(fā)中必須中的必要環(huán)節(jié).正確的處理錯(cuò)誤,能保障程序更加健壯.

Go語(yǔ)言中引入錯(cuò)誤處理的標(biāo)準(zhǔn)模式 ,error 接口,是Go語(yǔ)言內(nèi)建的接口類型

Go語(yǔ)言中錯(cuò)誤處理有如下的特點(diǎn)

  • 可能造成錯(cuò)誤的函數(shù),在返回值列表中應(yīng)該返回一個(gè)錯(cuò)誤接口 error 如果函數(shù)調(diào)用成功,錯(cuò)誤接口返回的是nil
  • 函數(shù)在調(diào)用之后需要檢查錯(cuò)誤,如果有錯(cuò)誤,需要進(jìn)行錯(cuò)誤處理
package main

import (
    "fmt"
    "os"
)

func getFileSize(name string) (size int64, err error) {
    size = 0
    f, err := os.Open(name)
    if err != nil {
        return size, err
    }
    info, err := f.Stat()
    if err != nil {
        return size, err
    }
    size = info.Size()
    return size, nil
}
func main() {
    // aa.log 文件不存在
    s, e := getFileSize("aa.log")
    if e != nil {
        fmt.Println(e)
    } else {
        fmt.Println(s)
    }
    fmt.Println("go on ...")
}

go run main.go

open aa.log: The system cannot find the file specified.
go on ...

8.1 自定義錯(cuò)誤

Go語(yǔ)言中支持自定義錯(cuò)誤,使用 errors.Newpanic 內(nèi)置函數(shù)

  • errors.New("錯(cuò)誤說(shuō)明") 返回一個(gè)error類型的值,表示一個(gè)錯(cuò)誤
  • panic() 內(nèi)置函數(shù) 可以接收任意類型的值,作為參數(shù),輸出錯(cuò)誤信息,且退出程序
package main

import (
    "errors"
    "fmt"
)

func readConfig(name string) (err error) {
    if name == "config.ini" {
        return nil
    } else {
        return errors.New("讀取配置文件錯(cuò)誤..")
    }
}
func test() {
    err := readConfig("config2.ini")
    if err != nil {
        // 輸出錯(cuò)誤信息,并退出程序
        panic(err)
    }
    fmt.Println("test() go on ..")
}
func main() {
    test()
    fmt.Println("client process go on ...")
}

go run main.go

panic: 讀取配置文件錯(cuò)誤..

goroutine 1 [running]:
main.test()
    /Go/src/GoNote/chapter6/panic-operate/main.go:18 +0x79
main.main()
    /Go/src/GoNote/chapter6/panic-operate/main.go:23 +0x29

8.2 異常的處理

Go語(yǔ)言程序有些錯(cuò)誤只能在運(yùn)行時(shí)檢查,像數(shù)組訪問(wèn)越界,空指針引用等,這些在運(yùn)行會(huì)引起panic異常,一旦發(fā)生panic異常,程序就會(huì)中斷執(zhí)行,并輸出日志,日志包括panic value和函數(shù)調(diào)用的堆棧跟蹤信息

Go語(yǔ)言中處理異常的關(guān)鍵函數(shù)是 defer panic recover

簡(jiǎn)單描述是 : Go語(yǔ)言在運(yùn)行的時(shí)候拋出一個(gè)panic的異常,然后在defer中通過(guò)recover捕獲這個(gè)異常,然后正常的處理掉

8.2.1 觸發(fā)panic

panic 觸發(fā)一般有三種

  1. 手動(dòng)觸發(fā)
  2. 主動(dòng)觸發(fā)
  3. 延遲觸發(fā)

示例 : 手動(dòng)觸發(fā)panic

package main

import "fmt"

func main(){
    fmt.Println("program start ...")
    panic("手動(dòng)觸發(fā)panic")
    fmt.Println("program end ...")
}

go run main.go

panic: 手動(dòng)觸發(fā)panic

goroutine 1 [running]:
main.main()
    E:/Go/src/GoNote/chapter6/demo2/main/main.go:7 +0x9d

示例 : 自動(dòng)觸發(fā)panic

package main

import "fmt"

func main() {
    defer fmt.Println("")
    var arr = [...]string{"zero", "one", "two", "three", "four"}
    length := len(arr)
    for i := 0; i <= length; i++ {
        fmt.Println(arr[i])
    }
}

go run main.go

zero
one
two
three
four

panic: runtime error: index out of range

goroutine 1 [running]:
main.main()
    E:/Go/src/GoNote/chapter6/demo3/main/main.go:10 +0x177

示例 : panic 在defer之后執(zhí)行

package main

import (
    "fmt"
    "time"
)

func demo1() {
    defer func() {
        fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
    }()
}

func main() {
    demo1()
    defer fmt.Println("first ...")
    defer fmt.Println("second ...")
    panic("really over")
}

go run main.go

2019-08-24 16:25:06
second ...
first ...
panic: really over

goroutine 1 [running]:
main.main()
    E:/Go/src/GoNote/chapter6/demo4/main/main.go:18 +0xfd
8.2.2 recover 捕獲異常

無(wú)論是運(yùn)行時(shí)拋出的panic 還是其他形式的panic,我們都可以配合defer和recover實(shí)現(xiàn)異常的捕獲和恢復(fù),讓程序繼續(xù)運(yùn)行下去

本質(zhì)上Go語(yǔ)言中沒(méi)有異常系統(tǒng),使用panic和recover是模擬了其他編程語(yǔ)言中的應(yīng)對(duì)異常的機(jī)制

package main

import (
    "fmt"
    "runtime"
)

func divZero(){
    num1 := 10
    num2 := 0
    res := num1 / num2
    fmt.Println("res=", res)
}
func pointCite(){
    var s *string
    *s = "hello world"
}
func Run(f func()){
    defer func() {
        err := recover()
        switch err.(type) {
        case runtime.Error:
            fmt.Println("runtime error : ",err)
        default:
            fmt.Println(err)
        }
    }()
    f()
}
func main(){
    fmt.Println("start===>")
    Run(divZero)
    fmt.Println("go on===>")
    Run(pointCite)
    fmt.Println("end===>")

}

go run main.go

start===>
runtime error :  runtime error: integer divide by zero
go on===>
runtime error :  runtime error: invalid memory address or nil pointer dereference
end===>

panic 和 recover 的關(guān)系

  • 有panic 沒(méi)有recover 程序直接中斷
  • 有panic 和recover捕獲,程序不會(huì)中斷,從報(bào)panic的點(diǎn)退出后,程序繼續(xù)執(zhí)行后面的流程
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容