Personal tools
You are here: Home ブログ 井上 Goのポインタ型のまとめ(Cプログラマ向け)
« December 2010 »
Su Mo Tu We Th Fr Sa
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Recent entries
Apache2.4のリリース予定は来年(2011年)初め(あくまで予定) inoue 2010-12-23
Herokuの発音 inoue 2010-12-20
雑誌記事「ソフトウェア・テストPRESS Vol.9」の原稿公開 inoue 2010-12-18
IPA未踏のニュース inoue 2010-12-15
労基法とチキンゲーム inoue 2010-12-06
フロントエンドエンジニア inoue 2010-12-03
ASCII.technologies誌にMapReduceの記事を書きました inoue 2010-11-25
技術評論社パーフェクトシリーズ絶賛発売中 inoue 2010-11-24
雑誌連載「Emacsのトラノマキ」の原稿(part8)公開 inoue 2010-11-22
RESTの当惑 inoue 2010-11-22
「プログラマのためのUXチートシート」を作りました inoue 2010-11-19
「ビューティフルコード」を読みました inoue 2010-11-16
Categories
カテゴリなし
 
Document Actions

Goのポインタ型のまとめ(Cプログラマ向け)

対象読者
CとGoのポインタ型の違いが気になる人
注意
以下、敢えてすべての変数に型を明示しています。Go的には := を使って、自明な変数の型を省略するのが流儀です。この文書はCあるいはJavaプログラマのために敢えて型を明示しています。Goでプログラムを書くときは真似しないでください。言語の流儀には従うべきだからです。それから個別の型に意味がない時は byte を使っています。byteを使う意味はその程度だと思ってください。
Cと違う部分
  1. 配列が値型
  2. (値型、ポインタ型と並んで)参照型がある(map、slice、channelの3つ)
  3. 配列にアドレス参照演算を適用すると、暗黙にslice型になる(ことがある)

最初に配列とポインタの関係です。Cの場合、配列変数の値は配列の先頭アドレス値を持っていて、ポインタ型変数のように扱えます。「ように」の辺りが曲者ですが、とにかく、そのままポインタ型変数に代入できてしまいます。

Goではそういうことはできません。

// (対照のために)普通のポインタの使用例
var a *byte;
a = new(byte);
*a = 'a';

// (対照のために)普通の配列の使用例
var a [8]byte;
a[0] = 'a';

// コンパイルエラー(配列の先頭アドレスをポインタ型で受けられない)
var a *byte;
a = new([8]byte);

// 配列へのポインタ
var a [8]byte;
var pa *[8]byte = &a;
(*pa)[0] = 'x';

// (参考)ポインタの配列
var ap [8]*byte;
ap[0] = new(byte);
*ap[0] = 'x';
//上記はこうも書けます. *(ap[0]) = 'x'; 

上記に現れる配列はあくまで「[8]byte」の型だと考えてください。配列へのポインタ型変数は「[8]byte型オブジェクト」のアドレス値を持つと考えてください。どうしてもC言語脳から離れられない人は、[8]byte を頭の中で struct Foo { char a[8]; } のように置き換えるとよいでしょう。

配列は値型なので、配列を代入したり関数の引数に値渡しすると、要素のコピーが起きます。

配列の話は受け入れればそんなに気持ち悪くはありません。しかし、参照型は気持ち悪いです。誤解のないように書いておくと、ぼくは参照型そのものを否定しているわけではありません。むしろ、Goは(Javaのように)オブジェクトを代入可能な変数の型をすべて参照型にすればよいと思っています。ポインタ型と参照型の混在が気持ち悪いのです。

混在はC++でも同じではないかと言うかもしれませんが、C++は型名に & をつけて参照型を明示しています。Goの場合、mapとsliceとchannelだけが参照型なのが気持ち悪いです。

一応、具体例を載せます。最初に断っていますが、普通は := を使うので、こんなに気持ち悪くはなりません。

// map(参照型)の例
var m map[int] int;
m = make(map[int] int);
m[1] = 10;
for k,v := range m {
        fmt.Printf("%d => %d\n", k, v);
}

// map(参照型)へのポインタ
var pm *map[int] int;
pm = &m;
(*pm)[2] = 20;

mapは参照型なので、代入や関数の引数で値渡しをしても、要素のコピーは起きません。

配列にアドレス参照演算を適用するとsliceになります。ちなみにsliceも参照型です。見た目的には []byte のように要素数を明示しない配列のように見える型です。

Cで配列型変数の値を暗黙にポインタ型変数に代入できたのが形を変えて出現しているイメージです。だから配列も参照型にすればいいのに。

func sum(a []int) int {
        s := 0;
        for i := 0; i < len(a); i++ {
               s += a[i]
       }
        return s
}

func sum2(a *[3]int) int {
        s := 0;
        for i := 0; i < len(*a); i++ {
               s += (*a)[i]
       }
        return s
}

func myFunc() {
       var a [3]int = [3]int{3,2,1};  // 以下、どちらでも動作
       fmt.Printf("sum = %d\n", sum(&a));  // 暗黙にスライスに変換
       fmt.Printf("sum = %d\n", sum2(&a)); // 配列へのポインタ
       // 以下省略
The URL to Trackback this entry is:
http://dev.ariel-networks.com/Members/inoue/go-pointer/tbping
Add comment

You can add a comment by filling out the form below. Plain text formatting.

(Required)
(Required)
(Required)
This helps us prevent automated spamming.
Captcha Image


Copyright(C) 2001 - 2006 Ariel Networks, Inc. All rights reserved.