[Golang Advanced]ポインターの詳細な説明

ポインタは、メモリアドレスを表す値であり、このメモリアドレスは、多くの場合、メモリに格納されている別の変数の値の開始位置です。

GoのポインターのサポートはJavaとC / C ++の間で行われます。Javaのようなポインターに対するコードの直接操作をキャンセルしたり、C / C ++でのポインターの悪用を回避したりすることはありません。セキュリティと信頼性の問題。

ポインターアドレスと変数空間

Go言語はポインターを保持しますが、C言語のポインターとは異なります。

  • デフォルト値:nil。
  • 演算子は&、変数のアドレスを取る*のアクセスへのポインタを介してターゲットにオブジェクトを。
  • ポインタ演算をサポートしていない、でサポートされていない->ダイレクトで、事業者.対象部材へのアクセス。

最初にコードを見てください。

package main

import "fmt"

func main(){ 
	var x int = 99
	var p *int = &x

	fmt.Println(p)
}

我々はに実行するとvar x int = 99スペースが生成されます場合は、メモリ内に、このスペースは、我々はそれに名前を与えたx。同時に、それはまた、例えばアドレス、持っている、0xc00000a0c8私たちはこのスペースを使用したい場合は、私たちが使用できるアドレスへの訪問、私たちがそれを訪問するために与えた名前 を使用することもできますx

実行し続けvar p *int = &x、我々が定義したときにポインタ変数 pp変数格納されxたアドレスを。

したがって、ポインタはアドレスであり、ポインタ変数はアドレスを格納する変数です。

その後、我々は、変更x内容を:

package main

import "fmt"

func main() {
	var x int = 99
	var p *int = &x

	fmt.Println(p)

	x = 100

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)
	
	*p = 999

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)
}

それは、見つけることができるx*p同じ結果。

ここで*p呼ばれます解引用间接引用

*p = 999x変数のアドレス、動作するようにx対応するスペースを。

かどうかにかかわら x *p 、私たちは同じ空間を運営しています。

スタックフレームのメモリレイアウト

まず、のメモリレイアウトマップで見てみましょう32位例。

画像

このうち、データ領域には初期化されたデータが格納されます。

上記のコードは、に格納されたスタック領域概してmake()またはnew()アウトに格納されているスタック領域

次に、新しい概念であるスタックフレームについて理解しましょう

スタックフレーム:に使わ機能動作させるメモリを提供するためには、メモリを取るためstackに。

関数が呼び出されるとスタックフレームが生成され、関数が呼び出されるとスタックフレームが解放されます。

では、格納に使用されるスタックフレームは何ですか?

  • ローカル変数
  • 仮パラメーター
  • メモリーフィールド記述値

その中でも、ローカル変数の格納状態に関与する形状は同等です

プログラムが実行されると、最初に実行されmain()、次にスタックフレームが生成されます。

実行しているときvar x int = 99の時間を、スペースがスタックフレームの内部で生成されます。

同様に、実行するためにvar p *int = &x、それはまた、スタックフレーム時間内のスペースを持つことになります。

以下に示すように:

画像

関数を追加して、もう一度勉強してみましょう。

package main

import "fmt"

func test(m int){
	var y int = 66
	y += m
}

func main() {
	var x int = 99
	var p *int = &x

	fmt.Println(p)

	x = 100

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)

	test(11)

	*p = 999

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)
}

、ときに操作下記test(11)場合、フレームは、スタックを生成し続けるmain()生成されるスタックフレームが終わっていません。

画像

ときにtest()あなたの実行が終了すると、それがスタックフレームを緩和します。

画像

ヌルポインターとワイルドポインター

Nullポインタ:初期化されていないポインタ。

var p *int

現時点で、その値を操作したい場合は*p、エラーを報告します。

ワイルドポインター:無効なアドレススペースによって初期化されました。

var p *int = 0xc00000a0c8

ポインタ変数のメモリストレージ

Expressionがnew(T)作成するTことです行わ匿名変数の型をT割り当てられた新しい値を入力し、メモリ空間の一部をクリアし、その後、結果として返さこのメモリアドレス空間、そしてこの結果は、この新たなポイントであるTタイプの値ポインタ値*T返されるポインタ型はです。

new():ヒープ上に位置するメモリを作成し、デフォルト値は、次のような空間データ型のデフォルト値であるp := new(int)ことが*pあります0

package main

import "fmt"

func main(){
	p := new(int)
	fmt.Println(p)
	fmt.Println(*p)
}

この場合、pそれはもはや空ポインタまたはポインタフィールドではありません。

私たちは、単に使用しnew()、そのライフサイクルのメモリまたはどのように移動し、言語メモリ管理システムは、私たちがすべての世話をするのに役立ちますので、それを削除するには気にせずに機能します。

次に*p、値を変更します。

package main

import "fmt"

func main(){
	p := new(int)
	
	*p = 1000
	
	fmt.Println(p)
	fmt.Println(*p)
}

注意を払うにこの時間、*p = 1000の真ん中には、あなたと同じですか?*pfmt.Println(*p)*p

まずそれについて考えてから、簡単な例を見てみましょう。

var x int = 10
var y int = 20
x = y

さて、私たちは、上記のコードを考えるvar y int = 20の真ん中同じ同じではないでしょうか?yx = yy

結論:同じではない

var y int = 20y代表的なメモリ空間は、我々は一般的に、この呼び出し左値と、の代表メモリ空間の内容を、私たちは、一般的にそれを呼び出す、正しい値x = yy

x = y これは、 対応するメモリ空間の内容がxメモリ空間に書き込まれることを意味しy ます。

等号の左側の変数は、変数が指すメモリ空間を表します。これは、書き込み操作と同等です。

等号の右側の変数は、変数メモリスペースに格納されているデータ値を表します。これは、読み取り操作に相当します

これを理解したら、前のコードをもう一度見てみましょう。

p := new(int)

*p = 1000

fmt.Println(*p)

だから、*p = 1000それは1000の意味が書き込まれている*pメモリ行きます。

fmt.Println(*p)ある*pメモリ空間印刷に格納されたデータ値が。

したがって、これら2つは同じではありません。

main()で作成しないとどうなりますか?

func foo() {
	p := new(int)

	*p = 1000
}

我々はすでに、実行時に、言ったfoo()時には、スタックフレームを生成実行の最後、スタックフレームを解放します。

それで、現時点で、それはpまだそこにありますか?

pどこ?スタックフレームはスタック上にあり、pそれがされているため、new()生成されたので、その上p、消えないpので、我々は達成することができ、このことの利点を取る、対応するメモリ値が消えない転送アドレスを

ヒープ領域については、無制限と思われがちですが、無制限の前提としては、利用申請を行い、利用後すぐに解放することです。

関数パラメーター

上記を知っていると、ポインタを関数パラメータとして理解する方がはるかに簡単になります

アドレスを渡す(参照):アドレス値を関数パラメーターとして渡します。

値(データ)を渡す:実パラメーターの値を仮パラメーターにコピーます。

アドレスまたは値が渡されるかどうかに関係なく、その値を仮パラメーターにコピーするのは実際のパラメーターですが、この値はアドレスの場合もデータの場合もあります。

したがって、関数パラメーターは常に値で渡されます。

概念を理解したら、古典的な例を見てみましょう。

package main

import "fmt"

func swap(x, y int){
	x, y = y, x
	fmt.Println("swap  x: ", x, "y: ", y)
}

func main(){
	x, y := 10, 20
	swap(x, y)
	fmt.Println("main  x: ", x, "y: ", y)
}

結果:

swap  x:  20 y:  10
main  x:  10 y:  20

それが異なる理由を簡単に分析してみましょう。

場合最初の実行main()、システムは、スタックフレームがスタックフレームにスタック領域を生成xし、y二つの変数。

実行している場合swap()、システムは、スタック領域上のスタックフレーム、スタックフレーム内の生成xy2つの変数を。

動作はx, y = y, x、交換swap()スタックフレームが生成されるxy値。この場合にはmain()存在しないxy変化。

swap()操作が完了すると、対応するスタックフレームが解放され、スタックフレームのx y値も消えます。

実行しているときに、fmt.Println("main x: ", x, "y: ", y)これらの言葉を、その値は変更されません。

次に、パラメーターがアドレス値である場合の状況を確認します。

アドレスを渡す中心的な考え方は、独自のスタックフレームスペース内の他のスタックフレームスペースの値を変更することです。

値を渡すアイデアは、スタックフレームスペースのスタックフレームスペースの値を変更することです。

違いを理解するように注意してください。

次のコードを引き続き確認します。

package main

import "fmt"

func swap2(a, b *int){
	*a, *b = *b, *a
}

func main(){
	x, y := 10, 20
	swap(x, y)
	fmt.Println("main  x: ", x, "y: ", y)
}

結果:

main  x:  20 y:  10

ここでは違反していなかった函数传参永远都是值传递アドレス値の値を文を、今回。

今回は、xとのy交流の値は完了です。

このプロセスを分析してみましょう。

最初に実行main()スタックフレームを作成した後、あるx y二つの変数が。

ファイル名を指定して実行swap2()時、また、スタックフレームを作成し、そこにあるa b二つの変数が。

なお、このとき、 a 及び b 格納された値 x y アドレス。

実行している場合は*a, *b = *b, *a時間、左側に*a示しxたメモリアドレスを、右側は*b表しyメモリアドレスの内容を。だから、この時間は、main()あるx交換します。

したがって、これは swap2() 操作の main() 変数値です。

swap2()ので、次に、それは問題ではない開放main()値が変更されているがあります。

おすすめ

転載: www.cnblogs.com/BlameKidd/p/12709216.html