LoginSignup
8
6

More than 3 years have passed since last update.

Golangの言語仕様を全部読んでまとめる

Posted at

https://golang.org/ref/spec の和訳摘要です. 完全性はないです.

Version of Jan 14, 2020

ソースコードの表現

ソースコードはUTF-8である.

文字

改行 = LF
unicode文字 = Unicodeの"Letter"
unicode数字 = Unicodeの"Number, decimal digit"

文字と数字

文字 = unicode文字と"_"

字句要素

コメント

プログラム // 行コメント
/* 複数行
コメント */

トークン

トークンは5種類

  • 識別子
  • キーワード
  • 演算子
  • 約物(句読点)
  • リテラル

空白(スペース, タブ, CR, LF)は無視される

セミコロン

セミコロンは文の終りを示す
一行に複数文を書きたいときのみ, 明示的にセミコロンを必要とする

識別子

識別子は文字始まり, 文字+unicode数字が続く

キーワード

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

演算子と約物

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=

整数リテラル

_を区切り文字として使える. 123_456

0bで2進, 0oで8進, 0xで16進を示す

小数リテラル

が1つ含まれる数字

  • 1e+01: 指数表記
  • 0x_1: 16進
  • 0x_1P-16: 16進+指数表記

虚数リテラル

整数, 小数の最後にiをつけると虚数

Runeリテラル

'Rune'はUnicodeのコードポイント.

  • 'ä'はUTF-8では0xc3 0xa4だが, RuneではU+00E4 -'\377' '\x07' '\xff' '\u12e4'

文字列リテラル

  • "バックスラッシュエスケープされる文字列\n"
  • `エスケープされない文字列`

定数

  • リテラルは定数
  • unsafe.Sizeof(式)は定数
  • cap(式) len(式) は定数のことがある
  • real(複素定数) imag(複素定数) は数値定数
  • true false 真偽値定数
  • iota 整数定数

型名を省略したときのデフォルトは bool, rune, int, float64, complex128, string

変数

変数は静的型付けされる

interface{}型は任意の値を代入できる

型は配列,構造体,ポインタ,関数,インターフェース,スライス,マップ,チャンネル

メソッド集合

型はメソッドを持つ

  • インターフェースは,そのインターフェスをメソッドとして持つ
  • Tは,Tをレシーバにするメソッドを持つ
  • *Tは,*TTをレシーバにするメソッドを持つ
  • 構造体が埋め込みフィールドを含む場合,そのメソッドも持つ

真偽値型

bool
truefalse

ゼロ値はfalse
==による比較可能

数値型

uint8       非負8-bit (0 to 255)
uint16      非負16-bit (0 to 65535)
uint32      非負32-bit (0 to 4294967295)
uint64      非負64-bit (0 to 18446744073709551615)

int8        8-bit  (-128 to 127)
int16       16-bit (-32768 to 32767)
int32       32-bit (-2147483648 to 2147483647)
int64       64-bit (-9223372036854775808 to 9223372036854775807)

float32     IEEE-754 32-bit 浮動小数点
float64     IEEE-754 64-bit 浮動小数点

complex64   float32 複素数
complex128  float64 複素数

byte        uint8のエイリアス
rune        int32のエイリアス

uint        32-bit or 64-bit
int         uintと同じ大きさ
uintptr     ポインタを格納できる大きさ

ゼロ値は0
==による比較 <などによる順序比較可能

文字列型

文字列の長さlen(文字列)はバイト列の長さ
文字列は変更できない

文字列[添字]でバイト列を参照する
&文字列[添字]は不可
文字列[添字] = 文字は不可

文字列[low : high]は部分文字列を返す

ゼロ値は""
nilにならない

==による比較が可能 <などによる順序比較可能
byte列による辞書順で比較される

配列型

配列[要素数]型Tは,型Tを要素数個格納できる型
要素数は静的に決定
[1][2][3]int[1]([2]([3]int))
make([要素数]型) ゼロ初期化された配列

len(配列), cap(配列)は要素数
[要素数]型{要素0, 要素1, …} 配列の合成リテラル
[...]型{要素0, 要素1, …} 要素数は...とすると要素数となる
指定された要素数に対して要素が足りなければ要素はゼロ値となる

配列[添字]により要素が得られる
配列[low : high]によりスライスが得られる
得られたスライスは元の配列と要素を共有している
スライスを書き換えると配列も書き換わる

ゼロ値はそれぞれの要素がゼロ値となる
==による比較が可能 個別の要素を比較する

スライス型

スライス[]型Tは,型Tを格納する列の型
要素数が動的に決定

len(スライス)は要素数
cap(スライス)で予備を含めたスライスの大きさ=キャパシティーを得られる
make(スライス型, 長さ, キャパシティー) キャパシティーを与えてゼロ初期化されたスライス
new([キャパシティー]スライス型)[0:長さ]は上と等価

[]型{要素0, 要素1, …} スライスの合成リテラル. キャパシティーは指定できない.
スライス[low : high]によりスライスが得られる
得られたスライスはもとのスライスと要素を共有している
スライスを書き換えるともとのスライスも書き換わる

ゼロ値はnil
スライス型のnilは要素数0のスライスのように振る舞う
==による比較ができない

構造体

struct {複数のフィールド}は構造体型
変数0, 変数1, … 型
は名前付きフィールド (x int, x, y int)
だけ書くと埋め込みフィールドとなり, フィールド名は型と同じ
_ 型はパディング

型{キー: 値…} 合成リテラル
キーを省略した場合,structの順番となる
値が足りないときはゼロ値となる

構造体Sに型Tを埋め込むと,型Tのフィールドとメソッドは構造体Sでも参照できる

type T struct {
  X int
}
type S struct {
  T // Tを埋め込む
}

func main() {
  s := S{T{1}}
  // s.X は S.T.X と同じ
  fmt.Printf("%#+v:  %v === %v", s, s.X, s.T.X)
  // main.S{T:main.T{X:1}}:  1 === 1
}

フィールドは文字列のタグを持つ
タグが未指定の場合,空文字列のタグを持つ

リフレクションで参照できる

struct {
    microsec  uint64 `protobuf:"1"`
    serverIP6 uint64 `protobuf:"2"`
}

ゼロ値はそれぞれのフィールドをゼロ値で初期化する
==による比較可能 個別のフィールドを比較する

ポインタ型

*TTのポインタ型

&値によりポインタが得られる
*ポインタにより値を参照する

ゼロ値はnil
nilを参照すると実行時パニック
==による比較可能

関数型

func(引数) 返り値は関数型

ゼロ値はnil
nilを呼び出すと実行時パニック
==による比較不可能

インターフェース型

interface{複数のシグネチャ}はインターフェース型
シグネチャは メソッド名(引数)返り値

インターフェースIのすべてのシグネチャが型Tのメソッドの時, 型TはインターフェースIを実装していると言う

interface{}は要求するメソッドが無いので, 全ての型が実装している

インターフェースは他のインターフェースを埋め込める
埋め込まれたインターフェースのシグネチャを取り込む

ReadWriterReadWriteCloseを持つ

type Reader interface {
    Read(p []byte) (n int, err error)
    Close() error
}
type Writer interface {
    Write(p []byte) (n int, err error)
    Close() error

type ReadWriter interface {
    Reader
    Writer
}

ゼロ値はnil
==による比較可能 元の型として比較する

マップ型

map[キー型]値型はマップ型
make(map[キー型]値型, キャパシティー)でキャパシティー付きのマップが作られる
map[キー型]値型{キー0: 値0, キー1: 値1, …}
len(マップ)は要素数

マップ[キー]は値とその存在を返す
+ キーが存在するとき,その値とtrue
+ 存在しないとき,ゼロ値とfalse
v, ok = m[0]

ゼロ値はnil
nilはキーが存在しないマップとして振る舞う
==による比較不可能

チャンネル型

スレッドセーフなキュー

chan 型Tは 型Tを送受信するチャンネル型
chan <- 型Tは 型Tを送信するチャンネル型
<- chan 型Tは 型Tを受信するチャンネル型

make(chan 型T, キャパシティー)はチャンネルを作る唯一の方法
キャパシティーはチャンネルのキューの大きさを示す

len(チャンネル)はキュー内の要素数
cap(チャンネル)はキューの大きさ

バッファーに要素があるとき, 受信は成功する
バッファーに要素がないとき, 受信は送信されるまでブロックする
バッファーが足りているとき, 送信は成功する
バッファーの要素がいっぱいのとき, 送信は受信されるまでブロックする

ゼロ値はnil
チャンネル型のnilは何も送受信されない

close(チャンネル)によりチャンネルをクローズできる

==による比較可能 同じmake()によって生み出されたか判定する

型と値の性質

型の同一性

  • 異なるパッケージの非公開名前は区別される
  • シグネチャの引数や返り値の名前は無視される
  • キャパシティーは無視される
  • エイリアスは同一の型となる

代入可能

値xが型Tに代入可能である とは 次のいずれかを満たすことである:

  • xの型VがTと同一である
  • xの型VとTが同一の基底型を持ち, VとTの少なくともどちらかがDefined型ではない
  • Tがインターフェース型であり, xがTを実装している
  • x が送受信チャンネルでTがチャンネル型であり, VとTが同一の要素型をもち, VとTの少なくともどちらかがDefined型ではない
  • xがnilであり,Tがポインタ,関数,スライス,マップ,チャンネル,インターフェースである
  • xが定数であり,Tの値として表現可能

Defined型は, type 型名 型で定義された型名の型のこと

type T1 Ttype T2 Tのとき,
Tの値はT1, T2に代入できるが, T1の値をT2に代入することはできない

表現可能

定数xが型Tの値として表現可能であるとは,
要するに,定数がその型として扱えるということ

ブロック

ブロックは文のリスト {文; 文; …}

暗黙的にブロックが生成される

  • すべてのGoのソースコード全体=宇宙ブロック
  • それぞれのパッケージのソースコード全体
  • ファイル全体
  • if, for, switch
  • switch, selectのブランチ

ブロックによってスコープが規定される

宣言とスコープ

宣言は, 定数, 型, 変数の3種類
トップレベル宣言は, 宣言に加えて関数宣言とメソッド宣言の5種類

boolなどの事前宣言識別子は宇宙ブロックのスコープに属する
トップレベルの宣言はパッケージのスコープに属する
インポートしたパッケージはファイルのスコープに属する

他の名前は最も内側のブロックのスコープに属する

ラベルのスコープ

定義されたラベルは用いる必要がある
ラベルは関数のブロックのスコープに属する. ラベル以外の識別子とは衝突しない

ブランク識別子

_はブランク識別子として特別な意味を持つ

事前宣言識別子

Types:
    bool byte complex64 complex128 error float32 float64
    int int8 int16 int32 int64 rune string
    uint uint8 uint16 uint32 uint64 uintptr

Constants:
    true false iota

Zero value:
    nil

Functions:
    append cap close complex copy delete imag len
    make new panic print println real recover

エクスポートされる識別子

識別子の初めの文字がUnicodeの大文字クラス("Lu")であり,
パッケージブロックで宣言された名前である時

その識別子はエクスポートされる

識別子の唯一性

同じスコープに同じ識別子は持てない

定数定義

const 定数名 = 定義 により定数を定義できる
const 定数名 型 = 定義 により型を指定できる

iotaconst()の中で宣言ごとに0から1ずつ増える特別な値である.

const (
    Sunday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Partyday
    numberOfDays  // this constant is not exported
)

Iota

1つの宣言の中ではiotaは同じ値となる

const (
    bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
    bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
    _, _                                  //                        (iota == 2, unused)
    bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
)

型宣言

エイリアス型宣言

type エイリアス名 = 型

エイリアスは元の型と同一の型である

型宣言

type 型名N 型T

基底型をTとして新しい型を型名Nとして作る.
型名NはDefined型となる.

すなわち,type TimeZone intはintと異なる振る舞いをする
また,Defined型はメソッドを持つことができる.

type TimeZone int

const (
    EST TimeZone = -(5 + iota)
    CST
    MST
    PST
)

func (tz TimeZone) String() string {
    return fmt.Sprintf("GMT%+dh", tz)
}

変数宣言

var 変数名 型 = 値で変数宣言
型と値は省略可能

参照されない変数はエラーとなる

_により値を捨てることができる

var _, found = entries[name]

短縮変数宣言

変数名 := 値var 変数名 = 値の省略としてほとんど振る舞う
ただしvarと異なり, 一部の変数への代入が可能
少なくとも1つは新しい変数を定義する必要がある

関数定義

func 識別子 シグネチャ ブロックにより関数が定義される

返り値を返すならばブロックの最後のreturnを省略できない
ただし, if-elseと条件のないforのあるときは省略できることがある(see 終端文)

関数のブロックを省略した場合,アセンブリルーチンなどでGo以外の外部から定義が与えられることを想定される

メソッド宣言

func (レシーバ変数 レシーバ型) 識別子 シグネチャによりメソッドが定義される

レシーバ型(*T, T)はTがDefined型である必要がある

メソッドの型はfunc (レシーバ型, メソッドの型...) メソッドの返り値

被演算子

被演算子はすべてのリテラルと識別子と修飾識別子と(式)である

修飾識別子

識別子にパッケージ名をつけることで別のパッケージの識別子を参照できる

パッケージ名.識別子

合成リテラル

チャンネル以外の型には{}によるリテラルがある

合成リテラルの例はそれぞれの型の節に移動した

複数の型が合成された型のとき, 内側の型名を省略できる

次の2つは同じ:
golang
map[string]Point{"orig": {0, 0}}
map[string]Point{"orig": Point{0, 0}}

関数リテラル

func シグネチャ 関数本体は関数であり,クロージャを作る
外側の変数を参照するための環境を持つ
()で呼び出すことができる

基本式

セレクタ

x.fはxのフィールドかメソッドを返す
xが埋め込みフィールドを持つ場合, 幅優先で再帰的に探索する

  • xの型(T, *T)でTがポインタでもインターフェースでもない
    • 探索時に最も浅いところのfを返す
    • 同じ深さに同じfがあるならば不正
  • xの型がインターフェースI
    • xの動的な実際の型のfを返す.
    • fがインターフェースIのメソッドでないならば不正
  • xがポインタ型 (例外)
    • xの実体である(*x).fが不正でないならx.fはP(*x).fの短縮となる
  • xがポインタ型でnil
    • 実行時パニック
  • xがインターフェースでnil
    • x.fを評価もしくは呼び出した時に実行時パニック

メソッド式

型.メソッド(*型).メソッドにより関数が得られる

メソッド値

値.メソッドにより関数が得られる

添字式

値a[添字i]

  • 値aがポインタのとき(*値a)[添字i]と等価
  • マップ,配列,スライス,文字列のとき それぞれの型のところで示した

スライス式

値a[low:higth]は部分文字列やスライスを返す
値aがポインタのとき,(*値a)[low:high]と等価

lowを省略すると0,highを省略するとlen(値a)となる

型アサーション

値x.(型T)により実行時に型変換ができる

  • 型Tがインターフェースのとき,xはTを実装している必要がある
  • 型Tがインターフェースでないとき,Tはxの型を実装している必要がある

そうでなければ実行時パニックを起こす

ただしv, ok := x.(T)の形で呼び出した場合は, 実行時パニックを起こさない
かわりにvがゼロ値, okがfalseとなる

呼び出し

関数は複数の値を返すことができる

返した複数の値は次のどれかを行う必要がある

  • 分割代入する: x, y := f()
  • returnする: return f()
  • 合成する: g(f())

多引数関数 ...引数

who ...string)

関数の最後の引数を...型にできる.
渡した引数がスライスとして渡される

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

スライス......引数に渡すことができる

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

演算子

数値演算

  • 数値 + - * / %
  • ビット | ^ << >> & &^
  • 単項演算子 + - ^

&^はbit clear(AND NOT)
存在理由→stack overflow

オーバーフローしてもパニックではなくwrap-aroundする

x < x+1が常にtrueとは限らない

小数の演算は積和演算(FMA)などの丸めで結果が異なることがある

文字列は + で結合できる
ただし, 多くの文字列を結合するならばstrings.Builderを用いるべき

比較演算

  • 比較 == !=
  • 順序 < <= > >=

比較する2値は互いに代入可能である必要がある

スライス,マップ,関数は比較できない

  • 真偽値,整数,小数 比較可能かつ順序付け可能
  • 複素数 real→imagの辞書順比較で順序付けされる
  • 文字列 bit列での辞書順比較で順序付けされる
  • ポインタ 比較可能. 大きさゼロの要素のポインタ値は異なることがある
  • チャンネル makeで作られた同じチャンネルであるか比較する
  • インターフェース 同じ型で同じ値か比較する
  • 構造体 すべてのフィールドの値が同じか比較する
  • 配列 すべての要素が同じか比較する

論理演算子

  • 短絡演算 || &&
  • 単項演算子 !

アドレス演算

  • 参照 &x
  • 参照先の値を得る *x

受信演算

  • 受信 <-ch

受信する.
バッファーが無いときは送信があるまでブロックする

x, ok = <-chのとき, 受信に成功したかクローズしたかを返す
受信に成功すればok=trueとなる

コンバージョン

型T(値x)
コンパイル時に検査される型変換

xが定数の時 値xが型Tとして表現可能でなければ不正
xが定数でない時 次のどれかを満たす

  • xがTに代入可能
  • 構造体のタグを無視して, xの型がTの基底型と同一
  • 構造体のタグを無視して, xの型とTのポインター型がDefined型ではなく,それらの基底型が同一
  • xが整数か小数で,Tが整数型か小数型
  • xが複素数で,Tが複素数型
  • xが整数か[]byteか[]runeで,Tが文字列
  • xが文字列で, Tが[]byteか[]rune

すなわち,次のキャストが可能

  • 構造体のタグを無視するキャスト
  • 数値のキャスト
  • 文字列とスライスのキャスト

定数式

定数の演算は定数式になる

評価順

関数の引数の評価は左から右に行われる

終端文

return以外でも終了文がある
- return goto panic
- if-else for switch select (条件あり)

空文

空文は何もしない

ラベル付き文

ラベル: 文

式文

式は文である

送信文

チャンネルに値を送信する
golang
ch <- 3

インクリメント・デクリメント文

x++
x--

代入

x = 1
x += 1
a, b = b, a

If文

if x := f(); x < y {
    return x
} else if x > z {
    return z
} else {
    return y
}

Switch文

式Switch文

switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
switch { // switch trueと等価
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}

型Switch文

switch 変数 := 変数.(type)
順番に型アサーションを試し,成功したブランチを実行する

switch i := x.(type) {
case nil:
    print("x is nil")
case int:
    print(i+1)
}

For文

1条件For

for a < b {
  a *= 2
}
for { // for trueと等価
  if a < b {
    break
  }
}

 3条件For

for i := 0; i < 10; i++ {
  f(i)
}

range For

for i, v := range a {
  print(i, v)
}
  • aが配列,スライスの時 iは添字, vは要素
  • aが文字列の時 iはUTF-8での添字,vはRune
  • aがマップの時 iはキー,vは要素 順序不定 ループ中の要素追加は不定
  • aがチャンネルの時 1要素 チャンネルがCloseするまで受信ループ

Go文

最後の関数適用がゴルーチンとして並列に実行される

go Server()
go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)

Select文

実行できる受信/送信を1つ当確率にランダムに実行する.
すべての受信/送信がブロックした場合,defaultがあればdefaultを実行する

select {
case <- c1:
case x := <- c2:
default: // ブロックしない
}
// 0,1をランダムに送信し続ける
for { 
  select {
  case c <- 0: 
  case c <- 1:
  }
}

Return文

名前付き返り値を用いることができる

func complexF3() (re float64, im float64) {
    re = 7.0
    im = 4.0
    return
}

Break文

一番内側のfor switch selectを抜ける
もしくは指定したラベルまで抜ける

Continue文

forに戻る
もしくは指定したラベルのforに戻る

Goto文

指定したラベルに処理を移す

Fallthrough文

switch内で次のブランチも実行する

Defer文

最後の関数呼び出しを,現在の関数から抜けるreturnpanicのタイミングで実行する
deferの実行は文の順番と逆に行われる

組み込み関数

Close

チャンネルを閉じる

次の時実行時パニック
- 閉じたチャンネルを閉じる
- 閉じたチャンネルに送信する
- nilを閉じる

長さとキャパシティ len/cap

それぞれの型の説明で説明済み

確保 new

new(T)は型のゼロ値を返す
ほとんどの場合T{}と等価

スライス,マップ,チャンネルの生成

make(型, 要素数 キャパシティ)を指定できる

スライスへの追加とコピー append/copy

appendでスライスの末尾に複数要素を追加できる
appendの第1引数は使い回され変更を受けることがある

b := append(a, 1, 2)
b := append(a, c...)

copyはsrcの要素をdstに代入する

copy(dst, src []T) int

マップからキーを削除 delete

キーが存在しない場合は何もしない
golang
delete(m, k)

複素数演算 complex/real/imag

complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

パニックとリカバリ panic/recover

panicは直ちにすべての関数を抜けようとする

func panic(interface{})
func recover() interface{}

deferでrecover()を呼ぶとパニックの送出が止まる
パニックしたときの引数が得られる

func protect(g func()) {
    defer func() {
        log.Println("done")  // Println executes normally even if there is a panic
        if x := recover(); x != nil {
            log.Printf("run time panic: %v", x)
        }
    }()
    log.Println("start")
    g()
}

ブートストラッピング print/println

デバッグ出力, 標準エラー出力に出ることが多い

print("Hello")
println("World")

パッケージ

Goのビルド単位

ソースファイル

Goのソースファイルは

package パッケージ名
複数のインポート宣言

から始まる

インポート宣言

パッケージをインポートするときは,
パッケージ名に別名をつけることや
現在のパッケージと名前空間を共有することができる

パッケージ名は実装依存

import "lib/math"  math.Sin
import m "lib/math"  m.Sin
import . "lib/math"  Sin

プログラムの初期化と実行

ゼロ値

確保された変数は初期値がない場合,ゼロ値になる.
false 0 "" nilのいずれか

パッケージの初期化

パッケージグローバルの変数は依存の解決されたものから上から順番に初期化される.

パッケージのinit()が呼ばれる.
init()は何回でも定義できる.
init()はプログラムの他の場所から参照できない.

インポートされたパッケージが先に初期化される.
複数回インポートされるパッケージは1回だけ初期化される.
パッケージ間の循環参照は起こせない.

プログラムの実行

mainパッケージのmain()が実行される,

エラー error

error型はインターフェースである.

type error interface {
    Error() string
}

nilはエラーのないことを示す.

実行時パニック panic

panic関数はerror型を受け取り,実行時パニックを起こす

システム関係

unsafeパッケージ

package unsafe

type ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type
type Pointer *ArbitraryType

func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr

Offsetofは要素のオフセットを返す

uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))

Alignof(x)はアライメントを返す

uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0

大きさとアライメントの保証

数値型は固定サイズである.

type                                 size in bytes

byte, uint8, int8                     1
uint16, int16                         2
uint32, int32, float32                4
uint64, int64, float64, complex64     8
complex128                           16

structのアライメントは要の最大のアライメントと等しい.
arrayのアライメントは要素のアライメントと等しい

ゼロ要素のstructやarrayは0より大きい大きさを持つことがある.
ゼロ要素の異なる変数が同じメモリに配置されることがある.

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6