• 締切済み

C言語のポインタについて教えてください。

C言語のポインタについて教えてください。 ・pointer1.c  int main(){   int a;   int *p;   p = &a;     a = 123;   printf("%d", *p);   return 0;  } ・pointer2.c   int main(){ int a[100]; int *p; p = &a[0]; int i; for(i = 0; i < 100; i++) a[i] = i; for(i = 0; i < 100; i++) printf("%d", *p++); return 0; } と二つのソースコードがあるとき、pointer2.cの「p = &a[0]」をpointer1.cのように「p = &a」と書けないのはなぜですか?  また、「&a」は動かすことのできなく、「aを指し示す*p」は動かすことができる変数のようなもの、という認識に誤りはないでしょうか?  宜しくお願いします。

みんなの回答

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.5

>pointer2.cの「p = &a[0]」をpointer1.cのように >「p = &a」と書けないのはなぜですか? 要するに、型が違うからです。 pの型はint*型でありint型のポインタを格納する為のものです。 提示されているコードpointer1.cの場合、aはint型であり、 &aはint*型です。あっていますね。 pointer2.cの場合、aはint[100]型であり、 &aはint(*)[100]型となります。 1要素を指し示す事で、&a[0]はint*型となります。 また、ここでは書かれていませんが、p = a;とした場合でも、 C言語では”暗黙の変換”が行われる為、aはint[100]型ではなく、 int*型で扱われるというケースがあります。 ほどんどの場合は、この”暗黙の変換”が行われるので、 int[100]型を意識する方が少ないかもしれません。

回答No.4

既に、No.3 の方が書かれていますが、 int a[100]; に対して、単独で a と書かれた場合、「多くの場合は」 (int *) と「みなされます」 それに対して、&a は、int[100] という型をポイントするポインタとなります。 これは、 http://www.kouno.jp/home/c_faq/c6.html#3 にもあるとおり、配列名が「配列の名前」として認識される例のひとつでもあります。 int[100] という型は、余り意識することはないのですが、たとえば、いわゆる多次元配列を扱う場合に、認識したほうが理解しやすいこともあります。 Cには、厳密には多次元配列というのは存在せず、たとえば、int[100] という型の配列(2次元配列)だったり、int[10][10] という型の配列(3次元配列)だったりします。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

間違って覚えてしまうといけないので念の為: int a[100]; としたとき, 「a の型」はあくまで「int [100]」です. 「int *」とか「int * const」とかではありません. そして, このとき &a は「int (*)[100]」という型になります. これは当然 (int *p; としたときの) p の型「int *」とは違うので, p = &a; という代入は不当なものとなります. なお, int a[100]; としたときの a の型は「int [100]」ですが, 式の中で a (のような配列型) を使うと「特定の場合を除いて」その先頭要素へのポインタに変換されます. 「変換される結果として配列名がポインタであるかのように扱える」だけであり, あくまで「本来配列とポインタとは別物」であることはちゃんと理解しなければなりません.

noname#140045
noname#140045
回答No.2

C言語には 「エリア(実体)」 「アドレス(番地)」 「ポインタ(指標)」 が、あります。 これを理解しないと、いけません。(日本語訳(?)が適切かは、あまり気にしないで下さい) まず、 int a; などは、整数値を入れる実体だと思って下さい。 実体はメモリ上に存在するため、すべて番地が付いています。 そして、実体ではなく番地を保存するのが指標となります。 この指標は、longならば4byte,shortならば2byteと実体のサイズ(広さ)も持っています。 (例:long *l; short *s; みたいな) そして int b[100];// チョット紛らわしいのでb[100]としました。 の場合はb[0]~b[99]までの1つ1つが実体となります。 そして、指標に実体の番地を入れる場合には、&を付けるのがルールです。 p=&a; p=&b[0]; となります。 そして、ここからはルールだと思って覚えてもらうしかないのですが、 int b[100]; の場合、ただ単にbと記述した場合には、実体とはなりません。 では、何になるかと言えば、番地となるのです。 つまり、[0]~[99]が配列の実体を表すものであるため、bだけを記述した場合には、(先頭)番地を示すというルールになっているのです。 --------------------------(ここからは、うろ覚えですが) なお、 p=&b; が、エラーになるかどうかは、実はコンパイラによって違います。 MS系のコンパイラは、エラーにならないはずです。 しかし、C言語のコーディング規約からすれば、エラーとなるのが正解です。 つまり、bが番地を示す以上、さらに&bは番地の番地を示す意味の通らないものになってしまうからです。 このあたりは、厳密さを取るか、曖昧さを取るかで、微妙に違ってきます。 ※変に日本語訳にしなかった方がわかりやすかったかな?

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

いくつか誤解があるようです。 ・int *p;は (int *)型の変数 p だと考えてみましょう。 変数なので、値を代入したり、(許された範囲での)計算をしたりできます。 ・C言語では、配列は「ポインタ定数」(変更できないポインタ)と考えましょう。 int a[100] は、 「int型変数を100個並べた(のと同じ状態の)領域へのポインタ」a、ただし、a自体を変更することはできない、ということになります。 ・&演算子は、それを付けた変数等へのポインタを求めるものです。 pointer1.cでは、int a; なので、aは「int型の変数」です。 &aは「int型の変数aへのポインタ」なので、 [int型へのポインタ]である (int *)型の変数pに代入することができます。 pointer2.cでは、int a[100]; なので、aは「int型へのポインタ(定数)」です。 &aは「int型へのポインタ(定数) aへのポインタ」なので、 [int型へのポインタ]である (int *)型の変数pとは、型が一致しません。 a[0]は、配列の1要素であり「int型の変数」です。&a[0]は「int型変数a[0]へのポインタ」となるので、pに代入することができます。 /* 余談です(ややこしくなります) 実際のところ、int a[100]とした場合、「配列変数a」自体を記憶する領域というのはないので、&a は &a[0]と同じアドレスを返します。 ポインタの「型」は、*で実体を扱うときや、加減算したときに移動する量等を判定するためのもので、アドレスそのものはどんなポインタでも共通です。 よって、今回の場合は、 p=&a;としても(警告は出るものの)期待通りに動いてしまいます。 これが int *a;だと、「ポインタ変数a」自体を記憶しておく領域があるので、 &a と &a[0] とでは違うアドレスが返るのが普通です。 */ ・「&a」は動かすことのできなく、「aを指し示す*p」は動かすことができる変数のようなもの というのが意味不明ですが。 *p++ のことを指しているのなら、これは次の複数の動作を1つにまとめたものです。 ・*p で ポインタpが指す実体へアクセスする。 今回の場合、値を参照しているだけなので、 ポインタpが指すアドレスにあるint型の数値を取り出している。 ・pを1つ後ろへずらして、隣のint型を指すようにする。 つまり、変化しているのは (int *)型変数pの値です。 これを「動く」と表現しているのなら ・「配列a」は動かすことはできない ・「(たまたま最初にaを指し示していた)ポインタp」は(ただの変数なので)動かすことができる。 となります。

関連するQ&A