

C言語はポインタの理解が難しいと、方々で言われています。  
その原因の一つにポインタの表記方法があると考えています。

例えばポインタ宣言時と、アドレス代入時で同じ*の意味が異なります。

`int *pointer;`の`*`は、`pointer`がポインタであることを示すもので、  
`a = *pointer;`の`*`は、`pointer`アドレスに格納された値を取り出すこと示すものです。

ここで私が抱く疑問は、  
`int *pointer`の表現方法として、`*`を使わない方法をなぜ取らなかったか、できない理由があったのかということです。  
例えば`const`や`static`のように、`int`の前に`point`とか書く方法もあったのではないでしょうか。

また、  
ポインタ`pointer`は、`*pointer`で中身を指し示し、`pointer`でアドレスを表現しますが、  
これ、どう考えても逆の方が良かったんじゃないの？って思います。  
逆にすると、何かしら弊害があるのでしょうか。

まだまだC言語について知識が足りないため、至らない点あるかと思いますが、  
お付き合い頂ければ幸いです。


CC BY-SA 4.0
2018-09-15T09:27:22.287
48424
2018-09-16T02:39:00.537
2018-09-15T09:51:50.043
4236
23008
question
c
C言語のポインタの表現の歴史的背景


まず、以下は私の私見で、間違いがあればごめんなさい。

この理解、あっていますか？ポインタはあくまでもどこまでも、アドレスを「ポイント」するだけで、知りえたアドレス情報をどう取り扱うかはプログラマ側にゆだねられている理解です。

つまり、「メモリ上のこの場所から情報が格納されているよ」という情報だけを渡してこられるので、そこから先のメモリに入っている値を何バイト読みこんでデータとするかはプログラム側にゆだねられる。つまりご指定のa
= *
pointerも結局アドレスを渡しているだけ。値では渡されていない。値のように見えるのは、たとえばこのアドレスを扱う側の関数や組み込み関数側で「ポインタで示されたアドレスから（始まるメモリ空間）特定のルールで情報を切り出す」処理と「ポインタで示されたアドレス（から始まるメモリ空間）に特定のルールで情報を格納する」処理が実装されているからではないですか？

そういう理解の上では

[code]

    int *pointer;の*は、pointerがポインタであることを示す。
    a = *pointer;の*は、pointerがポインタaに渡すべき「アドレス」を示すポインタであることを示す。
    
[/code]

で一貫性が取れているのではないかと考えています。


CC BY-SA 4.0
2018-09-15T23:17:08.923
48440
2018-09-15T23:17:08.923
10174
48424
answer


歴史的なことは初期のCコンパイラーがヒントになるかもしれません。下記はその紹介記事です。

[本の虫:
デニス・リッチーによって書かれた最初のCコンパイラーがGitHubで公開](https://cpplover.blogspot.com/2013/05/cgithub.html)

この頃には既にポインターが存在していました。しかし、`int
**ipp;`というポインターのポインターのような宣言は出来ず、また、通常のポインタの宣言も最初は`int ip[]`と書いていたようです(`int
*ip;`という表現も受け付けたようですが)。

以下、私見です。

Cの変数は必ず何らかの記憶域(storage)を持つ実体(object)です(記憶域はメモリ上とは限りませんし、しばしば最適化によって変数自体がなかったことにされることもありますが)。つまり、変数を束縛している実体は変数の記憶域に収まっているということです。JavaやC#の参照型のようなオブジェクト自身であるところの実体と変数の記憶域が異なるような変数はありません。変数と実体が同じであるという構造は、変数への操作が単純なメモリ上のデータ操作に変換できるため、コンパイラも非常に単純な作りにすることができます。

文字列などのシーケンシャルなデータを扱うには配列のような仕組みが必要になります。配列全体を実体とすることは可能ですが、別の変数に同じ配列を見せようとしても、そもそも複数の変数が同一の実体を共有することは出来ませんし、配列全体をコピーすることも非効率です。また、変数とオブジェクトの寿命が必ず一致してしまうということは、コンパイル時に静的に決まる変数では、データを動的に生成・消滅させることが難しくなってしまいます。そこで、ポインターという実体です。配列へのアクセスをポインターのアクセスと同様に扱える(配列とポインターが全く同じと言うことではありません、この二つは厳密に区別されます)ようにすることで、ポインターを介した配列操作を可能にしました。また、`malloc`等で動的に確保した記憶域については、ポインターを介してアクセスすることで、変数の寿命と動的に確保された記憶域にあるオブジェクトの寿命を切り離すことが出来ました。

重要なのはポインターも一つの実体に過ぎないと言うことです。こうして、Cはその言語構造を複雑化することなく、ポインターを用いて強力な表現を可能にしました。当時の技術は今ほどコンパイラや構文解析の研究が進んでいないため、単純であると言うことは極めて重要でした。JavaやC#の参照型ような型を別途用意するような複雑さは取り入れることはしなかったのです(当時、そのような言語が全くなかったのかまではわかりません)。

では、ポインターをどのように表現するのかです。初期のCコンパイラーに見られたように、`*`でポインターが示す先の実体を表現するようにしていましたが、宣言が`int
ip[];`という形でした。ポインターと配列は同様に扱えると言うことから、配列の宣言のように書けることはそれはそれで意味があったと思います。さて、`int
ip[];`ですが、これは、`int
(ip[]);`ともいえます。つまり、`ip`を配列とみなしてその要素にアクセスしたときの型は`int`であるということです。そこから`int
(*ip);`つまり`int
*ip;`もまた同様に考えられます。`ip`をポインターとみなしてその指し示す先の実体の型が`int`であるということです。これは、宣言ではないところでの`*`と同じような解釈(ポインターの指し示す先の実体という解釈)をしているということです。このようにすることで、構文解析を単純化しようとしたのではないかと推測されます。それが当時うまくいったのかまではわかりませんが。

なお、その後の言語を見るとこの方法は失敗だったと考えられます。キャストやsizeofの引数として`(int
*)`という表現が使えるため、宣言においても、あたかも`int
*`でひとまとまりの型だと見られてしまうという誤解も生みました。配列は後置になるため、更に混乱の元でしょう。Java等では`int[]`のような型表現を前置におくようにしたのもそれらが理由と思われます(C/C++ユーザーのため、Javaでは後置の書き方もサポートはしていますが)。ただ、最近は型を前置すること自体がよろしくないと考えられており、後置の言語(Go、Rust等)が増えているようにも思います。


CC BY-SA 4.0
2018-09-16T02:39:00.537
48442
2018-09-16T02:39:00.537
7347
48424
answer