• ベストアンサー

文字列配列を動的に割り当てるアルゴリズムはこのようなものであっていますか?

結果的に char strs[10][10]; と同じ領域を確保しようというものです。 ちなみにコンパイル時に warning C4700: local variable 'strs' used without having been initialized という警告がでますが、きちんと動作します。 この警告の意味することがわからないのと、今回のようなアルゴリズムとして適当なものであるのか教えてほしいです。 ↓以下ソース↓ #include <stdio.h> #include <stdlib.h> int main(){ char **strs; *strs = (char *)malloc(10); for( int i=0 ; i < 10 ; i++ ){ strs[i] = (char *)malloc(10); } strs[0] = "maiueo"; strs[1] = "kakiku"; printf("%s", strs[0] ); printf("%s", strs[1] ); for( int i=0 ; i < 10 ; i++ ){ free(strs[i]); } free( *strs ); return 0; }

質問者が選んだベストアンサー

  • ベストアンサー
  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.1

>*strs = (char *)malloc(10); の部分strs は、ポインタへのポインタですけど、 その指し示すポインタがありません。(なので初期化せずに使っていると怒られます) >strs[0] = "maiueo"; >strs[1] = "kakiku"; mallocで確保したのとは別のポインタが代入されているので、 このあとのfree が(正常には)できません。 以上に注意して書き直したもの ---------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> int main(){ char **strs; strs = (char **)malloc(sizeof(char*)*10); for( int i=0 ; i < 10 ; i++ ){ strs[i] = (char *)malloc(10); } strcpy(strs[0],"maiueo"); strcpy(strs[1], "kakiku"); printf("%s\n", strs[0] ); printf("%s\n", strs[1] ); for( int i=0 ; i < 10 ; i++ ){ free(strs[i]); } free( strs ); return 0; }

ggaogg
質問者

お礼

ソースありがとうございます。 や、やっと理解できました。 ソースを見ながら考えて、やっとこさわかりました。 概念としては strs (char *)[0]--"string" (char *)[1]--"string" (char *)[2]--"string" (char *)[3]--"string" といったかんじでしょうか。 しかし、よくよく考えてみると、私のソースでも (char **)strs ↓(アドレス) (char *)[0]--"string0" (char *)[1]--"string1" (char *)[2]--"string2" (char *)[3]--"string3" となり、この場合[0]を開放すると、"string0"の領域のみが開放されるのであり、[0]はいぜんとして(char *)のままだと思います。よってstrs領域は開放されないままであり、最後にfree(strs)としても成功するような気が。 それでも警告をださずにコンパイルするとなると、BLUEPIXYさんの示してくれたソースのようにする以外はなさそうですが、、、 私の考えはいぜんとして間違ったままでしょうか?

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (3)

  • rickky
  • ベストアンサー率66% (2/3)
回答No.4

buf[10][10]ではわかりにくいので、  char buf[100][80]; で説明します。 「配列へのポインタ」を1個宣言します。  char (*p)[80]; malloc関数で領域を取ります  p = (char (*)[80])malloc(100*80); そうすれば、   p[1][2]='A'; などのように、ご自由にお使い下さい。 プログラムの中で、 strs[0] = "maiueo"; strs[1] = "kakiku"; このような代入をしたいのであれば、それは「ポインタ配列」宣言です。 char **strs; strs = (char **)malloc(10*sizeof(char *)); strs[0] = "ABCDE"; strs[1] = "FGHIJ"; 普通は、 strs[0] = (char *)malloc(計算された文字数); のように、さらにmalloc関数を使います。 (1文字1バイトの場合)。

ggaogg
質問者

お礼

p = (char (*)[80])malloc(100*80); こ、こんな使い方もできるんですね・・。 なんとなく読むことくらいはできるのですが、こんな表現はじめて見ました。 これで素直にp[0]からp[n]まで100個ずつ割り当てられるなんて~すごい。 ちょっと私のレベルを超えた表現みたいですが、参考になりました。ありがとうございます。 それと、 strs[0] = "maiueo"; としてたのは、私のよく犯すミスで、本当はstrcpyでコピーしたような感じを頭で思い描いてソースかいていたりします・・。 質問でまぎらわしい間違いをしてすみませんでした。

全文を見る
すると、全ての回答が全文表示されます。
  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.3

>この場合[0]を開放すると、… #1で言う、「その指し示すポインタがありません。」は、 >*strs = (char *)malloc(10); で、確保された領域のアドレスはどこに格納されているのか? ということが問題です。 例えば、 char area1[10]; char *p1; の時 p1=&area1[0]; とか p1=area1; とするわけですが、この場合は、 area1[10]の領域の先頭アドレスがポインタp1 に格納されるワケです。 ここで、 char **pp1; とした場合、 pp1=&p1; とした場合は、 *pp1=area1; は、p1=area1 と同じ意味になりますが、 pp1=&p1; とせずに *pp1=area1; とすると、まだその指している場所(先述で言うとp1 にあたる)がありません。 by the way... 「このあとのfree が(正常には)できません」ですが strs[0] = (char *)malloc(10); とした場合、strs[0] には、malloc で確保した領域のアドレスが入っています。 これは、 free(strs[0]) で解放できます。 が、しかし strs[0] = "maiueo"; とした場合には、strs[0] には、定数としてあらかじめ確保された領域のアドレスが設定されているのであって free("maiueo"); ができないように strs[0] = "maiueo"; としてしまったら free(strs[0]) ; とはできません

ggaogg
質問者

お礼

あ、なるほど。 そもそもstrcpyを使ってコピーするということを忘れてたんですね。 char **ppstr; があって *ppstr = (char *)malloc( 10 ); としたのでは、最終的に指し示す部分に割り当てられるのに対して ppstr = (char **)malloc( (char *) * 10 ); とすれば、2次元配列の列を割り当てることができる、ということだったんですね・・。 ありがとうございます。頭スパークしてたみたいで、こんな単純なことがわからなかったみたいです。

全文を見る
すると、全ての回答が全文表示されます。
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

#1の方の指摘されている内容以前に.. > char strs[10][10]; ということであれば、 char (*strs)[10] = malloc(sizeof(char[10][10])); ではないでしょうか?

ggaogg
質問者

お礼

回答どうもです。 いまだに#1の方の書いてある内容が把握できず考え中なのですが、動的に割り当てるのは使用するメモリは極力抑えておいて、必要になったら後から付け足していくためのものなので、列と番を同時に宣言しては意味がないのです。 例えば、 (char *)[0] (char *)[1] . . (char *)[50] といったかんじでメモリ確保しておき、必要になったらそれぞれをそれぞれの長さでメモリ確保する必要があるので。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 動的に生成した文字列の配列を返す関数について

    動的に生成した文字列の配列を返す関数について お世話になります。 動的に文字列の配列を生成する関数を作ったのですが、 配列をうまく受け渡すことができず困っています。 以下のように入力された件数の数だけ "abc 0"~"abc n"という文字列を生成を行っています。 関数自体は期待通りの動作をしているようなのですが、 (Test1関数の最後でbfを確認しました) 呼び出し側にうまく配列を渡すことができません。 以下にソースを掲載いたしますのでどなたかご教示いただけたらと思います。 環境はVisualStudio2005です。 よろしくお願いします。 #include <stdio.h> #include <string.h> #include <stdlib.h> void Test1(char **bf, int *cnt) { int i; int kensu; int charlength; char num[10]; char **nm1 = NULL; char **nm2 = NULL; printf("件数を入力:"); scanf("%d",&kensu); for(i=0; i < kensu; i++) { nm2 = (char**)realloc(nm1, sizeof(char*) * (i + 1)); nm1 = nm2; charlength=128; nm1[i] = (char*)malloc(sizeof(char) * (charlength)); strcpy(nm1[i], "abc "); itoa(i, num, 10); strcat(nm1[i], num); } bf = nm1; *cnt = i; return ; } void main() { int cnt; char **bf = NULL; Test1(bf, &cnt); printf("START\n"); printf("全部で%d件。\n", cnt); for(int i=0;i < cnt;i++) { printf("%s\n",bf[i]); } free(bf); printf("END\n"); }

  • 文字列中の空白部の読み込みの方法を教えてくださいm(_ _)m

    いつもお世話になっています!!キーボードから以下のような文字列を読み込み、大文字のみをカウントするプログラムを作りたいのですが、空白部になると次の文字を判定せずにプログラムが停止してしまいます。。。どなたかアドバイスをお願いしますm(_ _)m ○文字列 My name is Marly. ○プログラムソース #include<stdio.h> #include<stdlib.h> #include<string.h> int main(void){ char *s; int i,j=0,m; s=(char*)malloc(sizeof(char)); printf("please iput sentens\n"); scanf("%s",s); m=strlen(s); for(i=0;i<m;i++){ if( 0x41 <= s[i] && s[i] <= 0x5A) j++; } printf("%d",j); free(s); return(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));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • mallocがうまく動かない

    コマンドライン引数で指定された文字列を逆順に返すプログラムを作るため 下記のようなプログラムを組みました。 ところが変数strの大きさがargv[1]より大きくなってしまいます。 どうすればよいのでしょうか。 #include <stdio.h> #include <stdlib.h> char *mstrrev (char *s); int main(int argc, char *argv[]){ char *str; str = mstrrev(*(argv+1)); printf("%s",str); free(str); } char *mstrrev (char *s){ int length,i; char *str; for(length=0;*(s+length)!='\0';length++); str = (char *)malloc(sizeof(char)*length); for(i=0;i<length;i++){ str[i] = s[length-1-i]; } return str; }

  • Cの文字列関連の質問です。よろしくお願いします。

    Cの文字列関連の質問です。よろしくお願いします。 『読み込んだ文字列がナル文字を含めて15文字以下であれば、その文字列をそのまま格納し、そうでない場合、読み込んだ文字列の先頭14文字とナル文字を格納する』 という条件を満たすプログラムを作成しました。 #include <stdlib.h> #include <string.h> #include <stdio.h> int main(void) { int num; char (*p)[15]; printf("文字列の個数: "); scanf("%d", &num); p = (char (*)[15])malloc(num * 15); if (p == NULL) puts("記憶域の確保に失敗"); else { int i; char tmp[100]; /* 書込み */ for (i = 0; i < num; i++) { printf("p[%d]: ", i); scanf("%s", tmp); sprintf(p[i], "%.14s", tmp); } /* 表示 */ for (i = 0; i < num; i++) printf("p[%d]: %s\n", i, p[i]); free(p); } return 0; } この場合は、ちゃんと条件を満たした結果が出ました。 ここで24行目の『sprintf(p[i], "%.14s", tmp);』を『strncpy(p[i], tmp, 14);』に変更する明らかに結果がおかしくなります。 strncpyを使った方法に変更する場合、どのように修正を行えばいいのでしょうか? 長々と書いてしまいましたが、よろしくお願い致します。

  • C言語 動的なメモリの確保 実行できない

    malloc関数を使いメモリを確保しそこへ"ABCD"と記憶させ、ポインタ*Cを使い確保したメモリの内容を表示するプログラムです。 ********************************************* #include <stdio.h> #include <stdlib.h> int main(void) {   int i;   char *C;   C = (char *) malloc (sizeof(char) * 5);   C = "ABCD";   for(i = 0; i < 5; i++){     if(C[i] != NULL){       printf("%s", C[i]);    ←※エラー※     }   }   free(C);   return 0; } ********************************************* 正常にコンパイルできますが実行エラーになります。VCを使いF10のデバッグテストで※のところエラーになります。なぜなのでしょうか?

  • 配列

    こんばんは。 Cを勉強中の者なのですが 前に配列についての内容で自分も分からない事があったので投稿しました。 キーで文字列を入力して関数に渡し、 関数側で引数(文字列データ分)のメモリを確保する事は 出来るのでしょうか。 #include <stdio.h> #include <stdlib.h> void test(char *hiki); int main(void){ char inp[256]; scanf("%s",&inp); printf("%s\n",inp); test(inp); return 0; } void test(char *hiki){ char *i; i = (char*)malloc( ); /* 「hiki」をつかって出来るの でしょうか? */ 全然急ぎとかではなく、ちょっと疑問に思った事なのですが教えていただけるとうれしいです。 (考え方がおかしかったらスミマセン)

  • VC++ 再帰関数

    ■キーボードより読み込んだ文字列の長さを求めるプログラムを再帰関数を使って作る #include <stdlib.h> int unk_r(char*p)   {    if(*p == 0x00)    return(0);   else    return(1+unk_r(p+1));   } int main()   {    char *i;    i = (char *)malloc(20);    scanf("%s",i);    printf("%d\n",unk_r(i));   } このプログラムについて、行、単語ひとつひとつ細かいところまで具体的に解説お願いします。どういう働きをしているのか等。 例えば #include <stdlib.h> はmallocを使うのに必要などなど。 よろしくお願いします。

  • 文字列

    C言語の勉強をしてて、文字列を反転させる以下のようなプログラムを書いてみました。 #include <stdio.h> #include <string.h> void str_reverse(char s[]) { int i,l=strlen(s); char *r; for(i=0;i<l;i++){ r[i]=s[l-i-1]; } for(i=0;i<l;i++) s[i]=r[i]; return; } int main() { char s[100]; scanf("%s",&s); str_reverse(s); printf("%s",s); return 0; } 実行してみると基本的にうまくいくのですが、文字を10文字入力した時だけ 「問題が発生したため、○○.exeを終了します。…」 というエラーウィンドウが出てきてしまいます。 どなか原因と解決法を教えてください。

  • 文字列を関数に渡すぷろぐらむなのですがおかしいです。

    <ソース> #include<stdio.h> #include<stdlib.h> void str(char a[]); int main() { char st[10]="abcde"; str(st); str("ABCabc123"); return 0; } void str(char a[]) { int i; printf("%s\n",a); i=0; while(a[i]){ a[i]=toupper(a[i]); putchar(a[i]); i++; } putchar('\n'); } 分からないところがあるので質問します。 toupperは、大文字にするんですよね。 putcharは、基本的にchar型でしたっけ? putsとgetsは、int型でしたっけ? 後、プログラムが暴走してます。 どこがおかしいんでしょう?