ポインタは、メモリアドレスを表す値であり、このメモリアドレスは、多くの場合、メモリに格納されている別の変数の値の開始位置です。
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
、我々が定義したときにポインタ変数 p
、p
変数格納され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 = 999
x
変数のアドレス、動作するように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
中の真ん中には、あなたと同じですか?*p
fmt.Println(*p)
*p
まずそれについて考えてから、簡単な例を見てみましょう。
var x int = 10
var y int = 20
x = y
さて、私たちは、上記のコードを考えるvar y int = 20
中の真ん中同じ同じではないでしょうか?y
x = y
y
結論:同じではない
var y int = 20
y
代表的なメモリ空間は、我々は一般的に、この呼び出し左値と、の代表メモリ空間の内容を、私たちは、一般的にそれを呼び出す、正しい値。x = y
y
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()
、システムは、スタック領域上のスタックフレーム、スタックフレーム内の生成x
とy
2つの変数を。
動作は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()
値が変更されているがあります。