ホームページ バックエンド開発 Golang Go 配列の仕組みと For-Range の扱いが難しい

Go 配列の仕組みと For-Range の扱いが難しい

Aug 20, 2024 pm 06:44 PM

How Go Arrays Work and Get Tricky with For-Range

Go 配列の仕組みと For-Range の扱いが難しい

これは投稿の抜粋です。投稿全文はこちらからご覧いただけます: How Go Arrays Work and Get Tricky with For-Range.

古典的な Golang の配列とスライスは非常に簡単です。配列は固定サイズであり、スライスは動的です。ただし、言っておきたいのは、Go は表面的には単純に見えるかもしれませんが、内部では多くのことが起こっているということです。

いつものように、基本から始めて、それからもう少し深く掘り下げていきます。心配しないでください。配列はさまざまな角度から見ると非常に興味深いものになります。

スライスについては次のパートで説明します。準備ができたらここにドロップします。

配列とは何ですか?

Go の配列は他のプログラミング言語の配列とよく似ています。サイズは固定されており、同じタイプの要素を連続したメモリ位置に保存します。

これは、アドレスが配列の開始アドレスと要素のインデックスに基づいて計算されるため、Go が各要素にすばやくアクセスできることを意味します。

func main() {
    arr := [5]byte{0, 1, 2, 3, 4}
    println("arr", &arr)

    for i := range arr {
        println(i, &arr[i])
    }
}

// Output:
// arr 0x1400005072b
// 0 0x1400005072b
// 1 0x1400005072c
// 2 0x1400005072d
// 3 0x1400005072e
// 4 0x1400005072f
ログイン後にコピー

ここで注意すべき点がいくつかあります:

  • 配列 arr のアドレスは、最初の要素のアドレスと同じです。
  • 要素タイプがバイトであるため、各要素のアドレスは互いに 1 バイト離れています。

How Go Arrays Work and Get Tricky with For-Range

メモリ内の配列 [5] バイト{0, 1, 2, 3, 4}

画像をよく見てください。

私たちのスタックは、上位のアドレスから下位のアドレスへと下向きに成長していますよね?この図は、arr[4] から arr[0] まで、スタック内で配列がどのように見えるかを正確に示しています。

ということは、最初の要素 (または配列) のアドレスと要素のサイズがわかれば、配列の任意の要素にアクセスできるということですか? int 配列と安全でないパッケージを使用してこれを試してみましょう:

func main() {
    a := [3]int{99, 100, 101}

    p := unsafe.Pointer(&a[0])

    a1 := unsafe.Pointer(uintptr(p) + 8)
    a2 := unsafe.Pointer(uintptr(p) + 16)

    fmt.Println(*(*int)(p))
    fmt.Println(*(*int)(a1))
    fmt.Println(*(*int)(a2))
}

// Output:
// 99
// 100
// 101
ログイン後にコピー

最初の要素へのポインタを取得し、int のサイズ (64 ビット アーキテクチャでは 8 バイト) の倍数を加算して次の要素へのポインタを計算します。次に、これらのポインターを使用してアクセスし、int 値に変換します。

How Go Arrays Work and Get Tricky with For-Range

メモリ内の配列 [3]int{99, 100, 101}

この例は、教育目的でメモリに直接アクセスするための安全でないパッケージを試してみたものです。結果を理解せずに運用環境でこれを実行しないでください。

型 T の配列それ自体は型ではありませんが、特定のサイズと型 T を持つ配列は型とみなされます。私が言いたいのは次のとおりです:

func main() {
    a := [5]byte{}
    b := [4]byte{}

    fmt.Printf("%T\n", a) // [5]uint8
    fmt.Printf("%T\n", b) // [4]uint8

    // cannot use b (variable of type [4]byte) as [5]byte value in assignment
    a = b 
}
ログイン後にコピー

a と b は両方ともバイトの配列ですが、Go コンパイラーはそれらを完全に異なる型として認識します。%T 形式によりこの点が明確になります。

Go コンパイラーが内部でこれをどのように認識するかは次のとおりです (src/cmd/compile/internal/types2/array.go):

// An Array represents an array type.
type Array struct {
    len  int64
    elem Type
}

// NewArray returns a new array type for the given element type and length.
// A negative length indicates an unknown length.
func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} }
ログイン後にコピー

配列の長さは型自体で「エンコード」されるため、コンパイラーは配列の長さをその型から認識します。あるサイズの配列を別のサイズに割り当てたり、比較しようとすると、型の不一致エラーが発生します。

配列リテラル

Go では配列を初期化する方法がたくさんありますが、そのうちのいくつかは実際のプロジェクトではめったに使用されない可能性があります。

var arr1 [10]int // [0 0 0 0 0 0 0 0 0 0]

// With value, infer-length
arr2 := [...]int{1, 2, 3, 4, 5} // [1 2 3 4 5]

// With index, infer-length
arr3 := [...]int{11: 3} // [0 0 0 0 0 0 0 0 0 0 0 3]

// Combined index and value
arr4 := [5]int{1, 4: 5} // [1 0 0 0 5]
arr5 := [5]int{2: 3, 4, 4: 5} // [0 0 3 4 5]
ログイン後にコピー

上で行っていること (最初のものを除く) は、値の定義と初期化の両方であり、これは「複合リテラル」と呼ばれます。この用語は、スライス、マップ、構造体にも使用されます。

ここで興味深いことに、要素が 4 つ未満の配列を作成すると、Go は値を 1 つずつ配列に入れる命令を生成します。

arr := [3]int{1, 2, 3, 4} を実行すると、実際に何が起こっているかは次のとおりです。

arr := [4]int{}
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
ログイン後にコピー

この戦略はローカルコードの初期化と呼ばれます。これは、初期化コードがグローバルまたは静的な初期化コードの一部ではなく、特定の関数のスコープ内で生成および実行されることを意味します。

以下の別の初期化戦略を読むと、このように値が 1 つずつ配列に配置されるわけではないことがより明確になります。

「要素数が 4 つを超える配列はどうなりますか?」

コンパイラは、バイナリで配列の静的表現を作成します。これは、「静的初期化」戦略として知られています。

This means the values of the array elements are stored in a read-only section of the binary. This static data is created at compile time, so the values are directly embedded into the binary. If you're curious how [5]int{1,2,3,4,5} looks like in Go assembly:

main..stmp_1 SRODATA static size=40
    0x0000 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
    0x0010 03 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ................
    0x0020 05 00 00 00 00 00 00 00                          ........
ログイン後にコピー

It's not easy to see the value of the array, we can still get some key info from this.

Our data is stored in stmp_1, which is read-only static data with a size of 40 bytes (8 bytes for each element), and the address of this data is hardcoded in the binary.

The compiler generates code to reference this static data. When our application runs, it can directly use this pre-initialized data without needing additional code to set up the array.

const readonly = [5]int{1, 2, 3, 4, 5}

arr := readonly
ログイン後にコピー

"What about an array with 5 elements but only 3 of them initialized?"

Good question, this literal [5]int{1,2,3} falls into the first category, where Go puts the value into the array one by one.

While talking about defining and initializing arrays, we should mention that not every array is allocated on the stack. If it's too big, it gets moved to the heap.

But how big is "too big," you might ask.

As of Go 1.23, if the size of the variable, not just array, exceeds a constant value MaxStackVarSize, which is currently 10 MB, it will be considered too large for stack allocation and will escape to the heap.

func main() {
    a := [10 * 1024 * 1024]byte{}
    println(&a)

    b := [10*1024*1024 + 1]byte{}
    println(&b)
}
ログイン後にコピー

In this scenario, b will move to the heap while a won't.

Array operations

The length of the array is encoded in the type itself. Even though arrays don't have a cap property, we can still get it:

func main() {
    a := [5]int{1, 2, 3}
    println(len(a)) // 5
    println(cap(a)) // 5
}
ログイン後にコピー

The capacity equals the length, no doubt, but the most important thing is that we know this at compile time, right?

So len(a) doesn't make sense to the compiler because it's not a runtime property, Go compiler knows the value at compile time.

...

This is an excerpt of the post; the full post is available here: How Go Arrays Work and Get Tricky with For-Range.

以上がGo 配列の仕組みと For-Range の扱いが難しいの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Golang vs. Python:パフォーマンスとスケーラビリティ Golang vs. Python:パフォーマンスとスケーラビリティ Apr 19, 2025 am 12:18 AM

Golangは、パフォーマンスとスケーラビリティの点でPythonよりも優れています。 1)Golangのコンピレーションタイプの特性と効率的な並行性モデルにより、高い並行性シナリオでうまく機能します。 2)Pythonは解釈された言語として、ゆっくりと実行されますが、Cythonなどのツールを介してパフォーマンスを最適化できます。

Golang and C:Concurrency vs. Raw Speed Golang and C:Concurrency vs. Raw Speed Apr 21, 2025 am 12:16 AM

Golangは並行性がCよりも優れていますが、Cは生の速度ではGolangよりも優れています。 1)Golangは、GoroutineとChannelを通じて効率的な並行性を達成します。これは、多数の同時タスクの処理に適しています。 2)Cコンパイラの最適化と標準ライブラリを介して、極端な最適化を必要とするアプリケーションに適したハードウェアに近い高性能を提供します。

ゴーを始めましょう:初心者のガイド ゴーを始めましょう:初心者のガイド Apr 26, 2025 am 12:21 AM

goisidealforforbeginnersandsutable forcloudnetworkservicesduetoitssimplicity、andconcurrencyfeatures.1)installgofromtheofficialwebsiteandverify with'goversion'.2)

Golang vs. C:パフォーマンスと速度の比較 Golang vs. C:パフォーマンスと速度の比較 Apr 21, 2025 am 12:13 AM

Golangは迅速な発展と同時シナリオに適しており、Cは極端なパフォーマンスと低レベルの制御が必要なシナリオに適しています。 1)Golangは、ごみ収集と並行機関のメカニズムを通じてパフォーマンスを向上させ、高配列Webサービス開発に適しています。 2)Cは、手動のメモリ管理とコンパイラの最適化を通じて究極のパフォーマンスを実現し、埋め込みシステム開発に適しています。

Golang vs. Python:重要な違​​いと類似点 Golang vs. Python:重要な違​​いと類似点 Apr 17, 2025 am 12:15 AM

GolangとPythonにはそれぞれ独自の利点があります。Golangは高性能と同時プログラミングに適していますが、PythonはデータサイエンスとWeb開発に適しています。 Golangは同時性モデルと効率的なパフォーマンスで知られていますが、Pythonは簡潔な構文とリッチライブラリエコシステムで知られています。

GolangとC:パフォーマンスのトレードオフ GolangとC:パフォーマンスのトレードオフ Apr 17, 2025 am 12:18 AM

GolangとCのパフォーマンスの違いは、主にメモリ管理、コンピレーションの最適化、ランタイム効率に反映されています。 1)Golangのゴミ収集メカニズムは便利ですが、パフォーマンスに影響を与える可能性があります。

パフォーマンスレース:ゴラン対c パフォーマンスレース:ゴラン対c Apr 16, 2025 am 12:07 AM

GolangとCにはそれぞれパフォーマンス競争において独自の利点があります。1)Golangは、高い並行性と迅速な発展に適しており、2)Cはより高いパフォーマンスと微細な制御を提供します。選択は、プロジェクトの要件とチームテクノロジースタックに基づいている必要があります。

Golang vs. Python:長所と短所 Golang vs. Python:長所と短所 Apr 21, 2025 am 12:17 AM

GolangisidealforBuildingsCalables Systemsduetoitsefficiency andConcurrency、Whilepythonexcelsinquickscriptinganddataanalysisduetoitssimplicityand vastecosystem.golang'ssignencouragesclean、readisinediteNeditinesinedinediseNabletinedinedinedisedisedioncourase

See all articles