構造体でポインタを使用するためにGo言語で押さえておくべきこと

Go言語
この記事は約9分で読めます。
Go初学者
Go初学者

Go言語の構造体で、ポインタの扱いで注意するところはありますか?

この記事では構造体でのポインタを使用方法を理解できます!
  • 構造体のポインタを定義する方法2種類
  • 関数を使用してポインタの動きを確認する

Go言語の構造体を学んでいく上で、構造体におけるポインタを押さえておく必要があります。

基本型のポインタと若干の違いもあります。

ですから、事前知識として基本型のポインタについての理解が必要です。

ポインタの知識が曖昧な方は、当ブログ記事を参考にしてください。

もりぴ
この記事を書いた人

XHTML1.0時代にHTML&CSSを勉強した経験あり。無趣味だった私が2020年5月からプログラミング学習を開始し現在も挫折せずに趣味で学習を楽しんでいる51歳。プログラミングの楽しさをブログを通してお伝えしていきます。

もりぴをフォローする

Go言語の構造体でポインタを定義する2種類の方法

Go言語で構造体でポインタを定義する方法は2種類あります。

それぞれの定義方法を確認していきましょう。

new()関数を使ってポインタ定義する

new() 関数で構造体のポインタを定義する記述方法は下記の通りです。

変数名 := new(型)

では、プログラムで確認していきましょう。

もりぴ
もりぴ

今回の構造体はロールプレイングゲームのキャラクターにしてみました!

// new()関数を使ってポインタ定義
package main

import "fmt"

// 構造体を定義
type Character struct {
    Name      string
    Attribute string
    Hp        int
    Mp        int
    Money     int
}

func main() {
    // new()を使用する
    player1 := new(Character)
    fmt.Println(player1)
    // 実体を出力する
    fmt.Println(*player1)
}
// 出力結果
$ go run main.go 
&{  0 0 0}
{  0 0 0}

&{ 0 0 0}& がポインタを表しています。

そして、初期化していないので各フィールドには型の初期値が代入されています。

& を使ってポインタ定義する

& を使ったポインタ定義の記述方法は下記の通りです。

変数名 := &型{}

では、プログラムで確認しましょう。

// new()関数を使ってポインタ定義
package main

import "fmt"

// 構造体を定義
type Character struct {
    Name      string
    Attribute string
    Hp        int
    Mp        int
    Money     int
}

func main() {
    // new()を使用する
    player1 := new(Character)
    fmt.Println(player1)
    // 実体を出力する
    fmt.Println(*player1)

    // &を使用してポインタ定義
    player2 := &Character{}
    fmt.Println(player2)
    // 実体を出力する
    fmt.Println(*player2)
}
// 出力結果
$ go run main.go 
&{  0 0 0}  // new()を使った出力
{  0 0 0}
&{  0 0 0}  // &を使った出力
{  0 0 0}

どちらも同じ出力結果になっているのを確認できます。

Go初学者
Go初学者

じゃーどっちを使うのがいいの?

POINT

構造体のポインタ定義は & を使う方が一般的で & の方がポインタをイメージしやすいからという見解が多いようです。

構造体を関数で使用してみる

ここで重要なポイントとして、構造体は値渡しの型です。

ここを踏まえた上で、ポインタの基本を学習と同じように2つの関数でプログラムの動きを確認していきましょう。

関数の引数にポインタを使用しない場合

関数の引数にポインタを使用しない場合のプログラムを確認していきます。

その前に、構造体を初期化していきます。

// new()関数を使ってポインタ定義
package main

import "fmt"

// 構造体を定義
type Character struct {
    Name      string
    Attribute string
    Hp        int
    Mp        int
    Money     int
}

func main() {
    // 構造体を初期化
    player3 := Character{
        Name:      "もりぴ",
        Attribute: "勇者",
        Hp:        100,
        Mp:        50,
        Money:     1000,
    }
    fmt.Println(player3)
}
// 出力結果
$ go run main.go 
{もりぴ 勇者 100 50 1000}
もりぴ
もりぴ

構造体定義の復習も兼ねて、初期化終了いたしました!

では、本題の関数の引数にポインタを使用しない(値渡し)プログラムを確認していきましょう。

// new()関数を使ってポインタ定義
package main

import "fmt"

// 構造体を定義
type Character struct {
    Name      string
    Attribute string
    Hp        int
    Mp        int
    Money     int
}

// 関数を定義(値渡し)
func upscore(player Character) {
    fmt.Println(player.Name, "の各ステータスがアップしました!")
    player.Hp += 100
    player.Mp += 50
    player.Money += 500
}

func main() {
    // 構造体を初期化
    player3 := Character{
        Name:      "もりぴ",
        Attribute: "勇者",
        Hp:        100,
        Mp:        50,
        Money:     1000,
    }
    fmt.Println(player3)

    // 値渡しの関数を実行
    upscore(player3)
    fmt.Println(player3)
}
// 出力結果
$ go run main.go 
{もりぴ 勇者 100 50 1000}
もりぴ の各ステータスがアップしました!
{もりぴ 勇者 100 50 1000}
Go初学者
Go初学者

あれ?値が変わっていませんね…

構造体が値渡しなので、player3 のデータをコピーして関数の引数として渡しています。

コピーされたデータなので元のデータには影響しません。

関数の引数にポインタを使用した場合

それでは、関数の引数にデータのアドレスを渡す(参照渡し)ためにポインタを使用してみましょう。

// new()関数を使ってポインタ定義
package main

import "fmt"

// 構造体を定義
type Character struct {
    Name      string
    Attribute string
    Hp        int
    Mp        int
    Money     int
}

// 関数を定義(参照渡し)
func upscore2(player *Character) {
    fmt.Println(player.Name, "の各ステータスがアップしました!")
    player.Hp += 100
    player.Mp += 50
    player.Money += 500
}

func main() {
    // 構造体を初期化
    player3 := Character{
        Name:      "もりぴ",
        Attribute: "勇者",
        Hp:        100,
        Mp:        50,
        Money:     1000,
    }
    fmt.Println(player3)

    // 参照渡しの関数を実行
    upscore2(&player3)
    fmt.Println(player3)
}
// 出力結果
$ go run main.go 
{もりぴ 勇者 100 50 1000}
もりぴ の各ステータスがアップしました!
{もりぴ 勇者 200 100 1500}
もりぴ
もりぴ

無事に私のHP・MP・Moneyの各ステータスがアップしました!

エンジニア
エンジニア

あれ?!関数を定義している変数名の前に * を付けなくてもいいんですか?

もりぴ
もりぴ

いいところに気付きましたね!構造体では省略してもOKです。

基本型での参照渡しで実体にアクセスするには、* が必要でしたが構造体では不要です。

// 関数を定義(参照渡し)
// 基本型の場合は実体にアクセスすのに*が必要
func upscore2(player *Character) {
    fmt.Println(player.Name, "の各ステータスがアップしました!")
    // (* ) は構造体の参照渡しでは省略可
    (*player).Hp += 100
    (*player).Mp += 50
    (*player).Money += 500
}

上記のようにプログラムを記述してもエラーにならずに、同じ出力結果が得られます。

【まとめ】Go言語の構造体でポインタを使用するには?

今回は構造体を関数の引数として使用する場合に必要な、ポインタの定義についてお伝えしていきました。

それでは、今回のまとめにはいります。

この記事で押さえておくこと!
  • new()関数を使ってポインタ定義する 変数名 := new(型)
  • & を使ってポインタ定義する 変数名 := &型{}
  • 構造体でポインタを定義する際には、& を使うのが一般的
  • 構造体は値渡しの型
  • 構造体の場合、関数内で使用する変数名の前に * は不要

以上です。

基本型のポインタの基本を押さえていれば、それほど難しくはなかったのではないでしょうか。

難しく感じた型は、Go言語入門編を振り返ってみましょう。

comment

タイトルとURLをコピーしました