Go の基本文法に関する 10 の質問

1.リカバリ実行タイミング

なし。リカバリは遅延関数で実行する必要があります。reverse は、祖父母レベルで呼び出された場合に例外をキャプチャしますが、直接呼び出された場合は無効です。

func main() {
    
    
    recover()
    panic(1)
}

直接の遅延呼び出しも無効です。

func main() {
    
    
    defer recover()
    panic(1)
}

defer が呼び出されたとき、複数レベルのネストは依然として無効です。

func main() {
    
    
    defer func() {
    
    
        func() {
    
     recover() }()
    }()
    panic(1)
}

有効にするには、defer 関数内で直接呼び出す必要があります。

func main() {
    
    
    defer func() {
    
    
        recover()
    }()
    panic(1)
}

2. クロージャが同じ変数を誤って参照する問題に対処する方法

各反復で在这里插入代码片ローカル。i := i 行がないと、同じ変数が出力されます。

func main() {
    
    
    for i := 0; i < 5; i++ {
    
    
        i := i
        defer func() {
    
    
            println(i)
        }()
    }
}

または、関数パラメータを通じて i を渡します。

func main() {
    
    
    for i := 0; i < 5; i++ {
    
    
        defer func(i int) {
    
    
            println(i)
        }(i)
    }
}

3. defer ステートメントがループ内で実行されるとどうなりますか?

defer は関数が終了するときにのみ実行できます。for で defer を実行すると、リソースの解放が遅れます。

func main() {
    
    
    for i := 0; i < 5; i++ {
    
    
        func() {
    
    
            f, err := os.Open("/path/to/file")
            if err != nil {
    
    
                log.Fatal(err)
            }
            defer f.Close()
        }()
    }
}

func はローカル関数なので、ローカル関数で defer を実行しても問題ありません。

4.Goroutine リークを回避するための手段を指定します。

メモリ リークは、コンテキスト パッケージを通じて回避できます。

func main() {
    
    
    ctx, cancel := context.WithCancel(context.Background())

    ch := func(ctx context.Context) <-chan int {
    
    
        ch := make(chan int)
        go func() {
    
    
            for i := 0; ; i++ {
    
    
                select {
    
    
                case <- ctx.Done():
                    return
                case ch <- i:
                }
            }
        } ()
        return ch
    }(ctx)

    for v := range ch {
    
    
        fmt.Println(v)
        if v == 5 {
    
    
            cancel()
            break
        }
    }
}

以下の for ループはデータのフェッチを停止すると、cancel 関数を使用して別のコルーチンによるデータの書き込みを停止します。以下の for がデータの読み取りを停止し、上記の for ループがまだ書き込みを行っている場合、メモリ リークが発生します。

5. for select ループから抜け出す方法

通常、for ループでは、break を使用してループを抜け出すことができますが、Go 言語では、for select が使用されている場合は、break を使用してループを抜け出すことができないことに注意してください。

func testSelectFor2(chExit chan bool){
    
    
 EXIT:
    for  {
    
    
        select {
    
    
        case v, ok := <-chExit:
            if !ok {
    
    
                fmt.Println("close channel 2", v)
                break EXIT//goto EXIT2
            }

            fmt.Println("ch2 val =", v)
        }
    }

    //EXIT2:
    fmt.Println("exit testSelectFor2")
}

6. スライスで見つける方法

sort.searchXXX メソッドは go でソートされたスライス内の指定されたメソッドを検索するために使用されますが、その戻り値は、対応する検索要素が存在しない場合に挿入される位置の添字です (要素は返された添字の前に挿入されます)。

この目的は、以下の機能をカプセル化することで実現できます。

func IsExist(s []string, t string) (int, bool) {
    
    
    iIndex := sort.SearchStrings(s, t)
    bExist := iIndex!=len(s) && s[iIndex]==t

    return iIndex, bExist
}

7. 入れ子構造を使用して構造体を初期化する方法

go の哲学は、継承より結合が優れているということです。結合は、構造体の入れ子を使用することで完了できます。埋め込まれた構造体のプロパティは、外部構造体のプロパティと同様で、直接呼び出すことができます。
なお、外部構造体を初期化する場合は、埋め込み構造体の構造体の初期化を指定する必要があり、以下のように、s1メソッドではエラーが報告され、s2メソッドは正常です。

type stPeople struct {
    
    
    Gender bool
    Name string
}

type stStudent struct {
    
    
    stPeople
    Class int
}

//尝试4 嵌套结构的初始化表达式
//var s1 = stStudent{false, "JimWen", 3}
var s2 = stStudent{
    
    stPeople{
    
    false, "JimWen"}, 3}
fmt.Println(s2.Gender, s2.Name, s2.Class)

8. スライスとアレイの違い

配列は、同じデータ型の 0 個以上の要素からなる固定長のシーケンスです。配列の長さは配列型の一部であるため、[3]int と [4]int は 2 つの異なる配列型です。配列のサイズを指定する必要があります。指定しない場合、サイズは初期化に基づいて自動的に計算され、変更できません。配列は値によって渡されます。配列は、同じ型のデータのコレクションである組み込み型であり、値型であり、要素の値は 0 から始まる添字インデックスを通じてアクセスされます。長さは初期化後に固定され、変更できません。
パラメータとしてメソッドに渡されると、同じポインタを参照するのではなく、配列のコピーがコピーされます。配列の長さもその型の一部であり、その長さは組み込み関数 len(array) を通じて取得されます。配列定義:

var array [10]int

var array =[5]int{
    
    1,2,3,4,5}

スライスは、同じタイプの要素の可変長シーケンスを表します。スライスは、ポインタ、長さ、容量の 3 つのプロパティを持つ軽量のデータ構造です。スライスのサイズを指定する必要はありません。スライスはアドレスによって渡されます。スライスは配列または組み込み関数 make() を通じて初期化できます。初期化時はlen=capとなり、要素追加時に容量capが足りない場合はlenの2倍に容量が拡張されます。スライスの定義:

var slice []type = make([]type, len)

9.新品とメーカーの違い

new の機能は、ポインターを型 (*T) に初期化することです。新しい関数は組み込み関数です。関数定義: func new(Type) Type。新しい関数を使用してスペースを割り当てます。新しい関数に渡されるのは値ではなく型です。戻り値は、この新しく割り当てられたゼロ値へのポインタです。
make の機能は、スライス、マップ、またはチャンの参照 (T) を初期化して返すことです。make 関数は組み込み関数です。関数定義: func make(Type, size IntegerType) Type; 最初のパラメーターは型、2 番目のパラメーターは長さ、戻り値は型です。
make(T, args) 関数には、new(T) とは異なる目的があります。これはスライス、マップ、およびチャネルを作成するためにのみ使用され、戻り値の型はT ( T ではなく
)の初期化された (ゼロ値ではない) インスタンスです。

10.Printf()、Sprintf()、Fprintf() 関数の違いと使用法は何ですか?

これらはすべてフォーマットされた文字列を出力しますが、出力対象は異なります。
Printf() は、フォーマット文字列を標準出力 (通常は画面、リダイレクト可能) に出力します。Printf() は標準出力ファイル (stdout) に関連付けられていますが、Fprintf にはこの制限がありません。
Sprintf() はフォーマット文字列を指定された文字列に出力するため、パラメータは printf よりも char* 1 つ多くなります。それがターゲット文字列アドレスです。
Fprintf() は指定されたファイルデバイスにフォーマット文字列を出力するため、パラメータは printf より 1 つ多いファイルポインタ FILE* になります。主にファイル操作に使用されます。Fprintf() は、出力をストリーム (通常はファイル) にフォーマットします。

おすすめ

転載: blog.csdn.net/m0_73728511/article/details/133471634