19
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Go7Advent Calendar 2019

Day 6

Goのポインタのポインタ

Last updated at Posted at 2019-12-05

この記事は Go7 Advent Calendar 2019 の 6 日目の記事です。

はじめに

Go ではポインタ型としてポインタを扱うことができます。Go のポインタのポインタの Tips を共有します。Devquizです。

Q: 以下はの標準出力はいずれも **Num 型の値を表示しますが、1, 2, 3 それぞれで何がどのように表示されるでしょうか??

package main

import (
	"fmt"
)

type Num struct {
	i int
}

func main() {
	np := &Num{i: 3}

	// 1: main 関数での出力
	fmt.Printf("%p\n", &np)
	
	// 2: pointer 関数での出力
	pointer(np)
	
	// 3: pointerpointer 関数での出力
	pointerpointer(&np)
}

func pointer(np *Num) {
	fmt.Printf("%p\n", &np)
}

func pointerpointer(npp **Num) {
	fmt.Printf("%p\n", npp)
}

 
 
 
 
 
 
 
 
 
 
  
 
 
 
 
 
 
 
 
  
 
 
 
 
 
 
 
 
  
 
 
 
 
 
 
 
 
  
 
 
 
 
 
 
 
 
  
 
 
 
 
 
 
 
 
 
 
 
 
  
 
 
 
 
 
 

答え

0x40c138
0x40c148
0x40c138

**Num 型だけど全部同じアドレスではないです。(それはそうなのですが)

何を表示しているのか

何を表示しているのか順番に確認していきましょう。

1: main 関数での出力

まず np := &Num{i: 3} についてです。これは無名変数 Num{i: 3} へのポインタです。コンパイラによって最適化されない限り、仮想メモリ上に確保されます、メモリ構造を簡易的に図にすると以下になっています。

image.png

Num{i: 3} もメモリ上に確保されています。Go Playground で fmt.Printf("%p\n", np) としてポインタのアドレスを確認してみます。

func main() {
	np := &Num{i: 3}
	fmt.Printf("%p\n", &np)
+	fmt.Printf("%p\n", np) // 0x40e020
	pointer(np)
	pointerpointer(&np)
}

すると 0x40e020 であることが分かります。

image.png

よって以下では **Num 型の変数 0x40c138 が出力されることになります。

fmt.Printf("%p\n", &np)

2: pointer 関数での出力

以下の関数について考えてみます。ここでは *Num を引数として渡しています。*Num は何だったかというと Num のアドレスを保持している変数でした。ポインターで示している アドレスは値で渡されます。よってアドレスの値を格納する変数は、元の変数を格納しているアドレスとは別にメモリ上に確保されます。

よって以下では 0x40c138 ではなく別のアドレス値 0x40c148 が出力されています。

func pointer(np *Num) {
	fmt.Printf("%p\n", &np)
}

image.png

3: pointerpointer 関数での出力

以下は Num のポインターへのポインターでした。図にするとわかりやすいです。

func pointerpointer(npp **Num) {
	fmt.Printf("%p\n", npp)
}

image.png

この **Num の値を格納する変数もメモリ上に確保されているので、そのアドレスを表示することができます。これも Go Playground に追加して確認しておきます。

func pointerpointer(npp **Num) {
	fmt.Printf("%p\n", npp)
+	fmt.Printf("%p\n", &npp) // 0x40c150
}

すると 0x40c150 であることが分かります。つまり以下です。こちらも 2 の場合と同様にもとのアドレス 0x40c138 とは別のアドレス 0x40c150 が割り当てられていることがわかります。

image.png

まとめ

  • ポインタ値は値

参考

19
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?