
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
Numeric types(数値型)
整数型、浮動小数点型、または複素数型は、それぞれ整数、浮動小数点、または複素数の値の集合を表します。これらは総称して数値型と呼ばれます。事前宣言されたアーキテクチャ非依存の数値型は次のとおりです。
uint8 すべての符号なし8ビット整数の集合(0〜255)
uint16 すべての符号なし16ビット整数の集合(0〜65535)
uint32 すべての符号なし32ビット整数の集合(0〜4294967295)
uint64 すべての符号なし64ビット整数の集合(0〜18446744073709551615)
int8 すべての符号付き8ビット整数の集合(-128〜127)
int16 すべての符号付き16ビット整数の集合(-32768〜32767)
int32 すべての符号付き32ビット整数の集合(-2147483648〜2147483647)
int64 すべての符号付き64ビット整数の集合(-9223372036854775808〜9223372036854775807)
float32 すべてのIEEE 754 32ビット浮動小数点数の集合
float64 すべてのIEEE 754 64ビット浮動小数点数の集合
complex64 float32の実数部と虚数部を持つすべての複素数の集合
complex128 float64の実数部と虚数部を持つすべての複素数の集合
byte uint8のエイリアス
rune int32のエイリアス
nビット整数の値はnビット幅で、2の補数演算を使用して表現されます。
また、実装固有のサイズを持つ事前宣言された整数型のセットもあります。
uint 32ビットまたは64ビット
int uintと同じサイズ
uintptr ポインタ値の未解釈ビットを格納するのに十分な大きさの符号なし整数
移植性の問題を回避するため、すべての数値型は定義された型であり、したがってbyte(uint8のエイリアス)とrune(int32のエイリアス)を除いて区別されます。式または代入で異なる数値型が混在する場合は、明示的な変換が必要です。たとえば、int32とintは、特定のアーキテクチャで同じサイズであっても、同じ型ではありません。
解説
数値型とは何か?
数値型は、数値(整数、小数、複素数)を扱うための型です。Goには用途に応じて様々なサイズと特性の数値型が用意されています。
たとえ話: 数値型は「数を入れる容器」で、容器の大きさ(ビット数)によって入れられる数の範囲が決まります。小さな容器(8ビット)には-128〜127しか入りませんが、大きな容器(64ビット)には非常に大きな数まで入ります。
1. 整数型(Integer types)
整数型は小数点を持たない数を表します。
符号付き整数(Signed integers)
正の数も負の数も表せる整数型です。
package main
import "fmt"
func main() {
var i8 int8 = -128 // 最小値
var i16 int16 = -32768
var i32 int32 = -2147483648
var i64 int64 = -9223372036854775808
fmt.Println(i8, i16, i32, i64)
// 正の数も扱える
var positive int32 = 12345
fmt.Println(positive)
}
各型の範囲
| 型 | ビット数 | 最小値 | 最大値 |
|---|---|---|---|
int8 | 8 | -128 | 127 |
int16 | 16 | -32,768 | 32,767 |
int32 | 32 | -2,147,483,648 | 2,147,483,647 |
int64 | 64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
符号なし整数(Unsigned integers)
0以上の数だけを表せる整数型です。負の数は扱えません。
package main
import "fmt"
func main() {
var u8 uint8 = 255
var u16 uint16 = 65535
var u32 uint32 = 4294967295
var u64 uint64 = 18446744073709551615
fmt.Println(u8, u16, u32, u64)
// 負の数は扱えない
// var invalid uint8 = -1 // エラー!
}
各型の範囲
| 型 | ビット数 | 最小値 | 最大値 |
|---|---|---|---|
uint8 | 8 | 0 | 255 |
uint16 | 16 | 0 | 65,535 |
uint32 | 32 | 0 | 4,294,967,295 |
uint64 | 64 | 0 | 18,446,744,073,709,551,615 |
アーキテクチャ依存の整数型
システムに応じてサイズが変わる型です。
package main
import (
"fmt"
"unsafe"
)
func main() {
var i int
var u uint
var ptr uintptr
// サイズを確認(32ビットシステムでは4バイト、64ビットでは8バイト)
fmt.Printf("intのサイズ: %d バイト\n", unsafe.Sizeof(i))
fmt.Printf("uintのサイズ: %d バイト\n", unsafe.Sizeof(u))
fmt.Printf("uintptrのサイズ: %d バイト\n", unsafe.Sizeof(ptr))
}
使い分け:
int: 最も一般的。通常はこれを使うuint: 負の数が絶対に必要ない場合uintptr: ポインタのアドレスを整数として扱う場合(低レベルプログラミング)
エイリアス型
package main
import "fmt"
func main() {
// byte は uint8 のエイリアス
var b byte = 255
var u8 uint8 = 255
fmt.Println(b == u8) // true
// rune は int32 のエイリアス(Unicode文字用)
var r rune = 'A' // 65
var i32 int32 = 65
fmt.Println(r == i32) // true
}
2. 浮動小数点型(Floating-point types)
小数点を持つ数を表します。IEEE 754規格に準拠しています。
float32 と float64
package main
import "fmt"
func main() {
var f32 float32 = 3.14159
var f64 float64 = 3.141592653589793
fmt.Println(f32) // 3.14159
fmt.Println(f64) // 3.141592653589793
// 精度の違い
fmt.Printf("float32: %.20f\n", f32)
fmt.Printf("float64: %.20f\n", f64)
}
特徴と使い分け
| 型 | ビット数 | 精度 | 用途 |
|---|---|---|---|
float32 | 32 | 約7桁 | メモリ節約が必要な場合 |
float64 | 64 | 約15-16桁 | 一般的な用途(推奨) |
特殊な値
package main
import (
"fmt"
"math"
)
func main() {
// 無限大
inf := math.Inf(1) // 正の無限大
negInf := math.Inf(-1) // 負の無限大
fmt.Println(inf) // +Inf
fmt.Println(negInf) // -Inf
// NaN(Not a Number)
nan := math.NaN()
fmt.Println(nan) // NaN
// チェック
fmt.Println(math.IsInf(inf, 1)) // true
fmt.Println(math.IsNaN(nan)) // true
}
3. 複素数型(Complex types)
実数部と虚数部を持つ数を表します。
complex64 と complex128
package main
import (
"fmt"
"math/cmplx"
)
func main() {
// complex64: float32の実数部と虚数部
var c64 complex64 = 1 + 2i
// complex128: float64の実数部と虚数部
var c128 complex128 = 3 + 4i
fmt.Println(c64) // (1+2i)
fmt.Println(c128) // (3+4i)
// 実数部と虚数部を取得
fmt.Println("実数部:", real(c128)) // 3
fmt.Println("虚数部:", imag(c128)) // 4
// 絶対値
abs := cmplx.Abs(c128)
fmt.Printf("絶対値: %.2f\n", abs) // 5.00
}
4. 2の補数表現
符号付き整数は2の補数で表現されます。これは負の数をビットで表現する標準的な方法です。
package main
import "fmt"
func main() {
var i int8 = -1
// -1 は 2の補数で 11111111 (8ビット)
fmt.Printf("10進数: %d\n", i) // -1
fmt.Printf("2進数: %08b\n", uint8(i)) // 11111111
var i2 int8 = -128
fmt.Printf("10進数: %d\n", i2) // -128
fmt.Printf("2進数: %08b\n", uint8(i2)) // 10000000
}
5. 型の変換
異なる数値型を混ぜて使うには、明示的な変換が必要です。
整数型同士の変換
package main
import "fmt"
func main() {
var i32 int32 = 100
var i64 int64
// 明示的な変換が必要
i64 = int64(i32)
fmt.Println(i64)
// これはエラー
// i64 = i32 // cannot use i32 (type int32) as type int64
// intとint32も別の型
var i int = 42
var i32_2 int32
i32_2 = int32(i) // 変換が必要
fmt.Println(i32_2)
}
整数と浮動小数点の変換
package main
import "fmt"
func main() {
// 整数 → 浮動小数点
var i int = 42
var f float64 = float64(i)
fmt.Println(f) // 42.0
// 浮動小数点 → 整数(小数部分は切り捨て)
var f2 float64 = 3.99
var i2 int = int(f2)
fmt.Println(i2) // 3(小数部分が切り捨てられる)
// 負の数
var f3 float64 = -3.99
var i3 int = int(f3)
fmt.Println(i3) // -3(ゼロ方向に切り捨て)
}
オーバーフロー
範囲を超えた値を変換すると、予期しない結果になります。
package main
import "fmt"
func main() {
// int32の最大値を超える
var i64 int64 = 2147483648 // int32の最大値+1
var i32 int32 = int32(i64) // オーバーフロー
fmt.Println(i32) // -2147483648(負の数になる!)
// uint8の範囲を超える
var i int = 256
var u8 uint8 = uint8(i) // オーバーフロー
fmt.Println(u8) // 0(256 % 256 = 0)
}
6. 実用例
例1: 適切な型の選択
package main
import "fmt"
func main() {
// 年齢: 0〜150程度 → uint8で十分
var age uint8 = 25
// 人口: 大きな数 → int64
var population int64 = 1_400_000_000
// 金額: 小数が必要 → float64
var price float64 = 1980.50
// カウンター: 通常はint
var count int = 0
fmt.Println(age, population, price, count)
}
例2: 計算での型変換
package main
import "fmt"
func main() {
// 整数の除算
var a int = 10
var b int = 3
// 整数同士の除算は整数になる
result1 := a / b
fmt.Println(result1) // 3(小数部分は切り捨て)
// 浮動小数点にしてから除算
result2 := float64(a) / float64(b)
fmt.Printf("%.2f\n", result2) // 3.33
}
例3: 範囲チェック
package main
import (
"fmt"
"math"
)
func SafeConvertToInt32(i int64) (int32, error) {
if i < math.MinInt32 || i > math.MaxInt32 {
return 0, fmt.Errorf("値が範囲外: %d", i)
}
return int32(i), nil
}
func main() {
var big int64 = 10000000000
if result, err := SafeConvertToInt32(big); err != nil {
fmt.Println("エラー:", err)
} else {
fmt.Println("変換成功:", result)
}
}
例4: 科学計算
package main
import (
"fmt"
"math"
)
func main() {
// 円の面積
radius := 5.0
area := math.Pi * radius * radius
fmt.Printf("半径%.1fの円の面積: %.2f\n", radius, area)
// 平方根
x := 16.0
sqrt := math.Sqrt(x)
fmt.Printf("%.0fの平方根: %.0f\n", x, sqrt)
// 指数関数
e := math.Exp(1)
fmt.Printf("e: %.10f\n", e)
}
まとめ: 数値型で覚えておくべきこと
整数型の選び方
// 通常はintを使う
var count int = 100
// 負の数が不要ならuint
var age uint = 25
// 小さい範囲ならuint8/int8
var percentage uint8 = 85
// 大きな数が必要ならint64
var bigNumber int64 = 999_999_999_999
浮動小数点型の選び方
// 通常はfloat64(推奨)
var price float64 = 1980.50
// メモリ節約が重要ならfloat32
var coordinate float32 = 35.6895
型変換の原則
// 1. 明示的な変換が必要
var i32 int32 = 100
var i64 int64 = int64(i32)
// 2. 精度の損失に注意
var f float64 = 3.99
var i int = int(f) // 3(小数部分が失われる)
// 3. オーバーフローに注意
var big int64 = 1000000000000
var small int32 = int32(big) // オーバーフロー!
実用的なアドバイス
package main
import "fmt"
func main() {
// 1. デフォルトはint, float64を使う
count := 100 // int
price := 1980.50 // float64
// 2. 型を明示する場合
var age uint8 = 25
var pi float64 = 3.14159
// 3. 定数は型なしで
const MaxRetries = 3
// 4. 計算では型を揃える
a := 10
b := 3
result := float64(a) / float64(b)
fmt.Println(count, price, age, pi, result)
}
数値型は、プログラミングの基礎です。適切な型を選ぶことで、メモリ効率が良く、バグの少ないコードが書けます!
おわりに
本日は、Go言語の言語仕様について解説しました。

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

コメント