• ベストアンサー

ダブルポインタで2次元配列を作成

S117の回答

  • S117
  • ベストアンサー率40% (18/45)
回答No.16

言語使用の解説からちょっと行ってみましょう。 質問者様は左辺値と右辺値をご存知ないようですね。(もしご存知ならこれを見て復習してください。) Cの値には左辺値と右辺値があり、右辺値は1だとか2だとかいった数値などです。 左辺値とは右辺値の入れ物を指すものです。 a = 10; とあれば、aが左辺値、10が右辺値です。 また、左辺値は右辺値が要求される場所にあった場合、自分の持っている右辺値を返します。 a = b; bは左辺値ですが、bは自分の持っている右辺値を代入演算子に渡します。 「&」はアドレス演算子といいます。 この演算子は単項演算子で、右結合です。 オペランドとして左辺値を取り、そのポインタを返します。 ポインタとはそれだけではただの右辺値であり、左辺値ではありません。そこで「*」が登場します。 「*」は間接演算子といい、ポインタ型の左辺値を右辺値にします。 よって以下のとおりとなります。(コメントは面倒なんでC99方式) char *p; //ポインタ char c = 'a'; //char型 p = &c; //cのアドレスをpに保存 (cは左辺値でアドレス演算子により右辺値のchar*型の値を返す。pは左辺値なので、pの右辺値が書き換えられる。 *p = 'b'; //pはchar*型だが、間接演算子によりポインタの指す場所(c)の左辺値を返す。結局この式はc = 'b';と同じになる。 putchar(*p); //pはchar*型だが、間接演算子により該当する場所の左辺値(cと等しい)を返す。しかし、関数の引数は右辺値を取るので*pの持つ値を返す。結局この式はputchar('b')ということになる。 という感じです。右辺値と左辺値については参考URLをご覧ください。 要するに左辺値は場所、右辺値は数値であり、場所からしかアドレス演算子でアドレス(ポインタ)が取れないのです。 ポインタ自体は場所を示す数値であり、場所じゃないので、間接演算子で場所を取ります。 そして、配列は先頭から連続する場所として作られるので、先頭アドレスにインデックスを加算したアドレスを間接演算子に渡すと、配列の指定されたインデックスの場所を返してくれるわけです。 そして、場所からは数値が取れますが、数値からは数値がいる場所は取れません。たとえば、左辺値を箱、右辺値を箱に入ってる玉として、玉を見るためには箱から取り出す必要があったとします。 箱に入ってる玉は、箱を開けて取り出してみれば分かります。箱を開けて玉を取り出しました。さて玉はどの箱に入っているのでしょうか? さっきの箱? いいえ、違います。取り出してるので今手元にある玉はどの箱にも入っていません。 どの箱にも入っていないのだから、箱の場所は分かりません。 実際の計算機上ではきっとレジスタといわれるアドレスの取れない一時的な記憶領域に計算のために、(あるいは計算の結果として)置かれています。

参考URL:
http://ja.wikipedia.org/wiki/%E5%80%A4_(%E6%83%85%E5%A0%B1%E5%B7%A5%E5%AD%A6)
karasu4649
質問者

お礼

みなさん、ありがとうございました。 とりあえず、浅い理解はできました。 これ以上深追いするのはやめておきます。 数学でもなんでもそうですが、ある時突然わかる日が来る場合もあります。 僕は頭が悪いのでその日は来ないかもしれません。 しかし大変ためになりました。

関連するQ&A

  • 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で返ってくるのは、ポインタ変数(の配列)だと思うので、変数のモードを切り替えるためにアスタリスクが必要なのだと思っていましたが、どこかで重大な勘違いをしているようです。 この件について、どなたか教えていただけないでしょうか。

  • 2次元配列とポインタ配列の違い

    2次元配列とポインタ配列の違いを比較するプログラムを作成したつもりなのですが、下のプログラムの printf("we[%d]=%u\n",i,&we[i][0]); という文がどうやら違うらしいのですが、全く分かりません。 なので、なんとなくでも構わないので分かる方がいらっしゃったらお願いします。 #include<stdio.h> int main(void) { int i; char *we[]={"Monday","Tuesday","Wednesday"}; char ek[][10]={"Monday","Tuesday","Wednesday"}; printf("2次元配列で格納したアドレス\n"); for(i=0;i<3;i++){ printf("ek[%d]=%u\n",i,&ek[i]); } printf("ポインタ配列のアドレス\n"); for(i=0;i<3;i++){ printf("we[%d]=%u\n",i,&we[i]); } printf("ポインタ配列で格納したアドレス\n"); for(i=0;i<3;i++){ printf("we[%d]=%u\n",i,&we[i][0]); } printf("ポインタ配列で格納した文字列\n"); for(i=0;i<3;i++){ printf("we[%d]=\%s\n",i,we[i]); } printf("2次元配列で格納した文字列\n"); for(i=0;i<3;i++){ printf("ek[%d]=\%s\n",i,ek[i]); } return(0); }

  • ポインタと配列 助けてください

    #include <stdio.h> int main(void) { int a[5] = {1, 2, 3, 4, 0}; int i, *ptr; ptr = ######; while (*ptr != 0){ ######; ######; }      for (i = 0; i < 5; i++) printf("a[%d]=%2d &a[%d]=%p\n", i, #####,i, #####); putchar('\n'); ptr = ######; for (i = 0; i < 5; i++){ printf("ptr値 =%d ptrアドレス =%p \n", ####, ####); ####; } printf("\n"); return (0); } 一次元配列a[]の一番目の要素以外を0にするプログラム(ただし、最後の要素は0)を作成したいのですが、#の部分に何をあてはめたらいいかわかりません。 助けてください。

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • ポインタ型配列のポインタを構造体のポインタ変数に格納する方法教えて!_ver2

    int型の配列は構造体のポインタ型のint型変数にはキャストすればうまくコンパイルが通りますが、同じ方法でfloat型の配列は構造体のポインタ型のfloat型変数にはキャストしてポインタの値を入れてもうまくコンパイルできず困っています。ちなみにコンパイルエラーは「互換でない型変換」と表示されます。 返答のほど、よろしくお願いいたします。 #include<stdio.h> #include<malloc.h> float time[] ={2.2, 2.3, 2.4}; int time2[] ={2, 2, 2}; struct timelist{ float *time; int *time2; struct timelist *next; }*head; void main(void) { struct timelist *p; p = (struct timelist *)malloc(sizeof(struct timelist)); p -> time = (float *)time[0]; p -> time2 = (int *)time2[0]; printf("time = %f\n", p -> time); printf("time2 = %d\n", p -> time2); }

  • 引数で指定された配列の要素数の取得

    どうもこんにちは。 C言語でプログラムを作成しています。 ある関数に配列を渡すことを考えていますが、渡した配列の要素数を取得する方法は何かありますか? 標準の関数を見ても、配列の先頭アドレスのポインタとともに、配列の要素数を渡しているものばかりで、配列のポインタを渡しているものは見かけません。 要素数があらかじめわかっていれば、それを引数の型に指定できますが、呼び出されるまで不明な場合はうまくいきません。 配列の要素数も引数として一緒に渡す必要がありますか? [作ってみたサンプル] #include <stdio.h> #include <stdlib.h> #include <string.h> // func1 と func2 をまとめられないだろうか。。。 void func1(int (*p)[10]) { ________int n = sizeof(*p) / sizeof((*p)[0]); ________int i; ________for (i = 0; i < n; i++) { ________________printf("%d\n", (*p)[i]); ________} ________printf("\n"); } void func2(int (*p)[5]) { ________int n = sizeof(*p) / sizeof((*p)[0]); ________int i; ________for (i = 0; i < n; i++) { ________________printf("%d\n", (*p)[i]); ________} ________printf("\n"); } int main(int argc, char *argv[]) { ________int ary1[10] = { 2, 4, 6, 8, 0, 1, 3, 5, 7, 9 }; ________int ary2[5] = { 3, 6, 9, 12, 15 }; ________func1(&ary1); ________func2(&ary2); ________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することでなぜエラーを回避することができるのでしょうか? よろしくおねがいします.

  • 2次元配列からダブルポインタへの合わせ方

    固定要素数の行列の処理ですが、subでセグメントエラーを起こしています。 アドレスを出力してみると、subでは元々確保したアドレスでは無いところを指しています。 呼び出し側では「行列の先頭のアドレス」を明示したつもりだったのですが、どこが間違っているのでしょうか。少し長いですが、ソースコードと出力を示します。 typedefのところとsubのIFは変更できないので、double[3][3]とdouble** のIFを合わせないといけないのですが、合わせ方がよくわかりません。 よろしくお願いします。 ========================================= #include <stdio.h> #include <string.h> typedef double Matrix[3][3]; void sub( const double** mat ); int main(void) {  int ret = 0;  int i, j;  Matrix mat = { { 0., 1., 2. },      { 3., 4., 5. },      { 6., 7., 8. } };    for( i = 0; i < 3; i++ ){   for( j = 0; j < 3; j++ ){    printf( "main : mat[%d][%d] = %f\n", i, j, mat[i][j] );    printf( "main : &mat[%d][%d] = %p\n", i, j, &mat[i][j] );   }  }  printf( "\n" );  sub( &mat[0][0] );  return ret; } void sub( const double** mat ) {  int i, j;  printf("---- &mat = %p\n", &mat );  for( i = 0; i < 3; i++ ){   for( j = 0; j < 3; j++ ){    printf( "sub : mat[%d][%d] = %f\n", i, j, mat[i][j] );    printf( "sub : &mat[%d][%d] = %p\n", i, j, &mat[i][j] );   }  }  printf( "\n" ); } ========================================= main : mat[0][0] = 0.000000 main : &mat[0][0] = 0x7ffffca7bca0 main : mat[0][1] = 1.000000 main : &mat[0][1] = 0x7ffffca7bca8 main : mat[0][2] = 2.000000 main : &mat[0][2] = 0x7ffffca7bcb0 main : mat[1][0] = 3.000000 main : &mat[1][0] = 0x7ffffca7bcb8 main : mat[1][1] = 4.000000 main : &mat[1][1] = 0x7ffffca7bcc0 main : mat[1][2] = 5.000000 main : &mat[1][2] = 0x7ffffca7bcc8 main : mat[2][0] = 6.000000 main : &mat[2][0] = 0x7ffffca7bcd0 main : mat[2][1] = 7.000000 main : &mat[2][1] = 0x7ffffca7bcd8 main : mat[2][2] = 8.000000 main : &mat[2][2] = 0x7ffffca7bce0 ---- &mat = 0x7ffffca7bc68 セグメンテーション違反です

  • ポインタと二次元配列

    二次元配列a[ ][ ]の第1行の要素以後を0にするプログラムで動きます。 疑問なんですが、4行目のa[ ][4]は、なぜ[ ]のように空欄になっているんでしょうか。 また、8行目のpa=a[1];は、なぜpa=a[0];ではだめなんでしょうか。第1行は先頭行で0行目を意味しているのではないでしょうか。 #include <stdio.h> void main(void) /*ポインタと二次元配列*/ { static int a[ ][4]={{1,2,3,0}, {4,5,6,0}, {7,8,9,-999}}; int j,k,*pa; pa=a[1]; while(*pa!=-999){ *pa=0; pa++; } for(j=0;j<3;j++){ for(k=0;k<4;k++) printf("%5d",a[j][k]); printf("\n"); } }

  • 二次元配列のポインタについて教えて下さい

    今日はCの配列のポインタについて質問いたします、宜しくお願いします。 1次元の配列からは、 =================================== int *p , a={1,2,3}; p = &a; printf("%d\n" , p[1] ) ; printf("%d\n" , *p[1] ) ; ===================================== でaの値がとれますが、 二次元の配列では下のような書き方ではエラーになります。 何故でしょうか、どう書いてやればいいのでしょう。 ===============================================  printf("%d\n" , pbb[1][1] ) ;  printf("%d\n" , *pbb[1][1] ) ; =============================================== 宜しくお願いします。