• 締切済み

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

  • C言語の配列とポインタについて

    C言語の配列とポインタについてわからないことがあります。 以下のソース例は、10個の値の平均値を求めるプログラムです。 コメントを挟んだ部分が疑問点です。 【ソース例】 #include <stdio.h> int getaverage(int *data); int main(void) { int average,array[10] = {15,78,98,15,98,85,17,35,42,15}; average = getaverage(array); printf("%d\n",average); return 0; } int getaverage(int *data) { int i,average = 0; for (i = 0;i < 10;i++) { average += data[i]; /*ポインタ変数なのに? */ } return average / 10; } 【実行例】 49 このdata[i]はポインタ変数であり、 配列arrayの i 番目の要素であるarray[i]の"アドレス" が代入されているはずだと思うのですが、 なぜ通常の整数変数であるaverageと数値計算が出来、正しい結果が出たのでしょうか? あたかもdata[i]には、 array[i]の"アドレス"ではなく、 array[i]の"メモリの中身"が代入されているようです。 どういうことでしょうか? 回答よろしくお願いします。

  • C言語、配列とポインタとアスタリスクの関係

    ちょっと行き詰まっています。 苦しんで覚えるCで勉強しているのですが、まさに苦しんでいます。 http://9cguide.appspot.com/19-01.html #include <stdio.h> #include <stdlib.h> int main() { int i; int *heap; heap = (int *)malloc(sizeof(int) * 10); if (heap == NULL) exit(0); for (i = 0;i < 10;i++) { heap[i] = i; } printf("%d\n",heap[5]); free(heap); return 0; } int *heap; ここで int ポインタを宣言しています。 heap = (int *)malloc(sizeof(int) * 10); ここでヒープを確保しています。(int *) のキャストも sizeof(int) も理解できました。 for (i = 0;i < 10;i++) { heap[i] = i; } まず1点目の疑問はここです。 変数 heap は「ポインタ変数」です。それでいて配列です。 ポインタ変数は、プログラムの文中で通常の変数として使うときには「*heap」のように先頭にアスタリスクを付けなければならかなったと記憶しています。 アスタリスクなしの「heap」はアドレス格納用の変数ではないでしょうか。 printf("%d\n",heap[5]); そして、その疑問をよそに、この命令が成り立っているようです。 画面上に出される結果は「5」であり、変数「heap」がただの配列として機能しているように見えます。 この printf 次のように書き換えると、エラーが出てコンパイルできませんでした。 書き換え実験1 printf("%p\n",*heap[5]); アスタリスクを付けて、通常の変数として扱い、受ける方も「%d」から「%p」に書き換えてアドレスを表示してみようと思ったのですが、 「「pointer」を付け忘れています。」というエラーが表示されました。 書き換え実験2 printf("%p\n",heap[5]); 受ける方を「%d」からポインタを受ける「%p」にしましたが、変数の方はアスタリスクなしです。 すると、結果はアドレス「00000005」が返ってきました。 (変数にアスタがないのになぜ?) 書き換え実験3 printf("%d\n",*heap[5]); これはもうめちゃくちゃですが、一応やってみました。コンパイルエラーで、 「「pointer」を付け忘れています。」というエラーが表示されました。 つまり、こういうことです。 0:printf("%d\n",heap[5]); //5 1:printf("%p\n",*heap[5]); //エラー 2:printf("%p\n",heap[5]); //00000005 3:printf("%d\n",*heap[5]);//エラー この結果から推測するに、アスタリスクはそもそも付けるとエラーになり、アドレスを表すか、そのアドレスに格納された値を表すかを切り替えるには、単にその変数を受ける「%d」や「%p」を変えるだけ、ということになるのだと思います。 mallocで返ってくるのは、ポインタ変数(の配列)だと思うので、変数のモードを切り替えるためにアスタリスクが必要なのだと思っていましたが、どこかで重大な勘違いをしているようです。 この件について、どなたか教えていただけないでしょうか。

  • C言語で困っています

    C言語で 100:1 99:2 98:3 .......ループ 1:100とやりたいんですが このやりかたがわかりません goto文などを使うのでしょうか? 一応コードは #include <stdio.h> int main(void) { int i,a; for(i=100; i>=1; i--){ for(a=1; a<=100; a++){ printf("%d:\n",i); break; printf("%d",a); } } return 0; } です まったくこれだと右側が出力されなくてだめみたいです どなたかお知恵をください お願いします

  • C言語のポインタ

    あまり意識せずにポインタを使っているせいか,次のプログラムではまってしまいました. #include<stdio.h> #include<stdlib.h> int main(void) {  int *p, q;  p = (int *)malloc(sizeof(int));  q = (int *)malloc(sizeof(int));  *p = 2;  printf("%d\n", *p);  return 0; } コンパイルエラーで実行ファイルが出力されません. このプログラムで変数qはなぜポインタじゃないのでしょうか? 次にtypedefでptr_intという型を定義したプログラムは, 上のようなエラーが出力されず,期待とおりの結果になりました. #include<stdio.h> #include<stdlib.h> typedef int* ptr_int; int main(void) {  ptr_int p, q;  p = (int *)malloc(sizeof(int));  q = (int *)malloc(sizeof(int));  *p = 2;  *q = 3;  printf("%d\n", *p);  printf("%d\n", *q); return 0; } typedefすることでなぜエラーを回避することができるのでしょうか? よろしくおねがいします.

  • C言語で分からないところがあるのですが……

    C言語で分からないところがあるのですが…… すみません。C言語の課題で分からないところがあり、質問しに来ました。 ユーザから数を受けて、そこまでのフィボナッチ数列を表示させるというプログラムです。 下のソースコード(でいいんですよね?)は正しいやつです。 for文を使って、繰り返しの作業を行うことに成功しましたが、doとwhileに書き換える作業がうまくできません。 どなたか助けてください。 #include<stdio.h> fib(int n) { if(n == 1)return(1); else{ if(n == 2) return (1); else return fib(n-1) + fib(n-2); } } main() { int n, i ; printf("INPUT the number. : "); scanf("%d",&n); for(i=1; i<=n; i++){ printf("F%d = %d\n",i, fib(i)); } }

  • // c のポインタと配列について質問です。

    // c のポインタと配列について質問です。 //次のようなプログラムがあります。 /* main.c */ #include <stdio.h> int main(void) { int array[5] = { 0, 1, 2, 3, 4 }; int i; int* p = &array[0]; for( i = 0; i < 5; ++i ){ printf( "%d ", p[i] ); } puts(""); return 0; } /* これをデバッグすると、定義されていないアドレスの配列 p[i] のため、エラーになるような気がしますが、実際には 1 2 3 4 5 と出力されます。 ここで質問ですが、 p[i] とは何を指しているのですか? できればc初心者の中学生でも分かるように教えてください。 p.s. 余計な御世話かもしれませんが、これをこのままコピペすれば、デバッグできます。 */

  • C言語の初心者です。

    ポインタについて教えてください。 #include <stdio.h> int main() { int *p,a[2],i=0; p=a; scanf("%d",p); printf("%d\n",*p); i++; scanf("%d",p+i); printf("%d\n",*(p+i)); return 0; } これは整数を入力して出力といった簡単なプログラムなのですが、こういうポインタの使いかたって普通はしないでしょうか・・?。ポインタは使わずにこういう場合は2次元配列とかで出すのが普通なのでしょうか? また、下のプログラムなのですが、上記のプログラムをただ単にキャラ型で試しただけなのですが、どうして実行時エラーがおこってしまうのでしょうか? printfのところの*nameの、*を抜くと実行時エラーはおこりません。しかし、上記の整数では*はつけたままでも エラーしません。 整数とキャラ型では何かちがうのでしょうか?? #include <stdio.h> int main() { char *name,a[10]; name=a; printf("入力---"); gets(name); printf("%s\n",*name); return 0; } 初心者特有の質問をしてしまって申しわけございません。でも、ずっと悩んでて、すこしずつでもポインタの本当の使いかたを知りたいとおもいまして、初期段階ではありますがどうしても解らなかったので質問させていただきました。 そういうものだから!以外でご説明いただける方がいらっしゃいましたら宜しくお願いいたします。

  • 【C言語】別関数でポインタの値を変えたのに変わらない。

    【C言語】別関数でポインタの値を変えたのに変わらない。 メイン関数のポインタの値を、別関数で書き換えるプログラムを作りました。 以下がそのプログラムになります。 そのままだと、ダブルポインタを操作する必要があるので分かり辛いです。なので、ダブルポインタをシングルポインタにしてからポインタの書き換えを行うようにしました。その結果、きちんとポインタの書き換えが出来なくなってしまいました。 なぜ出来なくなってしまったのでしょうか。 2つのプログラムの違いは、 >  *pp = &dummy; が >  p = *pp;      // ダブルポインタをシングルポインタにした >  p = &dummy; に変わっただけです。 【参考】http://www.kouno.jp/home/c_faq/c4.html#8 -----------------正しいプログラム---------------- // 以下プログラムは、正しく動作する // 実行結果は、 //   p = 5 // と表示される void func( int **pp ); int main (void){   int *p;   int a = 0;   p = &a;   func( &p );   printf("p = %d\n", *p);   return 0; } void func( int **pp ){   static int dummy = 5;   *pp = &dummy; } ---------------------------------------------- -----------------間違いプログラム---------------- // 以下プログラムは、正しく動作しない // 実行結果は、 //   p = 0 // と表示される void func( int **pp ); int main (void){   int *p;   int a = 0;   p = &a;   func( &p );   printf("p = %d\n", *p);   return 0; } void func( int **pp ){   static int dummy = 5;   int *p;   p = *pp;      // ダブルポインタをシングルポインタにした   p = &dummy; } ----------------------------------------

  • ポインタ

    C言語のポインタの勉強をしています。//エラー と書いている行でエラーがでます。*(a++),*(b++),*(c++)をそれぞれ*(a+i),*(b+i),*(c+i)とすればエラーがなくなるのですが、違いがわかりません。 先頭アドレスをセットしてないからエラーが出たのかと思いましたが、それが原因なら*(a+i),*(b+i),*(c+i)でエラーがなくなるのはどうしてでしょうか? 違いを教えて下さい。よろしくお願いします。 #include<stdio.h> int main(void) { void p_add(int *a, int *b, int *n); int i; int a[10] = {3,7,1,2,8,9,0,4,6,5}; int b[10] = {7,3,9,8,2,1,10,6,4,5}; int c[10]; memset(c,0x00,sizeof(c)); p_add(a, b, c); for(i = 0; i < 10; i++){ printf("a[%d]=%d b[%d]=%d c[%d]=%d\n", i, *(a++), i, *(b++), i, *(c+ +));                                               // エラー } return 0; } void p_add(int *a, int *b, int *n){ int i; for(i = 0; i < 10; i++){ *(n++) = *(a++) + *(b++); } }

  • C言語の配列について

    配列を20 定義し値を入力して合計値を出したいのですがどうすればよいのでしょうか 下のソースでエラーはおこりませんでした 何がちがうのでしょうか #include <stdio.h> int main() { int a[20]={}; int i, sum; printf("整数を入力してください:"); scanf("%d",&a); printf("\n"); for (i = 0; i < 10; i++) { sum += a[i]; } printf("sum= %d\n", sum); return 0; }

専門家に質問してみよう