Edited at

【Go】基本文法③(ポインタ・構造体)


概要

【Go】基本文法①(基礎)

【Go】基本文法②(フロー制御文)

に引き続きGoの基本を学習します。

今回はポインタと構造体に焦点を当てて学習しました。


ポインタ


ポインタとは?

ポインタはメモリのアドレス情報のことです。

詳しい説明は以下のリンクがとても参考になりました。

【参考】

package main

import "fmt"

func main() {
var var1 int = 10
var var2 *int = &var1
fmt.Println(var1) //=> 10
fmt.Println(var2) //=> 0x10414020
fmt.Println(*var2) //=> 10
}


変数のアドレス取得方法

Goでは& を用いることで変数のアドレスの取得が可能です。

例えば &var1 で変数 var1 のアドレスを取得することが可能です。

func main() {

var var1 int = 10
var var2 *int = &var1
fmt.Println(var1) //=> 10
fmt.Println(var2) //=> 0x10414020
}

上記では変数 var1 のアドレスを ポインタ型変数var2 に入れてその値(0x10414020)を表示しています。

次にポインタ型変数とは何か理解します。


ポインタ型とは?

メモリー上のアドレスを記憶する変数の型のことです。

サンプルコード上の *int がポインタ型を示します。

var var2 *int = &var1


ポインタ型変数とは?

ポインタ型で宣言された変数のことです。

つまり、メモリ上のアドレスを値として入れられる変数のことです。

ポインタ型にしたい変数に * をつけて宣言します。

var var1 int      //int型

var var2 *int //int型へのポイント型

サンプルコード上の var2 がポインタ変数を示します。

初期値が指定されていない場合、Go 言語のポインタは nil (nil ポインタ) に初期化されます。

【参考】ポインタ型変数とは


ポインタ型変数の中身へのアクセス方法

ポインタ型変数名の前に * をつけることで変数の中身へのアクセスが可能です。

例えば *var2 とすることで var2 の値の参照が可能です。

func main() {

var var1 int = 10
var var2 *int = &var1
fmt.Println(*var2) //=> 10
}


Structs(構造体)


構造体とは?

Goにはオブジェクト指向言語におけるclassというものは存在しません。

似た役割として関連する変数をひとまとめにする struct(構造体) が使用されます。


構造体の定義方法

構造体は下記のように typestruct を使用して定義します。

package main 

import "fmt"

type Person struct {
first_name string
age int
}


構造体の初期化方法

構造体の初期化方法は複数存在します。


  • ①変数定義後にフィールドを設定する方法

  • {} で順番にフィールドの値を渡す方法

  • ③フィールド名を で指定する方法

上から順に初期化の方法を確認します。

①変数定義後にフィールドを設定する方法

type Person struct {

first_name string
age int
}

func main(){
var mike Person
mike.first_name = "Mike"
mike.age = 20
fmt.Println(mike.first_name, mike.age) //=> Mike 20
}

{} で順番にフィールドの値を渡す方法

type Person struct {

first_name string
age int
}

func main(){
bob := Person{"Bob", 30}
fmt.Println(bob.first_name, bob.age) //=>Bob 30
}

③フィールド名を で指定する方法

type Person struct {

first_name string
age int
}

func main(){
sam := Person{age: 15, first_name: "Sam"}
fmt.Println(sam.first_name, sam.age) //=>Sam 15
}

また以下のように初期化関数を作成することで初期化をすることも一般的に行われます。

type Person struct {

first_name string
age int
}

func newPerson(first_name string, age int) *Person{
person := new(Person)
person.first_name = first_name
person.age = age
return person
}

func main(){
var jen *Person = newPerson("Jennifer", 40)
fmt.Println(jen.first_name, jen.age) //=>Jennifer 40
} 


構造体とポインタ

struct のフィールドは、struct のポインタを通してアクセスすることもできます。

package main

import "fmt"

type Person struct {
firstName string
age int
}

func main() {
tim := Person{"Tim", 25}
person1 := &tim
(*person1).age = 25
person1.age = 53 //shortcutでp.Xと書くことも出来る
fmt.Println(person1) //=> {Tim 53}
}


構造体でのメソッドの定義

Goにはクラスと言う概念は存在しませんが、構造体内にメソッド method を定義できます。

普通の関数と違うのはレシーバ引数の部分だけです。

メソッドは以下の様に定義することができます。

func (<レシーバ引数>) <関数名>([引数]) [戻り値の型] {

[関数の本体]
}

実際に以下の様にすることで Person という構造体に対して intro というメソッドを定義することが可能です。

type Person struct {

first_name string
age int
}

func (p Person) intro(greetings string) string{
return greetings + " I am " + p.first_name
}

func main(){
bob := Person{"Bob", 30}
fmt.Println(bob.intro("Hello")) //=> Hello I am Bob
}

上記の例では intro メソッドは p という名前の Person 型のレシーバを持つことを意味しています。


構造体の埋め込み

上記で何度も記した通り、Goはオブジェクト指向言語の様なクラスを持ちません。

ですのでクラスの継承も存在しません。

そこで継承に似た機能として構造体の埋め込みがあります。

package main 

import "fmt"

type Person struct {
first_name string
}

func (a Person) name() string{ //Personのメソッド
return a.first_name
}

type User struct {
Person
}

func (a User) name() string { //Userのメソッド
return a.first_name
}

func main(){
bob := Person{"Bob"}
mike := User{}
mike.first_name = "Mike"

fmt.Println(bob.name()) //=> Bob
fmt.Println(mike.name()) //=> Mike
}


参考

My Jorney of Go③

C言語のポインタきらい

Goで学ぶポインタとアドレス

メソッドの定義方法

ポインタ型変数とは

Goのinterface/structの埋め込み

お気楽 Go 言語プログラミング入門