
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
Integer literals(整数リテラル)
整数リテラルは、整数定数を表す数字の列です。オプションのプレフィックスで非10進数の基数を設定します。0bまたは0Bは2進数、0、0o、または0Oは8進数、0xまたは0Xは16進数を表します[Go 1.13]。単一の0は10進数のゼロと見なされます。16進数リテラルでは、文字aからfおよびAからFは値10から15を表します。
可読性のために、アンダースコア文字_を基数プレフィックスの後、または連続する数字の間に配置できます。このようなアンダースコアはリテラルの値を変更しません。
int_lit = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit = "0" ( "x" | "X" ) [ "_" ] hex_digits .
decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits = binary_digit { [ "_" ] binary_digit } .
octal_digits = octal_digit { [ "_" ] octal_digit } .
hex_digits = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600 // 2文字目は大文字の'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727
_42 // 識別子であり、整数リテラルではない
42_ // 無効: _ は連続する数字を区切る必要がある
4__2 // 無効: _ は一度に1つのみ
0_xBadFace // 無効: _ は連続する数字を区切る必要がある
解説
整数リテラルとは何か?
整数リテラルは、プログラム内に直接書かれた整数の値です。「42」「100」「0xFF」などがこれに当たります。
たとえ話: 料理のレシピに「砂糖50g」と書かれているとき、「50」が数値リテラルです。変数名ではなく、値そのものを直接書いています。
package main
import "fmt"
func main() {
// これらはすべて整数リテラル
age := 25 // 10進数
maxUsers := 1000 // 10進数
permissions := 0755 // 8進数
color := 0xFF00FF // 16進数
fmt.Println(age, maxUsers, permissions, color)
}
1. 10進数リテラル(Decimal literals)
最も一般的な数値表記です。0以外は0で始められません。
基本的な10進数
package main
import "fmt"
func main() {
// 10進数リテラル
var zero int = 0 // 単一の0
var one int = 1
var ten int = 10
var hundred int = 100
var million int = 1000000
fmt.Println(zero, one, ten, hundred, million)
// 注意: 0で始まる数は8進数になる(後述)
// var x int = 0123 // これは8進数の123(10進数の83)
}
アンダースコアで見やすく[Go 1.13]
大きな数値を読みやすくするために、**アンダースコア_**で桁を区切れます。
package main
import "fmt"
func main() {
// アンダースコアなし
population := 1000000 // 100万? 1000万?
// アンダースコアで3桁区切り(読みやすい!)
population2 := 1_000_000 // 明らかに100万
// 日本式の4桁区切りも可能
yen := 1_0000_0000 // 1億円
// 様々な区切り方
creditCard := 1234_5678_9012_3456 // クレジットカード風
fmt.Println(population2, yen, creditCard)
// アンダースコアは値に影響しない
fmt.Println(1000000 == 1_000_000) // true
}
実用例:
package main
const (
KB = 1_024 // 1キロバイト
MB = 1_024 * KB // 1メガバイト
GB = 1_024 * MB // 1ギガバイト
Million = 1_000_000 // 100万
Billion = 1_000_000_000 // 10億
MaxInt64 = 9_223_372_036_854_775_807 // int64の最大値
)
2. 2進数リテラル(Binary literals)
0bまたは0Bで始まり、0と1だけを使います。
package main
import "fmt"
func main() {
// 2進数リテラル
var b1 int = 0b1010 // 10進数の10
var b2 int = 0B1111 // 10進数の15
fmt.Println(b1, b2)
// ビットパターンが見やすい
var flags int = 0b00001111 // 下位4ビットが1
// アンダースコアで8ビット(1バイト)ごとに区切る
var ipAddress uint32 = 0b11000000_10101000_00000001_00000001
// 192.168.1.1のビット表現
fmt.Printf("フラグ: %b\n", flags)
fmt.Printf("IPアドレス: %d.%d.%d.%d\n",
(ipAddress>>24)&0xFF,
(ipAddress>>16)&0xFF,
(ipAddress>>8)&0xFF,
ipAddress&0xFF)
}
使用場面:
package main
import "fmt"
// ビットフラグの定義
const (
FlagRead = 0b0001 // 読み取り
FlagWrite = 0b0010 // 書き込み
FlagExecute = 0b0100 // 実行
FlagDelete = 0b1000 // 削除
)
func main() {
// 権限の組み合わせ
permissions := FlagRead | FlagWrite // 0b0011
// 読み取り権限があるかチェック
if permissions&FlagRead != 0 {
fmt.Println("読み取り可能")
}
// 実行権限がないことを確認
if permissions&FlagExecute == 0 {
fmt.Println("実行不可")
}
}
3. 8進数リテラル(Octal literals)
従来の書き方: 0で始まる
package main
import "fmt"
func main() {
// 0で始まると8進数
var octal1 int = 0755 // 8進数の755 = 10進数の493
var octal2 int = 0644 // 8進数の644 = 10進数の420
fmt.Println(octal1, octal2)
// 注意: 0で始まるつもりがなくても...
var wrong int = 0123 // これは10進数の123ではなく、8進数の123!
fmt.Println(wrong) // 83 が出力される
}
新しい書き方: 0oまたは0Oで始まる[Go 1.13]
package main
import "fmt"
func main() {
// 明示的に8進数であることを示す(推奨)
var octal1 int = 0o755 // 8進数の755
var octal2 int = 0O644 // 大文字Oでも可
fmt.Println(octal1, octal2)
// アンダースコアも使える
var octal3 int = 0o7_5_5
fmt.Println(octal3)
}
主な使用場面: Unixファイルパーミッション
package main
import (
"fmt"
"os"
)
func main() {
// ファイルパーミッション(8進数で表現)
// 0o644 = rw-r--r--
// 所有者: 読み書き可(6 = 4+2 = r+w)
// グループ: 読み取りのみ(4 = r)
// その他: 読み取りのみ(4 = r)
file, err := os.OpenFile(
"test.txt",
os.O_CREATE|os.O_WRONLY,
0o644, // パーミッション
)
if err != nil {
fmt.Println("エラー:", err)
return
}
defer file.Close()
// よく使われるパーミッション
const (
PermReadWrite = 0o644 // rw-r--r--
PermReadWriteExec = 0o755 // rwxr-xr-x
PermReadOnly = 0o444 // r--r--r--
PermPrivate = 0o600 // rw-------
)
}
8進数の各桁の意味:
| 数値 | 2進数 | 権限 | 説明 |
|---|---|---|---|
| 0 | 000 | — | なし |
| 1 | 001 | –x | 実行のみ |
| 2 | 010 | -w- | 書き込みのみ |
| 3 | 011 | -wx | 書き込み+実行 |
| 4 | 100 | r– | 読み取りのみ |
| 5 | 101 | r-x | 読み取り+実行 |
| 6 | 110 | rw- | 読み取り+書き込み |
| 7 | 111 | rwx | すべて |
4. 16進数リテラル(Hexadecimal literals)
0xまたは0Xで始まり、0-9とa-f(またはA-F)を使います。
package main
import "fmt"
func main() {
// 16進数リテラル
var hex1 int = 0xFF // 10進数の255
var hex2 int = 0x10 // 10進数の16
var hex3 int = 0xBadFace // 大文字小文字混在OK
fmt.Println(hex1, hex2, hex3)
// 小文字でも可
var hex4 int = 0xff
var hex5 int = 0xbadface
fmt.Println(hex4, hex5)
// アンダースコアで見やすく
var hex6 int = 0xBad_Face
var hex7 int = 0x_67_7a_2f_cc_40_c6 // 2桁ごとに区切る
fmt.Println(hex6, hex7)
}
使用場面1: 色の表現
package main
import "fmt"
func main() {
// RGBカラー(16進数で表現)
const (
ColorRed = 0xFF0000 // R:255, G:0, B:0
ColorGreen = 0x00FF00 // R:0, G:255, B:0
ColorBlue = 0x0000FF // R:0, G:0, B:255
ColorWhite = 0xFFFFFF // R:255, G:255, B:255
ColorBlack = 0x000000 // R:0, G:0, B:0
ColorGray = 0x808080 // R:128, G:128, B:128
ColorPink = 0xFF69B4 // ピンク
ColorOrange = 0xFFA500 // オレンジ
)
// RGBに分解
color := ColorPink
r := (color >> 16) & 0xFF // 255
g := (color >> 8) & 0xFF // 105
b := color & 0xFF // 180
fmt.Printf("RGB: (%d, %d, %d)\n", r, g, b)
fmt.Printf("カラーコード: #%06X\n", color)
}
使用場面2: バイトデータ
package main
import "fmt"
func main() {
// バイト列を16進数で表現
data := []byte{
0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello"のASCIIコード
}
fmt.Printf("16進数: % X\n", data) // 48 65 6C 6C 6F
fmt.Printf("文字列: %s\n", data) // Hello
// マジックナンバー(ファイル形式の識別)
const (
MagicPNG = 0x89504E47 // PNGファイルの先頭
MagicJPEG = 0xFFD8FFE0 // JPEGファイルの先頭
MagicGIF = 0x47494638 // GIFファイルの先頭
)
}
使用場面3: メモリアドレス
package main
import "fmt"
func main() {
x := 42
ptr := &x
// ポインタ(メモリアドレス)は16進数で表示される
fmt.Printf("アドレス: %p\n", ptr) // 0xc0000140a0 など
fmt.Printf("アドレス: 0x%x\n", ptr) // 同じ
// 特定のメモリアドレスを表現(低レベルプログラミング)
const (
BaseAddress = 0x40000000
OffsetAddress = 0x00001000
)
targetAddr := BaseAddress + OffsetAddress
fmt.Printf("ターゲットアドレス: 0x%X\n", targetAddr)
}
5. アンダースコアのルール
正しい使い方
package main
import "fmt"
func main() {
// ✅ 正しい使い方
// 基数プレフィックスの後
var a int = 0x_FFFF
var b int = 0b_1010
var c int = 0o_755
// 数字と数字の間
var d int = 1_000_000
var e int = 0xFF_FF_FF
// 複数のアンダースコア(別々の場所)
var f int = 1_234_567_890
fmt.Println(a, b, c, d, e, f)
}
間違った使い方
package main
func main() {
// ❌ 間違った使い方(コンパイルエラー)
// 識別子として解釈される
// var x int = _42 // これは変数名 "_42"
// 末尾にアンダースコア
// var y int = 42_ // エラー!
// 連続するアンダースコア
// var z int = 4__2 // エラー!
// プレフィックスと数字の間(xの後)
// var w int = 0_xBadFace // エラー!
// 正しくは:
var correct1 int = 0x_BadFace // OK
var correct2 int = 0xBad_Face // OK
}
6. 非常に大きな整数
Goの整数リテラルは任意の大きさを持てます(ただし、変数に代入するときは型の範囲内に収まる必要があります)。
package main
import (
"fmt"
"math/big"
)
func main() {
// 巨大な整数リテラル
huge := 170141183460469231731687303715884105727
fmt.Println(huge)
// アンダースコアで見やすく
huge2 := 170_141183_460469_231731_687303_715884_105727
fmt.Println(huge2)
// 注意: int64の範囲を超える場合はbig.Intを使う
bigNum := new(big.Int)
bigNum.SetString("170141183460469231731687303715884105727", 10)
fmt.Println(bigNum)
// 計算もできる
bigNum2 := new(big.Int)
bigNum2.SetString("123456789012345678901234567890", 10)
result := new(big.Int)
result.Mul(bigNum, bigNum2)
fmt.Println(result)
}
7. 実践的な使用例
例1: 設定値を読みやすく
package main
const (
// タイムアウト(ミリ秒)
TimeoutShort = 1_000 // 1秒
TimeoutMedium = 30_000 // 30秒
TimeoutLong = 300_000 // 5分
// データサイズ
MaxFileSize = 100_000_000 // 100MB
MaxUploadSize = 10_000_000 // 10MB
// 制限値
MaxUsers = 10_000
MaxRequests = 1_000_000
)
例2: ビットマスクとフラグ
package main
import "fmt"
const (
// ファイルオープンモード(8進数)
O_RDONLY = 0o0 // 読み取り専用
O_WRONLY = 0o1 // 書き込み専用
O_RDWR = 0o2 // 読み書き
O_CREATE = 0o100 // 作成
O_TRUNC = 0o1000 // 切り詰め
// 色定義(16進数)
ColorTransparent = 0x00000000
ColorSemiTrans = 0x80FFFFFF
ColorOpaque = 0xFFFFFFFF
// ビットフラグ(2進数)
FlagA = 0b0001
FlagB = 0b0010
FlagC = 0b0100
FlagD = 0b1000
)
func main() {
// 複数フラグの組み合わせ
mode := O_RDWR | O_CREATE
fmt.Printf("モード: 0o%o\n", mode)
flags := FlagA | FlagC
fmt.Printf("フラグ: 0b%04b\n", flags)
}
例3: プロトコルの定義
package main
import "fmt"
// HTTPステータスコード
const (
StatusOK = 200
StatusCreated = 201
StatusBadRequest = 400
StatusUnauthorized = 401
StatusForbidden = 403
StatusNotFound = 404
StatusInternalServerError = 500
)
// IPアドレス(16進数で32ビット表現)
const (
IPLocalhost = 0x7F000001 // 127.0.0.1
IPBroadcast = 0xFFFFFFFF // 255.255.255.255
)
func main() {
fmt.Println("ステータス:", StatusOK)
ip := IPLocalhost
fmt.Printf("IPアドレス: %d.%d.%d.%d\n",
(ip>>24)&0xFF,
(ip>>16)&0xFF,
(ip>>8)&0xFF,
ip&0xFF)
}
まとめ: 整数リテラルで覚えておくべきこと
基数の書き方
| 基数 | プレフィックス | 使える数字 | 例 |
|---|---|---|---|
| 10進数 | なし | 0-9 | 42, 1_000_000 |
| 2進数 | 0b, 0B | 0-1 | 0b1010, 0b_1111_0000 |
| 8進数 | 0, 0o, 0O | 0-7 | 0o755, 0644 |
| 16進数 | 0x, 0X | 0-9, a-f, A-F | 0xFF, 0xBad_Face |
アンダースコアのルール
- ✅ 数字と数字の間:
1_000,0xFF_FF - ✅ プレフィックスの後:
0x_FF,0b_1010 - ❌ 先頭や末尾:
_42,42_ - ❌ 連続:
4__2 - ❌ プレフィックス内:
0_x42
実用的なアドバイス
package main
const (
// 10進数: 日常的な数値
UserLimit = 1_000_000
// 2進数: ビット操作
Permission = 0b0111
// 8進数: ファイルパーミッション
FileMode = 0o644
// 16進数: 色、バイトデータ
Color = 0xFF5733
)
func main() {
// 適切な基数を選ぶことで、
// コードの意図が明確になる
}
整数リテラルは、コードを読みやすくするための重要な要素です。用途に応じて適切な基数とアンダースコアを使いましょう!
おわりに
本日は、Go言語の言語仕様について解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)

コメント