Go言語の構造体とポインタの関係!ポインタを使ったアクセス方法
生徒
「Go言語の構造体にポインタってどうやって使うんですか?」
先生
「構造体とポインタを組み合わせると、同じ変数を効率よく使い回すことができます。変更も共有されるので便利ですよ。」
生徒
「それってメモリとか関係してますか?」
先生
「はい、ポインタは“メモリの住所”のようなものです。詳しく説明していきましょう!」
1. ポインタとは?初心者向けにかんたん解説
ポインタ(pointer)とは、「変数が保存されている場所(メモリの番地)」を指し示す特別な変数です。ふつうの変数は数字や文字などの値そのものを入れていますが、ポインタはその値が置かれている場所を覚えています。
イメージで言うと、自分の家の場所を教える「住所」がポインタで、家の中にある家具などの中身が「値」です。住所だけ知っていれば、あとから家に行って中身を見たり並べ替えたりできますよね。同じように、ポインタがあれば変数の中身を直接変えることができます。
これがなぜ便利なのかというと、ポインタを使えば大きなデータを丸ごとコピーする必要がなくなり、プログラムが効率よく動くようになるからです。特に構造体のように複数の値をまとめて持つデータでは、ポインタがとても役に立ちます。
まずは、ポインタが「変数の場所を覚えているだけ」という点だけ理解できれば大丈夫です。下の短いサンプルを見ると、普通の変数とポインタ変数の違いが少しイメージしやすくなります。
num := 10
p := &num
fmt.Println(num) // 10
fmt.Println(*p) // 10(ポインタを使って中身を取り出せる)
&numで変数numの場所を取得し、*pを使うとその場所の中身を読み取れます。「場所を覚える」と「場所の中身を見る」という二段階がポインタの基本です。
2. 構造体にポインタを使う理由とは?
構造体は、名前や年齢など複数の値をひとまとめにした入れ物です。これを関数に渡すとき、ポインタを使うと効率が良く、意図どおりに更新できます。値そのものを渡すと毎回「丸ごとコピー」されますが、ポインタは「場所」を渡すだけなので無駄なコピーを避けられます。さらに同じデータを複数の関数で共有しやすくなり、変更結果が呼び出し元にも自然に反映されます。
type User struct {
Name string
Age int
}
// 値渡し:関数内で増やしても元の値は変わらない(コピーを操作)
func addOneByValue(u User) {
u.Age++
}
// ポインタ渡し:元の値を直接変える(場所を操作)
func addOneByPointer(u *User) {
u.Age++
}
func main() {
u := User{Name: "たろう", Age: 20}
addOneByValue(u)
// u.Age は 20 のまま(コピーを変更しただけ)
addOneByPointer(&u)
// u.Age は 21 に更新される(実体を変更)
}
まとめると、「大きな構造体を頻繁に渡す」「呼び出し元のデータを更新したい」「存在しない状態を表すために空(nil)を扱いたい」といった場面ではポインタが有利です。まずは「コピーを避けたいとき」「結果を反映したいとき」にポインタを選ぶ、と覚えておけば実務でも迷いにくくなります。
3. 構造体のポインタを作る基本文法
まずは構造体と、そのポインタを使ってみましょう。
type User struct {
Name string
Age int
}
func main() {
u := User{Name: "たろう", Age: 25}
ptr := &u // ポインタを作成
fmt.Println(ptr.Name) // ポインタ経由でも「.」でアクセスできる
}
&uでuのポインタを作っています。Go言語では、ポインタでもそのままptr.Nameのようにアクセスできます。
4. 関数にポインタを渡して値を変更する
構造体をポインタで関数に渡せば、中身を直接変更できます。
func updateAge(u *User) {
u.Age = 30 // ポインタを通じて元の変数を変更
}
func main() {
user := User{Name: "はなこ", Age: 20}
updateAge(&user)
fmt.Println(user.Age) // => 30
}
このように、*User型を引数にすることで、関数の中から直接データを更新できます。
5. new関数で構造体のポインタを作る方法
Go言語にはnew関数もあります。これを使うと、構造体のポインタを簡単に作成できます。
userPtr := new(User)
userPtr.Name = "じろう"
userPtr.Age = 18
newは、ゼロ値で初期化された構造体のポインタを返します。あとから値を代入すればOKです。
6. 構造体ポインタの応用:スライスと組み合わせる
構造体ポインタは、スライスと組み合わせて大量のデータを扱うときにも便利です。
users := []*User{
&User{Name: "たろう", Age: 25},
&User{Name: "はなこ", Age: 30},
&User{Name: "じろう", Age: 22},
}
このように、スライスの中にポインタを入れることで、メモリの無駄を省きながら柔軟にデータを操作できます。
7. 値型とポインタ型の違いをまとめて整理
- 値型:コピーされる。元のデータは変わらない。
- ポインタ型:参照される。元のデータが直接変わる。
ポインタをうまく使えば、プログラムの効率も見た目のコードもスッキリさせることができます。
まとめ
Go言語の構造体とポインタについて学んできました。とくに初心者がつまずきやすいのが、値型とポインタ型の違いです。ふだんの変数は値そのものを持ちますが、ポインタは値の置かれている場所の情報を持っています。この違いを理解すると、構造体を扱うときに無駄なコピーを防いだり、関数の内側から元の値を直接変更したりすることができるようになります。プログラムは目に見えない世界ですが、住所と家の関係に置き換えると、ポインタは「家の場所」を教えてくれる存在だということが分かります。家の場所さえ分かれば、中にある家具を見たり並べ替えたりできます。まさにそれがポインタの役割なのです。
とくに構造体は、名前や年齢などの複数の情報をまとめて扱うため、小さなデータでもコピーを繰り返すとメモリが無駄になります。ポインタを使えばその負担を抑えることができ、大量のデータを処理するときにも効率よく動かせます。たとえば構造体を関数に渡す場合、値型で渡すと関数の中で変更しても外側には反映されません。しかしポインタで渡せば、関数の内側で書き換えた内容がそのまま呼び出し元に反映されます。この仕組みを知っておくと、データ更新が必要な場面で自然に使えるようになります。
さらにGo言語は、ポインタ経由であっても構造体のフィールドにそのまま「ドット記法」でアクセスできます。多くの言語ではポインタ用の記号を使ったり特別な書き方をしたりしますが、Go言語では自動で参照外しを行ってくれるため、とても読みやすく、初心者でも扱いやすい設計になっています。この働きのおかげで、ポインタを使う場面でもコードが複雑になりません。書き方のルールがシンプルなため、構造体とポインタの関係に慣れるほど「便利だな」と思えるようになります。
また、ポインタとスライスを組み合わせると、大量の構造体を無駄なく扱えます。実体をコピーするのではなく、場所だけを並べるので、どれだけデータが増えても負担が少なくなります。スライスから取り出したポインタを通じて値を変更すると、その変更は同じデータを参照しているすべての場所に反映されます。データの一覧を管理したり、条件に応じて変更したりといった処理の幅が広がるため、実用的なプログラムでも役立ちます。
簡単なサンプルを振り返る
値型で渡した場合と、ポインタで渡した場合の違いをもう一度確認しましょう。
type User struct {
Name string
Age int
}
// 値型で受け取る(コピー)
func changeByValue(u User) {
u.Age = 99
}
// ポインタで受け取る(実体)
func changeByPointer(u *User) {
u.Age = 99
}
func main() {
user := User{Name: "たろう", Age: 20}
changeByValue(user)
fmt.Println(user.Age) // 20(コピーが変わっただけ)
changeByPointer(&user)
fmt.Println(user.Age) // 99(実体が変更される)
}
どちらも同じ構造に見えますが、値型は別の入れ物を作ってそこだけが変わります。対してポインタは元の入れ物に直接触れるため、関数を抜けたあとにも結果が残ります。この違いを知っておくだけで、Go言語の構造体を扱うときの考え方が一気に分かりやすくなります。
生徒
「今日の内容で、ポインタがなんのために使われるのか分かった気がします。メモリの住所を持っているから、同じ変数をみんなで共有できるという感じですね。」
先生
「その通りです。同じ構造体を何度もコピーする必要がなくなるので、無駄な処理が減ります。とくに大量のデータを扱うとき、ポインタはとても役に立ちます。」
生徒
「関数に渡したときの違いも印象に残りました。値型だとコピーだけど、ポインタだと実体なんですね。」
先生
「はい。値型は安全に扱えますが、更新が必要ならポインタのほうが向いています。状況に応じて使い分けると良いですね。」
生徒
「あと、Go言語が自動で参照外しをしてくれるのがとても書きやすかったです。他の言語でポインタを触ったときは難しい印象があったので、ちょっと安心しました。」
先生
「読みやすさと扱いやすさはGo言語の魅力のひとつです。構造体とポインタを覚えるだけで、さまざまな処理を効率よく書けるようになりますよ。」
生徒
「今日の内容を踏まえて、今度はスライスとポインタを使ってデータの一覧を扱う練習もしてみたいです。」
先生
「とても良いですね。まずは小さなサンプルを作りながら、少しずつ慣れていきましょう。理解が深まると、より複雑なプログラムでも落ち着いて書けるようになります。」
生徒
「ありがとうございます!次の学習も楽しみです!」
この記事を読んだ人からの質問
プログラミング初心者からのよくある疑問/質問を解決します
Go言語の構造体にポインタを使う必要がある理由は何ですか?値渡しと参照渡しの違いがよく分かりません。
Go言語では構造体をそのまま関数に渡すと、メモリ上で別のコピーが作られます。これが値渡しです。大きな構造体をたくさんコピーすると、メモリを無駄に使い、処理速度も遅くなります。一方、ポインタを使った参照渡しであれば、構造体の値をコピーせず、メモリ上の同じデータを扱います。そのため、変更した結果が元の構造体に反映されるうえ、コピー処理が無いので効率が良くなります。プログラムのパフォーマンスやメモリ効率を良くしたいとき、Go言語の構造体とポインタを組み合わせることはとても重要です。
【超入門】ゼロから始めるGo言語プログラミング:最速で「動くアプリ」を作るマンツーマン指導
「プログラミングの仕組み」が根本からわかる。Go言語でバックエンド開発の第一歩を。
本講座を受講することで、単なる文法の暗記ではなく、「プログラムがコンピュータの中でどう動いているか」という本質的な理解につながります。シンプルながら強力なGo言語(Golang)を通じて、現代のバックエンドエンジニアに求められる基礎体力を最短距離で身につけます。
具体的な開発内容と環境
【つくるもの】
ターミナル(黒い画面)上で動作する「対話型計算プログラム」や、データを整理して表示する「ミニ・ツール」をゼロから作成します。自分の書いたコードが形になる感動を体験してください。
【開発環境】
プロの現場でシェアNo.1のVisual Studio Code (VS Code)を使用します。インストールから日本語化、Go言語用の拡張機能設定まで、現場基準の環境を一緒に構築します。
この60分で得られる3つの理解
「なぜ動くのか」という設定の仕組みを理解し、今後の独学で詰まらない土台を作ります。
データの種類やメモリの概念など、他言語にも通じるプログラミングの本質を学びます。
ただ動くだけでなく、誰が見ても分かりやすい「綺麗なコード」を書くための考え方を伝授します。
※本講座は、将来的にバックエンドエンジニアやクラウドインフラに興味がある未経験者のためのエントリー講座です。マンツーマン形式により、あなたの理解度に合わせて進行します。
初めてのGo言語を一緒に学びましょう!