Go言語入門と使い方

Go言語を勉強したくなったのでWindowsにインストールしてみます。

ダウンロードサイトはこちらです。

ここでは1.8.1をインストールします。

msiファイルを実行すると以下画面が表示されます。

「Next」をクリックします。

「Next」をクリックします。

インストールバスがC:\goで問題なければ「Next」をクリックします。

「Install」をクリックします。これでインストールが始まります。

「Finish」を押してインストール完了です。

パスを通す

C:\Go\binにパスを通します。と思ったんですが、すでに環境変数GOROOTが設定されています。

Pathにも、「C:\Go\bin」が登録されています。

ですので、以下コマンドでバージョン確認してみます。

C:\>go version
go version go1.8.1 windows/amd64

GOPATHを通す

Go言語にはGOROOTとGOPATHというのがあります。

ウィンドウズでは、%GOPATH%です。これは必ず設定しないといけません。

設定していない場合、デフォルトでは、「C:\Users\ユーザ名」がGOPATHになるようです。

以下のように設定します。

OSの再起動が必要なようです。

Go言語のファイル拡張子

Go言語は、ファイル名.goとなります。

以下サンプルです。

package main

import "fmt"

func main() {
  fmt.Printf("hello !\nhello !!\n")
}

このgoファイルをコンパイルして実行します。

D:\Gosrc>go run hello.go
hello !
hello !!

D:\Gosrc>

コンパイルが通ったらbuildしてバイナリファイル(exe)を作成します。

D:\Gosrc>go build hello.go

D:\Gosrc>

これで、hello.exeが作成されます。

hello.exeを実行します。

D:\Gosrc>hello.exe
hello !
hello !!

D:\Gosrc>

パッケージ文

packageステートメントを使用して、どのパッケージに属するかを明記します。

import文

import文を使用してパッケージをインポートし、使用します。

import "fmt"

入出力フォーマットを実装したパッケージfmtパッケージをインポートします。

fmtパッケージの関数を使用する場合は、fmt.関数名というように記述します。

fmt.Printfというのは、fmtパッケージにPrintfという関数が定義されているという事になります。

初めに実行される関数

Go言語では、mainパッケージのmain関数が初めに実行されます。

関数の記述方法は以下の通りです。

func 関数名() {
  // 実装
}

日本語に対応するプログラミングにする

上記のプログラムを以下のように書き換えます。

package main

import "fmt"

func main() {
  fmt.Printf("こんにちは\n")
}

コンパイルすると以下のようにエラーが発生します。

D:\Gosrc>go run hello.go
# command-line-arguments
.\hello.go:6: invalid UTF-8 encoding

D:\Gosrc>

これはGo言語はUTF-8で保存しないといけないためです。

hello.goをUTF-8で保存しなおし、コンパイルします。

D:\Gosrc>go run hello.go
こんにちは

D:\Gosrc>

コメント

Go言語内のコメントは複数行コメントは/*~*/、1行コメントは//となります。

複数import文

複数のimport文を記述する時は、()で囲むことができます。

import (
  "os"
  "fmt"
)

但し、囲まずに1行ずつ書くことも可能です。

const文

constは定数を宣言する時に記述します。

const Str = "test"

複数同時に宣言する時はimport文と同じように()で囲むことができます。

package main

import "fmt"

const (
  Str1 = "こん"
  Str2 = "にちは!"
)
func main() {
  fmt.Printf("%s%s\n", Str1, Str2)
}

但し、定数に配列やスライスを宣言することはできません。

const num = [3]int{3,3,5} // これはエラー

varと=と:=

var Str = ""

変数Strの宣言をしています。初期値は””です。

Str := ""

上記のように:=を使う際は初期値を指定する必要があります。

変数をカンマ区切りで宣言する

変数の宣言(初期化)はカンマ区切りで宣言することができます。

package main

import (
  "fmt"
)

func main() {
  Str1, Str2 := "test1", "test2"
  fmt.Printf("%s%s\n", Str1, Str2)
}

以下のように初期化せずに型だけ宣言することも可能です。

var i int

int型の場合、0で初期化されます。

fmt.Printfのパラメータ

%sは文字列です。%cは文字です。%dは10進数です。%fは小数です。%gは小数の無駄な0を省きます。

package main

import "fmt"

const (
  Str = "test"
)
func main() {
  fmt.Printf("%c%c\n", Str[0], Str[1])
}

実行すると以下のようになります。

D:\Gosrc>go run hello.go
te

D:\Gosrc>
package main

import "fmt"

const (
  i = 2
  j = 2.50
)
func main() {
  fmt.Printf("%d,%f,%g\n", i, j, j)
}

実行すると以下のようになります。

D:\Gosrc>go run hello.go
2,2.500000,2.5

D:\Gosrc>

使用されていないimport文

使用されていないimport文を記述したままコンパイルするとエラーになります。

D:\Gosrc>go run hello.go
# command-line-arguments
.\hello.go:4: imported and not used: "fmt"

D:\Gosrc>

mainパッケージのmain関数は戻り値を持たない

main.mainは戻り値がありません。その為処理が正常終了すればそのまま終わります。

異常時に戻り値を返したい時は以下の記述をします。

os.Exit(1)

以下、使用例です。

package main
import (
  "os"
)
const (
  Str = "test"
)
func main() {
  if Str[0] == 't' {
    os.Exit(1)
  }
}

if文

if文は以下の書式になります。

if 条件式 {
}

if 条件式 {

} else {

}

if 条件式 {

} else if 条件式{

}

条件式には、i == 0やi != 0などというようにtrueかfalseになるように記述します。

その他使える演算子は以下です。Javaと同じです。

i > 0
i < 0
i >= 0
i <= 0

if文を使用した例です。

package main

import (
  "fmt"
)

func main() {
  var i int
  if i == 0 {
    fmt.Printf("%d\n", i)
  } else {
    fmt.Printf("%d\n", i)
  }
}

結果は以下のようになります。int型は初期化していない場合は0で初期化されます。

D:\Gosrc>go run hello.go
0

D:\Gosrc>

go言語の配列宣言

配列を宣言する方法はいくつかあります。

以下は、int型の要素を6つ持つ配列iを宣言しています。

var i [6]int

Go言語では、int型は0で初期化されます。bool型はfalseで初期化されます。

以下、例です。

package main
import (
  "fmt"
)
func main() {
  var i [6]int
  fmt.Println(i)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
[0 0 0 0 0 0]

D:\Gosrc>

配列を初期化するには以下のように記述します。

var num = [3]int{3,3,5}

3つのint型要素を持つnum配列を宣言しています。

num := [3]int{3,3,5}

でも同じです。

len(配列)とすると配列長を返してくれます。

package main

import "fmt"

func main() {
  var num = [3]int{3,3,5}
  fmt.Printf("%d\n", len(num))
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
3

D:\Gosrc>

配列要素にアクセスする

配列要素にアクセスするにはJavaと同じく変数名[添え字]でアクセスします。

Go言語の添え字の基底値は0です。

以下、例です。

package main
import "fmt"
func main() {
  var num = [3]int{3,3,5}
  fmt.Printf("%d\n", num[2])
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
5

D:\Gosrc>

Go言語の配列のもう一つの宣言方法

配列宣言方法はもう一つあります。要素数を指定せずに以下のように.(ドット)を使用する方法です。

var num = [...]int{3,3,5}

以下、例です。

package main
import "fmt"
func main() {
  var num = [...]int{3,3,5}
  fmt.Printf("%d\r\n", len(num))
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
3

D:\Gosrc>

配列に似ているスライス

Go言語では配列に非常に似ているスライスという型があります。

スライスは配列と違い、要素数を指定しません。

スライスの書式は以下の通りです。

変数名 := []型{初期値}
str := []string{"a", "b", "c"}

スライスは、変数名[start:end]という記述をすることによって、スライスを操作することができます。

startは0以上、endは配列長までです。len関数までです。

package main

import "fmt"

func main() {
  str := []string{"a", "b", "c"}
  fmt.Printf("%d\n", len(str))
  fmt.Println(str[0:2])
  fmt.Println(str[0:3])
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
3
[a b]
[a b c]

D:\Gosrc>

makeでスライスを作成する

make関数を使用することによってスライスを作成することができます。

make関数は組み込み関数なので、インポートする必要がなく、スライス型のオブジェクトを確保して初期化までしてくれます。

make関数の引数は3つあります。

第一引数:型
第二引数:配列長
第三引数:配列収容数(省略可能)

第三引数を省略した場合、配列収容数=配列長となります。

package main

import "fmt"

func main() {
  str := make([]string, 3)
  fmt.Printf("%d\n", len(str))
  fmt.Printf("%d\n", cap(str))
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
3
3

D:\Gosrc>

リスライス

既に出てきていますが、スライス変数を以下のように記述することをリスライスと言います。

str := []string{"a", "b", "c"}
slc = str[0:2]

リスライスでは、str[:]、str[0:]、str[:2]というように省略することが可能です。

str[:]  → str[0:3]
str[1:] → str[1:2]
str[:2] → str[0:2]

リスライスで注意する点

リスライスした変数の要素の値を変更すると、元のスライスの要素の値も変更されます。

以下、例です。

package main
import "fmt"
func main() {
  str := []string{"a", "b", "c"}
  slc := str[0:2]
  slc[1] = "z"
  fmt.Printf("%s\r\n", str)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
[a z c]

D:\Gosrc>

配列からスライスを作成する

配列からスライスを作成することができます。以下のように記述します。

var num = [...]int{3,3,5} // 配列は...と記述することにより要素数を省略できる
var slc = num[:]

これで、slcはスライスとなります。

以下、例です。

package main

import "fmt"

func main() {
  var num = [...]int{3,3,5}
  var slc = num[:]
  fmt.Printf("%d\r\n", len(slc))
  fmt.Printf("%d\r\n", cap(slc))
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
3
3

D:\Gosrc>

スライスに要素を追加するappend

append(スライス, 4,5...)

とすると、要素をスライスに追加していきます。

package main

import "fmt"

func main() {
  var num = []int{3,3,5}
  num = append(num, 4, 2)
  fmt.Printf("%d\n", len(num))
  fmt.Printf("%d\n", num)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
5
[3 3 5 4 2]
D:\Gosrc>

上記プログラム内の

var num = []int{3,3,5}

var num = [3]int{3,3,5}

というようにスライスでなく、配列だった場合はappendが使えない為、エラーとなります。

D:\Gosrc>go run hello.go
# command-line-arguments
.\hello.go:7: first argument to append must be slice; have [3]int

D:\Gosrc>

appendは容量を拡張する

スライスには容量という概念が存在します。

makeでスライスを作成します。容量(第三引数)を省略すると長さと同じになります。

package main
import "fmt"
func main() {
  str := make([]string, 3)
  fmt.Printf("%d\n", len(str))
  fmt.Printf("%d\n", cap(str))
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
3
3

D:\Gosrc>

では、appendで要素を増やしてみます。

package main
import "fmt"
func main() {
  str := make([]int, 3)
  fmt.Printf("%d\n", len(str))
  fmt.Printf("%d\n", cap(str))
  str = append(str,1) // 要素を一つだけ追加
  fmt.Printf("%d\n", len(str))
  fmt.Printf("%d\n", cap(str))
  fmt.Printf("%d\n", str)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
3
3
4
6
[0 0 0 1]

D:\Gosrc>

長さが4ですが、容量が6になぜなるのかはよくわかりません。。

スライスにスライスをコピーするcopy

組み込み関数のcopyを使えばスライスにスライスをコピーすることができます。

以下のようなプログラムがあるとします。

package main
import "fmt"
func main() {
  i := make([]int, 3)
  j := make([]int, 3)
  i[0] = 10
  i[1] = 20
  i[2] = 30
  fmt.Printf("%d\n", i)
  fmt.Printf("%d\n", j)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
[10 20 30]
[0 0 0]

D:\Gosrc>

上記のプログラムにcopyを追加してみます。

package main
import "fmt"
func main() {
  i := make([]int, 3)
  j := make([]int, 3)
  i[0] = 10
  i[1] = 20
  i[2] = 30
  copy(j, i) // 第一引数:コピー先、第二引数:コピー元
  fmt.Printf("%d\n", i)
  fmt.Printf("%d\n", j)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
[10 20 30]
[10 20 30]

D:\Gosrc>

長さの異なるスライスにスライスをcopy

先ほどは同じ長さのスライスにcopyしました。次は異なる長さのスライスにcopyした時の挙動を見てみます。

コピー元<コピー先の場合です。

package main
import "fmt"
func main() {
  i := make([]int, 3)
  j := make([]int, 4)
  i[0] = 10
  i[1] = 20
  i[2] = 30
  copy(j, i)
  fmt.Printf("%d\n", i)
  fmt.Printf("%d\n", j)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
[10 20 30]
[10 20 30 0]

D:\Gosrc>

次に、コピー元>コピー先の場合です。

package main

import "fmt"

func main() {
  i := make([]int, 3)
  j := make([]int, 2)
  i[0] = 10
  i[1] = 20
  i[2] = 30
  copy(j, i)
  fmt.Printf("%d\n", i)
  fmt.Printf("%d\n", j)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
[10 20 30]
[10 20]

D:\Gosrc>

エラーは発生せずにコピーが出来ることが確認できます。

Go言語のfor文

Go言語ではfor文は以下のように記述します。

num := 1000000
for i := 0; i <= num; i++ {
  // nop
}

以下は100万ループする例です。

timeをインポートして経過時間を最後に表示します。

package main

import (
  "fmt"
  "time"
)

func main() {
  num := 1000000
  start := time.Now()
  for i := 0; i <= num; i++ {
    // nop
  }
  goal := time.Now()
  fmt.Printf("%v\n", goal.Sub(start))
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
1.0001ms

D:\Gosrc>

for文中のcontinueとbreak

Javaと同じようにGo言語でもcontinueとbreakが使えます。

配列やスライスをfor rangeでループする

rangeの戻り値は2つあり、添え字と要素値になります。

記述方法は以下のように記述します。

package main

import "fmt"
func main() {
  slice := []int{1, 2, 3, 4, 5}
  for i, j := range slice {
    fmt.Printf("%d:%d\n", i, j)
  }
}

iに添え字が入り、jに要素値が入ります。結果は以下のようになります。

D:\Gosrc>go run hello.go
0:1
1:2
2:3
3:4
4:5

D:\Gosrc>

文字列を数値に変換する

Go言語では文字列⇔数値変換にはstrconvパッケージを使用します。

Atoi(string)関数の引数に文字列を渡し、戻り値は数値型になります。

以下、例です。

package main

import(
  "fmt"
  "strconv"
)

func main() {
  var i int
  i, _ = strconv.Atoi("100")
  fmt.Printf("%d\n", i)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
100

D:\Gosrc>

文字列を浮動小数点型に変換する

文字列を浮動小数点型に変換するにはParseFloat(string)関数を使用します。

以下、例です。

package main

import(
  "fmt"
  "strconv"
)
func main() {
  var i float64
  i, _ = strconv.ParseFloat("1.1234567890", 32)
  fmt.Printf("%f\n", i)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
1.123457

D:\Gosrc>

文字列をbool型に変換する

文字列をbool型に変換するにはParseBool(string)関数を使用します。

以下、例です。

package main
import(
  "fmt"
  "strconv"
)

func main() {
  var i bool
  i, _ = strconv.ParseBool("false")
  fmt.Printf("%t\n", i)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
false

D:\Gosrc>

ファイル作成する

Go言語ではファイル入出力はosパッケージを使用します。

os.Createの引数はバッククォートで囲まなくてはいけません。write.txtが存在しなくても、この関数でファイルを作成します。

file, err := os.Create(`D:\Gosrc\write.txt`) // バッククォートで囲むこと
if err != nil {
  log.Fatal(err)
}

バッククォートで囲んだパスが存在しない場合、以下のようなエラーとなります。

D:\Gosrc>go run hello.go
2017/05/08 19:28:53 open D:\Gosrcr\write.txt: The system cannot find the path specified.
exit status 1

D:\Gosrc>

バッククォートで囲んだパスが存在しない場合が絶対にない場合は、エラーが発生しませんので、アンダースコアでエラーハンドリングを省略することもできます。

file, _ := os.Create(`D:\Gosrc\write.txt`) // バッククォートで囲むこと

ファイルのクローズにはdefer文を使用する

Go言語のdefer文は、その関数がreturnする直前に実行されます。

Javaでいうfinally句に記述するようなものでしょうか。

defer file.Close() // file.Closeは、完了時に実行される

file.Write関数で作成したファイルに書き込みます。

以下、ファイル作成例です。

package main

import (
  "os"
  "log"
)

func main() {
  file, err := os.Create(`D:\Gosrc\write.txt`) // バッククォートで囲むこと
  if err != nil {
    log.Fatal(err)
  }
  defer file.Close() // file.Closeは、完了時に実行される

  output := "あいうえお\r\nかきくけこ\r\n" // ウィンドウズの場合は\r\nが改行コード
  file.Write(([]byte)(output))
}

ファイルを読み込む

次はファイルを読み込んでみます。ファイルを読み込むのもosパッケージを使用します。

os.Open(`ファイルパス`)関数で読み込みます。

Open関数は、Fileと、エラーがあればエラーを返します。

file, err := os.Open(`D:\Gosrc\write.txt`)

File型のRead関数の引数用にスライスを作成します。以下は1024バイトのスライスです。

buf := make([]byte, 1024)

for文で読み込みが終わるまで無限ループします。

for {
  n, _ := file.Read(buf)
  if (n == 0) {
    break
  }
  fmt.Print(string(buf[:n]))
}

File型のRead関数の戻り値は、読み込んだバイト数と、エラーがあればエラーを返します。

ファイルの終わりはバイト数0とerrにEOFがセットされることで通知されます。

以下、ファイルを読み込む例です。

package main

import (
  "os"
  "log"
  "fmt"
)

const BUFSIZE = 1024 // 読み込みバッファのサイズ

func main() {
  file, err := os.Open(`D:\Gosrc\write.txt`) // バッククォートで囲むこと
  if err != nil {
    log.Fatal(err)
  }
  defer file.Close() // file.Closeは、完了時に実行される

  buf := make([]byte, BUFSIZE)
  for {
    n, _ := file.Read(buf)
    if n == 0 {
      break
    }
    fmt.Print(string(buf[:n]))
  }
}

os/userパッケージを使用する

os/userパッケージを使用して、HomeDir関数を使用してホームディレクトリを取得します。

以下はuser.Current()が返すUser型のHomeDir関数を使用した例です。

package main

import (
  "fmt"
  "os/user"
)

func main() {
  u, _ := user.Current()
  fmt.Println(u.HomeDir)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
C:\Users\Takahashi-PC

D:\Gosrc>

osパッケージをインポートしてディレクトリを操作する

osパッケージのMkdir関数を使用してカレントディレクトリにディレクトリを作成します。

ウィンドウズの場合でも第二引数にパーミッションを指定します。

package main

import (
  "os"
)

func main() {
  os.Mkdir("sample_dir", 0777)
}

サブディレクトリも含めたディレクトリ作成をする

サブディレクトリも一気に作成したい場合は、osパッケージのMkdirAll関数を使用します。

package main

import (
  "os"
)

func main() {
  os.MkdirAll("sample_dir\\test1\\test2", 0777)
}

ウィンドウズの場合は\\としてエスケープしないといけないので注意。

ちなみにUNCサポートしているので便利。

package main

import (
  "os"
)

func main() {
  os.MkdirAll("\\\\192.168.1.1\\d$\\sample", 0777)
}

ディレクトリを削除する

ディレクトリを削除したい場合は、osパッケージのRemove関数を使用します。

但し、ディレクトリ内が空の場合のみ削除することができます。

package main

import (
  "os"
)

func main() {
  os.Remove("D:\\gotest")
}

ディレクトリが空でない場合は、以下エラーが出ます。

remove gotest: The directory is not empty.

ディレクトリ内のファイルやサブディレクトリも削除したい場合は、osパッケージのRemoveAll関数を使用します。

package main

import (
  "os"
)

func main() {
  os.RemoveAll("D:\\gotest")
}

ディレクトリ名を変更する

ディレクトリ名を変更したい場合は、osパッケージのRename関数を使用します。

package main

import (
  "os"
)

func main() {
  os.Rename("D:\\Gosrc\\gotest", "D:\\Gosrc\\aaa")
}

gotestがaaaに変わります。

ファイル名を変更する

ファイル名を変更したい場合は、osパッケージのRename関数を使用します。

package main

import (
  "os"
)

func main() {
  os.Rename("D:\\Gosrc\\gotest\\a.txt", "D:\\Gosrc\\gotest\\zzz.txt")
}

これでa.txtがzzz.txtにリネームされます。

GoDocServerでローカルでドキュメントを見る

Goをインストールすると、「すべてのプログラム」-「Go Programming Language」フォルダが出来ています。

その中の「GoDocServer」をクリックします。

すると、以下の画面が起動されます。

「http://localhost:6060/」にアクセスすると、ローカルでブラウザからドキュメントが見れるようになります。

独自関数を作成する

独自で関数を作成してみます。以下の書式です。

func 関数名(引数名 string) (戻り値名 bool, 戻り値名 err) {
  // 処理
}

関数の第一引数に渡ってきたパスがディレクトリならtrue,ファイルならfalseを返す独自関数を作ってみます。以下です。

package main

import (
  "os"
  "fmt"
)

func main() {
  var isDir, _ = idDir("D:\\Gosrc\\write.txt")
  fmt.Print(isDir)
}

func idDir(dir_name string)(isDir bool, err error) {
  fInfo, err := os.Stat(dir_name) // FileInfo型を返す
  if err != nil {
    return false, err // エラーの場合、エラー情報を返す
  }
  // fInfo.IsDir()がtrueかfalseになる
  return fInfo.IsDir(), nil
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
false
D:\Gosrc>

カレントディレクトリを取得する

os.Getwd()でカレントディレクトリを取得することができます。

package main

import (
  "os"
  "fmt"
)

func main() {
  var curDir, _ = os.Getwd()
  fmt.Print(curDir)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
D:\Gosrc
D:\Gosrc>

コマンドライン引数を使用する

Go言語でコマンドライン引数を使用するにはosパッケージを使用します。

os.Argsはstring型のスライスで定義されており、コマンドパラメータが代入されます。

os.Args[0]は実行ファイル名が入ります。引数はスライスの添え字が1から連番で入ります。

package main

import (
  "os"
  "fmt"
)

func main() {
  fmt.Println(len(os.Args))

if len(os.Args) != 3 {
  fmt.Println("指定された引数の数が間違っています。")
  os.Exit(1)
}

  fmt.Printf("引数1: %s\n", os.Args[1])
  fmt.Printf("引数2: %s\n", os.Args[2])
}

結果は以下のようになります。

D:\Gosrc>go run hello.go 123 456
3
引数1: 123
引数2: 456
D:\Gosrc>

コマンドライン引数をflagパッケージをインポートして使用する

flagパッケージを使用するともうちょっとコマンドライン引数を便利に扱うことができます。

以下、例です。

package main

import (
  "fmt"
  "flag"
)

func main() {
  // 第一引数:オプション、第二引数:デフォルト値、第三引数:ヘルプメッセージ
  var f = flag.Int("f", 1156, "int型でデフォルトは1156です")
  var long = flag.String("long-opt", "aiueo", "string型でデフォルトはaiueoです")
  var s = flag.Bool("s", false, "bool型でデフォルトはfalseです")

  flag.Parse() // 必ずparseするおまじない

  fmt.Println(*f)
  fmt.Println(*long)
  fmt.Println(*s)
}

flag.Intとか色々ありますが、第一引数がコマンドラインオプションです。

以下のように実行します。

D:\Gosrc>hello.exe -f 3333
3333
aiueo
false
D:\Gosrc>

-f、–fまではOKですが、—fで引数を指定するとエラーとなりますので注意してください。

-helpオプション(-hでも良い)を指定すると、第三引数のメッセージが表示されます。

D:\Gosrc>hello.exe -h
Usage of hello.exe:
-f int
int型でデフォルトは1156です (default 1156)
-long-opt string
string型でデフォルトはaiueoです (default "aiueo")
-s bool型でデフォルトはfalseです

D:\Gosrc>

Go言語のポインタ

Go言語でもC言語と同じようにポインタが使えます。但しポインタの演算はできません。

ポインタ変数の宣言は、変数の型名(intとか)の初めに*(アスタリスク)をつけます。

var p *int

これがすごく覚えづらいですが、宣言時は型に*をつけます。

このポインタ変数にはアドレスしか代入できません。

変数のアドレスは、&(アドレス演算子)を使用します。

var i int = 1 // 変数宣言
p = &i // ポインタ変数にアドレスを代入する

ポインタ変数の先頭に*(参照演算子)を使用するとポインタの中身を取得できます。

*p

以下、例です。

package main

import "fmt"

func main() {
  var p *int
  var i int = 1

  p = &i
  fmt.Println("p =", p)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
p= 0xc042008240

D:\Gosrc>

関数に値渡しと参照渡し(アドレス)をすることができます。

呼び出し元には&iというようにアドレスを渡します。

関数の定義ではfunc sample(a *int)というように型に*(アスタリスク)をつけて定義します。

以下、例です。

package main

import "fmt"

func main() {
  var i, j int = 1, 1
  test(&i, j)
  fmt.Printf("%d:%d", i, j)
}

func test(a *int, b int) {
  *a = *a + 1
  b = b + 1
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
2:1
D:\Gosrc>

ポインタ変数を宣言し、アドレスを代入せずに使用してみます。

package main

import "fmt"

func main() {
  var p *int
  fmt.Printf("%d", *p)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x48a50b]

goroutine 1 [running]:
main.main()
D:/Gosrc/hello.go:8 +0x6b
exit status 2

D:\Gosrc>

ポインタ変数にはアドレスを代入してから、値を取得することができるようになります。

以下、例です。

package main

import "fmt"

func main() {
  var p *int
  var i int
  p = &i
  // pのアドレス,*pの値,&iのアドレス
  fmt.Printf("%d:%d:%d", p, *p, &i)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
825741050432:0:825741050432
D:\Gosrc>

ちゃんと0で初期化されていることが分かります。

newによるメモリ割り当て

newは組み込み関数で、Javaのnewとほぼ同じ感覚です。

int型なら、new(int)とします。new(int)が返す値は、int型の初期化された値を持つポインタ変数になります。

よって以下のようなポインタ変数の初期化ができます。

package main
import "fmt"
func main() {
  var p *int = new(int)
  fmt.Printf("%d", *p)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
0
D:\Gosrc>

先ほどのようにエラーとならず、0で初期化されているのが分かります。

Go言語で構造体を使用してオブジェクト指向にする

Go言語ではtypeというキーワードを使用して構造体を宣言することができます。

構造体の宣言例です。

type Person struct{ Name string }

構造体にアクセスする例です。

package main

import "fmt"

type Person struct{ Name string }

func main() {
  var p = Person{Name: "Taro"} // 値型の変数を用意する
  fmt.Printf("私の名前は%sです。", p.Name)
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
私の名前はTaroです。
D:\Gosrc>

構造体にメソッドを定義する

上記の構造体です。

type Person struct{ Name string }

このPersonにメソッドを追加します。

func (p Person) Msg(msg string) {
  fmt.Printf("%s,私の名前は%sです。", p.Name)
}

これで、変数名.Msg(“string型”)という使い方が出来るようになります。

以下、例です。

package main

import "fmt"

type Person struct{ Name string }

func (p Person) Msg(msg string) {
  fmt.Printf("%s私の名前は%sです。", msg, p.Name)
}

func main() {
  var p = Person{Name: "Taro"} // 値型の変数を用意する
  p.Msg("よう!") // Person.Msg(p, "よう!")と記述することも可能
}

結果は以下のようになります。

D:\Gosrc>go run hello.go
よう!私の名前はTaroです。
D:\Gosrc>

ちなみに上記プログラムのp.Msg(“よう!”)部分ですが、以下のように第一引数に構造体変数を指定することで、以下のように書き換えることができます。

Person.Msg(p, "よう!")

ファイル分割する

$GOPATHか$GOROOT配下にファイルを分割して配置することができます。

私の場合は$GOPATHは、「C:\Users\ユーザ名\」としています。

test.goというのを作成したい場合は以下のように作成します。

C:\Users\ユーザ名\go\src\test\test.go

分割したtest.goの中身は以下の通りです。

package test

import "fmt"

func Test() {
  fmt.Printf("testです\r\n")
}

ここで注意しないといけないのは、関数名の先頭1文字目は大文字にしないといけません。

testパッケージを使用するhello.goは以下の通りです。

package main

import "fmt"
import "test" // 分割したパッケージをインポート

type Person struct{ Name string }

func (p Person) Msg(msg string) {
  fmt.Printf("%s私の名前は%sです。", msg, p.Name)
}

func main() {
  var p = Person{Name: "Taro"} // 値型の変数を用意する
  Person.Msg(p, "よう!")
  test.Test() // ここで使用
}

LiteIDEをインストールする

Go言語に特化したIDEをこちらからインストールします。

liteidex31.windows-qt4.zipというファイルをダウンロードできますので、解凍します。

liteideというフォルダができますので、適当な場所に配置して、liteide\bin配下のliteide.exeをクリックします。これでliteideが起動します。

かなり軽量です。

goファイルの配置場所を指定する

「ファイル」-「フォルダを開く…」でgoファイルを配置しているフォルダを指定します。

ここでは、D:\Gosrcにします。すると以下のようにフォルダ構成が左側に表示されます。

ブレイクポイントを指定する

liteideではブレイクポイントを指定して、途中の変数などをウォッチする事が出来ます。

このあたりはEclipseに似ています。

ブレイクポイントを貼りたい箇所にカーソルを持っていき、F9を押します。すると左側にオレンジ色のマークがつきます。

ブレイクポイントを削除したい場合は再度F9を押します。

ビルドラン(BR)

ビルドしてexeを作成して、そのexeを実行します。

ラン(R)

exeを実行します。exeがない場合、「エラー: プロセスの起動に失敗しました」とエラー表示されます。

ランターム(>R)

よくわかりません。

ファイルラン(FR)

goファイルを実行します。exeはなくてもgoファイルさえあれば動作します。

ビルド(B)

goファイルをビルドしてexeを作成します。

コンソールには以下のように出力されます。

D:/Go/bin/go.exe build -i [D:/Gosrc]
成功: プロセスがコード 0 で終了しました

以下のように実行すると、Gosrc.exeというバイナリファイルが作成されます。

go build -i

ForceBuild(B!)

良くわかりませんけど強制的にビルド?

D:\Gosrc>go build -i -a -v
runtime/internal/sys
runtime/internal/atomic
runtime
internal/race
errors
sync/atomic
math
internal/syscall/windows/sysdll
unicode/utf16
unicode/utf8
sync
io
syscall
internal/syscall/windows
internal/syscall/windows/registry
strconv
time
reflect
os
fmt
_/D_/Gosrc

D:\Gosrc>

go generate(G)

go generateと実行すると、ソース中の「//go:generate」で始まるコメントを解析し、コマンドを実行してくれます。

以下、hello.goの例です。

package main

import "fmt"

type Person struct{ Name string }

func (p Person) Msg(msg string) {
fmt.Printf("%s私の名前は%sです。", msg, p.Name)
}
//go:generate test.exe
func main() {
var p = Person{Name: "Taro"} // 値型の変数を用意する
Person.Msg(p, "よう!")
}

test.goの例です。test.goのバイナリはtest.exeとします。

package main

import "fmt"

func main() {
fmt.Printf("testです\r\n")
}

このファイル構成で以下のようにタイプします。

D:\Gosrc>go generate -v

上記のように-vオプションを付加すると、処理されるファイル名を標準出力します。

D:\Gosrc>go generate -v
hello.go
testです
test.go

D:\Gosrc>

gofmt(F)

goファイルを整形してくれます。整形はJavaに似ているように思います。

コマンドベースでタイプする場合は以下のようにタイプします。

D:\Gosrc>go fmt hello.go
hello.go

D:\Gosrc>

これで、hello.goが綺麗に整形されます。

エラー

invalid UTF-8 encoding

goファイルの文字コードがUTF-8出ない場合に発生するエラー。

multiple-value idDir() in single-value context

関数の戻り値が異なる場合に発生するエラー。

imported and not used: “os”

インポート文が記述されているが使われていないパッケージがある場合発生するエラー。

const initializer [3]int literal is not a constant

配列やスライスを定数で宣言した場合発生するエラー。

p redeclared in this block

変数を重複して宣言した場合発生するエラー。

invalid indirect of p (type int)

ポインタ変数ではない普通の変数に*をつけた場合発生するエラー。

panic: runtime error: invalid memory address or nil pointer dereference

ポインタ変数を定義してアドレスを代入せずに使用した場合発生するエラー。

main redeclared in this block previous declaration at .\hello.go:11

main関数が複数宣言されている場合に発生するエラー。

cannot refer to unexported name test.test

関数名の先頭文字は大文字でない場合に発生するエラー。

open hello.go: The process cannot access the file because it is being used by another process.

他のアプリ(エディタなど)が開いたままでつかんでいると発生するエラー。

スポンサーリンク
  • このエントリーをはてなブックマークに追加
スポンサーリンク

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA