• 締切済み

[C]char型のダブルポインタ

粗雑で申し訳ありませんが、 以下のソースをコンパイルできましたが、 うまく実行できません。 自分なりに間違いがないと思うのですが、 間違い等をご指摘頂ければ助かります。 #include <stdio.h> void func(char **ptr) ptr[][10] か (*ptr)[] なら通る *ptr[] は通らない { printf("----- func -----"); printf("%s\n", *ptr); printf("%c\n", **ptr); putchar('\n'); } int main(void) { char str[5][10] = {"AAAAA", *str[] にすると func で **ptr で通る "BBBBB", "CCCCC", "DDDDD", "EEEEE", }; printf("----- main -----"); printf("%s\n", *str); printf("%c\n", **str); putchar('\n'); func(str); return (0); } 実行結果 ----- main ----- AAAAA A ----- func ----- Bus error (core dump) 関数への受け渡しで、型が違うというお叱りを受けますが、 コンパイルはできました。 コンパイラはCCです。 ではよろしくお願いします。

みんなの回答

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.4

 #2です。  あれと思ってよく見ると・・・  あっ。ほんとだわ。間違ってる=^・・;=  (4)の[]に要素数定義するのが抜けてますね。たしかに。配列のよう素数は、先頭次元以外は省略できないので、fo-oさんが正しいです。このままでは、添え字演算ができません。確かに。  失礼しました。 

  • fo-o
  • ベストアンサー率50% (4/8)
回答No.3

mitonekoさんの#2の回答に対して勝手に補足しちゃいます。(汗 最後のほうの    まず、(1)と(3)は型が違うので、コンパ(イ)ラー... って所は、(2)であるのに(3)とタイプミスしたんだと思います。 最初のほうの   まず、(4)は、(3)と等価です って所は違うと思います。(4)はコンパイルが通ってしまいますが、ptrをインクリメント(++ptr)したり、配列でアドレッシング(ptr[1][2])したりするとコンパイルエラーがでます。なんで(4)なんかが警告すら表示されないでコンパイルが通るのか不思議です。(3)と等価なのは、 func(char (*ptr)[10]) //(5) func(char ptr[][10]) //(6) だと思います。 意味的には (1)はchar型ポインタのポインタ (2)はchar型ポインタの配列 (3)はptrが10個のchar型配列が5個ある配列 (4)はわからん (5)はptrが10個のchar型配列きざみにアドレッシングするポインタ (6)はptrが10個のchar型配列が何個かある配列 が厳密な意味の違いだと思うのですが、関数の引数の宣言では(2)は(1)として、(3),(6)は(5)として扱っているようです。 どうしても(1)のような形でアクセスしたいなら、strの宣言の後に、 char *strsub[] = { str[0], /* strsub[0]に"AAAAA"の先頭アドレスが入る */ str[1], /* strsub[1]に"BBBBB"の先頭アドレスが入る */ str[2], /* strsub[2]に"CCCCC"の先頭アドレスが入る */ }; このように、char型ポインタの配列を作っておいて、 func(strsub); とするしかないです。 そして、"char *str[]"として宣言、定義した場合、strsubのような配列をコンパイラが自動的に作るようです。でも、**ptrで読み取りはできるけど、**ptr='z'とかの書き込みはできません。コンパイル時に書き込み不可能のメモリ位置に*str[]が置かれるみたいです。 つまり、char **ptrで操作したいならstrsubのような(その先の配列も含めた)メモリーイメージをもったものを作らなければうまくいきません。 くどいようですが、例でいうと、main関数の引数宣言は、main(int argc, char**argv)ですね。 この引数のイメージは、プログラム名の配列、第一引数の配列、第二引数の配列... の他に、プログラム名の配列の先頭アドレス、第一引数の配列の先頭アドレス、第二引数の... を要素にした配列が存在します。そしてargvはこのアドレスを要素とした配列の先頭アドレスが入ります。 結局のところこの辺はCでは一番難しくこつこつメモリのアドレスやら値やらを細かく調べて自分自身で理解していくしかないです。頑張ってください。わかりにくい説明で申し訳なし。(汗

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.2

 回答は、全部、あなたが書いておられるコメントの中に書いているのですが・・・(苦笑)  あなたがfuncの後ろにかかれたコメントが回答のすべてです。 func(char **str) //(1) func(char *str[]) //(2) func(char str[5][10]) //(3) func(char (*str)[]) //(4)  まず、(4)は、(3)と等価です。  多次元配列を関数に渡すときの宣言においては、配列の先頭の[]の数「のみ」は省略することができます。よって、(3)は、func(char str[][10])と書くことができます。配列名は、配列の先頭アドレスと等価に扱われるので、これをポインターの形で書き換えたのが(4)となります。  さて、ここで、(2)の表記をちょっと調べてみます。  これは、実は、まったく意味が違います。 func(char *str[]) //(2)  この表記では、[]は、*より優先順位が高いので、 func(char *(str[]))と解釈されます。  つまり、strは、charへのポインターが格納された配列を意味します。というわけで、ここでstr[0]には、文字へのポインターが入っていることになります。  宣言として、 char *str[5];  と書いた場合には、文字へのポインターが5つ並んで格納されます。  (念のために書いておくと、char str[5][10]には、50個の文字が格納されています。長ったらしく書くと、10個の文字が格納されたメモリー領域が連続して5つ並んでいます。)  そうして、配列名は、配列へのポインターと等価に扱えるので、(2)をポインターで表記すると、(1)になります。    そう。(1)(2)と(3)(4)は、実はメモリーイメージがまったく違う宣言なのでした。    では、最後に、例示されたプログラムがコアダンプをはき出す理由を書いておきます。  まず、(1)と(3)は型が違うので、コンパラーは警告を吐きます。(ほんとはエラーにして欲しいくらいの警告です。)まぁ、ポインターは「偶然」どんな型に対しても同じメモリーイメージを取っていたために、コンパイルはできていまいます。  funcの中で、printf("%s\n",*str)を実行したとき、何が起こるか考えてみましょう。strは、mainで定義された配列へのポインターです。よって、strがさすメモリーには、'A'が入っています。次のアドレスも同じ数値が入っています。(asciiコードだとすると、0x4141がはいっています。)ptintfにこれを渡すわけですが、書式設定で、%sと指定されましたから、ポインターの大きさが2バイトと仮定すると、パラメータの大きさも2バイトのはずです。よって、0x4141を文字列へのアドレスとして解釈しますが・・・・メモリーアドレス0x4141に意味のある文字列が入っているはずがありませんし、アプリケーションがアクセスして良いアドレスであるとも限りませんよね?この場合は、どうやら、アクセスしてはならないアドレスだったようです。よって、プログラムは(というよりOSは)メモリーアクセス禁止領域にアクセスされた場合の規定の行動として、コアダンプをはき出します。(運が良かったですね。コアダンプでも出さなければ、バグ表面化しないこともありますから。デバッグ中に表面化しないというのは、バグが無いわけではないので、ある日突然、最悪のタイミングを計って登場します=^・・;=)  

  • shige_70
  • ベストアンサー率17% (168/946)
回答No.1

2次元配列とポインタ配列は、別物です。 この辺は、初心者がつまづくポイントであると同時に上級者でもたまに勘違いしている人がいるくらいです。 おそらくあなたも、2次元配列が内部では配列のアドレスが並んでいると思っていらっしゃるでしょうけど、実際は違います。 たとえば、char str[5][10]とあったら、10バイトのchar配列が5個作られたうえでそれぞれを指す5個のポインタが作られる、と思うかもしれませんが違います。 実際は、strが指す50バイトの連続した領域が確保された上で、最初の10バイトがstr[0]、次の10バイトがstr[1]、、、となります。 ようするに、str[a] は str+a*10 と同じ、また str[a][b] は *(str+a*10+b) と同じです。 そして、str と str[0] は同じアドレスを指しているはずです。 上記を踏まえて、何がいけなかったのか、いま一度見直してみてください。

関連するQ&A