• 締切済み

[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

  • for文

    以下のプログラムのforの条件文がなぜこれで動くのかよくわからないので 教えていただけないでしょうか? #include<stdio.h> int main(void) { int i; char str[] = "ABC"; char *ptr = "123"; for(i=0;str[i];i++) putchar(str[i]); putchar('\n'); for(i=0;ptr[i];i++) putchar(ptr[i]); putchar('\n'); printf("str = \"%s\"\n",str); printf("ptr = \"%s\"\n",ptr); return(0); }

  • ポインタについて

    #include<stdio.h> int main(void) { char str[10]; char *ptr = str; printf("文字列を入力してください。\n"); scanf("%s",ptr); printf("文字列は%sです。",str); return 0; } 上記のプログラムのscanf("%s",ptr);の ptrに&をつけるとなぜ先頭の4文字は入力しても 表示されなくなってしまうのでしょうか? よろしくお願いします。

  • C言語のchar ポインタ

    char ポインタで分からないことがあるのですが・・↓ ------ { char array[7]; char *ptr; strcpy(array, "abcdef"); ptr = &(array[0]); printf(" array = %s\n ptr = %s\n", array, ptr); } ------ これを実行して, array = abcdef になるのは分かるのですが ptr = abcdef  になるのはどうしてでしょうか。 ポインタはアドレスを指し示すものですよね。だったら ptr = にはarray[0]のアドレス('a'のアドレス)が入るのでは 無いんでしょうか・・?

  • C言語 ポインタ エラー

    文字列を入力後改行して入力した文字列を出力したいのですが、エラーがでます。 ハンドルされない例外が 0xFEFEFEFE (helloworld.exe) で発生しました: 0xC00001A5: 無効な例外ハンドラー ルーチンが検出されました。 (パラメーター: 0x00000003)。 調べてみましたが、理解できません 何卒よろしくお願いします。 #include <stdio.h> void put_string(const char *str) { while (*str) putchar(*str++); } int main(void) { char str[100]; printf("文字列を入力してください"); scanf_s("%s", str); put_string(str); putchar('\n'); return(0); }

  • char* を渡したとき、不適切なPtrが出る問題

    こんばんは。プログラムを勉強中の学生です。 詰まった部分があり、関連しそうな部分を勉強しましたが、問題が解決しなかったので、 こちらで質問させて頂きます。 今、とあるクラスで、 class Test{ ........................................ public: int Func1(char* str,){ unsigned int n = 0; while(str != "\0"){ n += *str; str++;}          ←ここに<不適切なPtr> return n % 3; } void Func2(char* str){ int i; i = Calc(str); ..................................... ............................ } }; のように宣言し、main()関数で、 int main(){ Test test; test.Func2("ABC"); // Case1: エラーは起こらない char s[]={"ABC"}; //Case2:不適切なPtrとなる。 test.Func2( s ); } としていますが、上記のように、"ABC"を直接入れたときのみ、うまくいき、 他の方法で、char型のポインタを代入した際には、不適切なPtrと出てしまいます。 この原因を教えていただけないでしょうか? 最終的には、 cin >> s ; などのように、キーボードから入力した値(文字列)を使いたいのですが、 現段階ではmain関数で "ABC"のように書かなければならず困っています。

  • ポインタの問題です

    学校の課題である文字列をポインタを使って逆順に表示するというプログラムを作ったのですが、「問題で引数で与えられた文字列を逆順とする関数を作成せよ.ただし文字数が最大256文字であると仮定して良い.」ということなんですが、課題が求めている答えと自分が作ったプログラムがあっているか確認お願いします。 #include <stdio.h> void reverseWord(char *str); void main(void) { char *str = "sapporo"; char *p, *q; printf("元: %s\n", str); p = q = str; while (*q != '\0') q++; printf("逆順:"); while (q >= p) putchar(*q--); printf("\n"); } よろしくお願いします。

  • charのポインタについて

    同じような質問ばっかりたて続けにしてしまってすみません・・ あの,以下のような例の時↓ ------ { char array[7]; char *ptr; strcpy(array, "abcdef"); ptr = &(array[0]); printf(" array = %s\n ptr = %s\n", array, ptr); } ------ これを実行したら、 array = abcdef ptr = abcdef となりますが、ポインタとして宣言してるptrから文字列を得たい時は *ptr と書くんではないのでしょうか? %s のように出力形式のところを指定をするだけで 文字列で表示してくれるのはなぜですか? 上の例のように書くのが正しいのでしょうか

  • int とcharの使い方と違い

    教えてgooに投稿するのは初めてですがよろしくお願いします。 最近C言語を勉強し始めた初心者です。 ネットのサイトを見て独学でしています。 過去スレッドをさらっと見て聞きたいことが書いてなかったので投稿します。(同じスレッドがあったらすみませんOTL) #include<stdio.h> int main(void) { int str='a'; printf("str=%c\n",str); return0; } この上のプログラムでは str=a と出て、エラーが出ずにコンパイル出来ました。でも、 #include<stdio.h> int main(void) { int str[]="abc"; printf("str=%s\n",str); return0; } とするとエラーが出ます。 int とcharの使い方と違いについて詳しく教えてほしいです>< お願いします。

  • char[]とchar*

    #include<iostream.h> main() { char str1[] = "AB"; char *str2 = "ab"; *(str1+1) = 'C'; *(str2+1) = 'c'; printf("%s\n", str1); printf("%s\n", str2); } このソースの *(str2+1) = 'c'; の所はC++では間違った処理ですか? []かnewなどの変数なら書き換えてよいのは分かりますが、str2はこれでよいのか教えて下さい。

  • C++のコンストラクタを使った自動ポインタでoperator関数の使い

    C++のコンストラクタを使った自動ポインタでoperator関数の使い方で分からないところがあります。 環境下はVisual C++でC/C++のWin32コンソールアプリケーションを使って行っています。 下記のコードで実行させています。やっていることは文字列を反転させて表示させるだけのことです。 #include "stdafx.h" #include <string> #include <iostream> class AutoPtr { char *ptr; public: AutoPtr():ptr(0) { } ~AutoPtr() { delete [] ptr; } // char *operator=(char *ptr) { delete [] this->ptr; this->ptr = ptr; return this->ptr; } operator char *(){ return ptr; } char &operator[](int index) { return ptr[index]; } }; void reverse(char *str) { int i, n; AutoPtr work; n = strlen(str); work = new char[n+1]; strcpy(work, str); for(i=0; i<n; i++) { str[i] = work[n-i-1]; } printf("%s\n", str); } int _tmain(int argc, _TCHAR* argv[]) { reverse("ABCDEFG"); return 0; } これを実行させると、reverse関数のfor()文の、str[i] = work[n-i-1];を実行させた所で実行エラーになってしまいます。その前の、strcpy(work, str);でworkにstrの内容が正常にコピーされているところまでは確認できています。operator関数の、 char &operator[](int index) { return ptr[index]; } で、operator[]はAutoPtrを配列のように扱っているはずなのですが、何故かstr[i] = work[n-i-1]; の所で実行エラーになってしまいます。 operaror関数の書き方が悪いのか、何が原因なのか分かりかねています。御経験のあるかたは、御教示いただけたらと思っています。 よろしくお願い致します。

専門家に質問してみよう