LoginSignup
5
2

More than 3 years have passed since last update.

【Go】ポインタについてまとめてみた

Last updated at Posted at 2021-02-17

はじめに

この記事の対象

  • ポインタってなんとなく聞いたことあるけど詳しく説明できない
  • Goを入門したばかりでポインタの扱い方が分からない

という方を対象としています。
「Goの変数宣言ってどうするの??」って方は一旦そちらを学んでからの方が理解できるかと思います!

また、この記事を読んでくださって、もし「認識が違うよ!」「ここ間違ってるよ!」などがあればご教授ください!
よろしくお願いします!

出てくるワード

この記事に関係するワードとして、

  • 変数
  • メモリ
  • アドレス
  • ポインタ
  • ポインタ型
  • ポインタ変数
  • ポインタ値
  • デリファレンス

などが出てきます。
今分からなくても、それぞれ説明するので安心してください!
(知らないワードがいくつも出たら思考停止しちゃいますよね...。)

変数とメモリとアドレスの関係

それぞれどんなものかをざっくり説明すると、

メモリ...データが入ってくる箱のイメージ。
アドレス...メモリを特定する為の住所のイメージ。メモリにデータが格納される時にメモリに付いてくる。
変数...データを格納するメモリに名前を付けるイメージ。

もうちょっと詳細に説明すると、

var num int = 100

上記のような変数宣言をすると、100と言うデータが、どこかのメモリに保存されます。
すると、この時にそれぞれのメモリを区別する為に番号が振られます。これがアドレスです。
ただ、このアドレスは16進数で表されて分かりにくいので変数と言う名前を付けて、人間に分かりやすくしています。

Goではどういう記法で書くか

例えばメモリを確保するということは、変数宣言すれば良いわけです。
アドレスの参照は、変数の前に&(アンパサンド)を付けてあげればOKです。

package main

import "fmt"

func main() {
  // 変数宣言+値代入
  var num int = 100
  // メモリに格納された値を表示
  fmt.Println(num) // => 100(メモリの値)
  // メモリのアドレスを表示
  fmt.Println(&num) // => 0xc00009a008(16進数のアドレス)
}

【イメージ】
スクリーンショット 2021-02-16 19.50.08.png
【参考】
数値を記憶する - 苦しんで覚えるC言語
変数とメモリの関係 - 苦しんで覚えるC言語

ポインタとは

ポインタってなんでしょう...?

ざっくり説明すると、

「アドレスの値を記憶する変数」のことを一般的には言うみたいです。

もっと詳細に説明

アドレスを記憶する変数 - 苦しんで覚えるC言語」には下記のように書かれています。

世間では、アドレスの値を記憶する変数をポインタと呼んでいますが、
これは本当はあまり正確な呼び方ではありません。
何故なら、ポインタとは、アドレスを扱う機能3つの総称だからです。
ポインタという呼び方は総称であり、正確には3種類に別れています。

ポインタとは、アドレスを扱う機能3つの総称だそうです。
で、このポインタの3つと言うのがポインタ変数ポインタ型ポインタ値です。

まずはこの3つワードの説明。

ポインタ型

int型を「intを記憶する変数」の型、と表現するなら、ポインタ型は「アドレスを記憶する変数」の型です。
int型やstring型などの型の一種です。

ポインタ変数

ポインタ型で宣言された変数のことを指します。int型やstring型の変数と変わりありません。
ポインタ変数が記憶できる値はアドレスです。
他の型の変数と同じく、別のアドレス(ポインタ値)を代入できます。
「記憶しているアドレス」のメモリの値を取得・書き換えが可能です。

ポインタ値

ポインタ変数が記憶できる値(アドレス)のことを指します。

Goではどういう記法で書くか

次に、このポインタに関してGoではどのような書き方をするか見てみましょう。
以下のコードはGoでのポインタの記法です。

// メモリを確保(アドレス付き)
var num int = 100
fmt.Println(num) // => 100

// ポインタ変数宣言+アドレス代入
var pointer *int = &num
fmt.Println(pointer) // => 0xc00009a008

*intがポインタ型、pointerがポインタ変数、&numがポインタ値にあたります。

*int(ポインタ型)の*はポインタ型を表す記号です。もしもstring型の値が格納されたアドレスを代入した場合は*stringとなります。
&num(ポインタ値)は、先ほど説明した通りアドレスのことです。この場合、numのアドレス。

var pointer *int = &numポインタ変数の宣言アドレス代入を行っています。

変数pointerには変数numのアドレスを代入しているので、当然「変数pointerのメモリの値」と「変数numのアドレス」は同じ値になります。

var num int = 100
fmt.Printf("value=%v type=%T", &num, num)        // => value=0xc00009a008 type=int

var pointer *int = &num
fmt.Printf("value=%v type=%T", pointer, pointer) // => value=0xc00009a008 type=*int

イメージ
スクリーンショット 2021-02-17 12.39.51.png

ポインタ変数から値にアクセスする

繰り返しになりますがポインタ変数にはポインタ値、つまりアドレスが記憶されています。
しかし、このポインタ変数の前に*を付けるとポインタ変数に記憶されているアドレスの中身の値にアクセスすることが可能になります。

var num int = 100
var pointer *int = &num

fmt.Println(num) // => 100
fmt.Println(*pointer) // => 100

イメージ
スクリーンショット 2021-02-17 16.17.17.png

デリファレンス

いきなりですが、ここで質問です。
以下のコードはどのような値が出力されるでしょうか??

問題
package main

import "fmt"

func main() {
  var num int = 100
  var pointer *int = &num
  *pointer = 999
  fmt.Println(num)     // => ???
  fmt.Println(*pointer) // => ???
}

正解は
.
.
.

正解
fmt.Println(num)      // => 999
fmt.Println(*pointer) // => 999

どちらも「999」になります。どちらもです!

解説
まず変数pointerはポインタ変数です。
ポインタ変数にはアドレスが記憶されるんでしたね。

で、*pointerですが、ポインタ変数の前に*を付けるとポインタ変数に記憶されているアドレスの中身の値にアクセスできると説明しました。

結局*pointer = 999は何をしているかと言うと、*pointerつまり、記憶されているアドレスの中身の値つまり、numのメモリの値を999に変更している。と言うことです。
なので、どちらも999になると言う訳です。
スクリーンショット 2021-02-17 18.39.39.png

ちなみに、「ポインタ変数*pointerは記憶されているアドレスの中身の値にアクセスできる」と言いましたが、このことをデリファレンスと呼びます。

参照元から参照先の値を得ることを特に指す語

引用元:間接参照 - Wikipedia

雑談:学んでいて面白いと思った部分

C言語(Goも)って参照渡しってのはできない

C言語(Goも)って参照渡しってのはできないんですね。

C言語では、あくまでも値渡ししか出来ないのですが、
アドレスを渡すことを慣習的に参照渡しと呼ぶことがあります。

&付き変数の正体はアドレスを求める演算子

ちなみに、&付き変数の正体についてはこんな記述がありました。

この&付き変数の正体は、非常に単純です。
実は、&は変数のアドレスを求める演算子なのです。

参照:&つけが必要な変数の正体

まあまあ&に関しては気になっていたので紹介しました。

以上です。

まとめ

逃げてきたポインタの理解をする為にこの記事を書いていたのですが、調べだすとなかなか面白い領域だと感じました!
知らないことを知っていくって面白いですね〜。(しんどい時もあるけど...)

もし、認識が間違っているとか、「ここどう言うこと?」って言うのがあれば是非教えて下さい!
よろしくお願いします!

それでは、これからも一緒に良いエンジニアライフを歩みましょ〜:fist:
お疲れ様でした!:wave:

参考にさせて頂いたサイト

どのサイトもまとめ方が上手くて憧れます。そしてポインタの理解にとても役立ちました。
本当にありがとうございます!

数値を記憶する - 苦しんで覚えるC言語
変数とメモリの関係 - 苦しんで覚えるC言語
ポインタ (pointer)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
ポインタ変数とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
Goで学ぶポインタとアドレス
アドレスを記憶する変数 - 苦しんで覚えるC言語
C言語のポインタ構文のつまづきどころ
デリファレンス (dereference)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
間接参照 - Wikipedia
&つけが必要な変数の正体 - 苦しんで覚えるC言語

5
2
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
5
2