• ベストアンサー

callocについて

ccalloc関数を用いてメモリを確保し、15文字以下の名前をキーボードから入力を10人分繰り返し、その後にすべての名前を出力をしなさい、ただし配列は使用しないものとする。 という問題があるのですが、calloc関数を習ったことがなくうまくいかず、エラーかnullが出力されます。どなたか教えてください、お願いします。 *********ソース********* #include <stdio.h> #include <stdlib.h> void main(void) { int i; char *str; char *buf; /*メモリの確保*/ str = (char *)calloc(110, sizeof(char )); /*メモリが確保出来なかった時*/ if(str == NULL){ printf("メモリの確保ができません。"); exit(1); } /*キーボードからの入力*/ for(i = 0; i < 10 ;i++){ gets(buf); *str = *buf; str++; } /*名前の出力*/ for(i = 0; i < 10 ;i++){ printf("%s", *(str)); str++; } /* メモリの開放 */ free(str); }

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

  • ベストアンサー
  • anmochi
  • ベストアンサー率65% (1332/2045)
回答No.1

とりあえず根本的な問題は中ほどにある gets(buf); *str = *buf; str++; だな。配列不許可という事だが、じゃあどこまでは許容されるんやろね。C言語においては、配列はメモリのオフセットに他ならない(←ちょっと難しい話)ので、解釈によっては難しいな。  さし当たっては配列っぽい表現を使わない方法、つまり、君のソースを部分的に修正する方法を検討してみようか。  まず、「15文字以下を10人分」という事で、メモリは160バイト必要だ。理由は、C言語の文字列は文字列+Null文字の1バイト分余計に必要だからだ。以下、「文字」は全て半角文字を意味する。 str = (char *)calloc(160, sizeof(char )); で、最初に指摘した箇所がなぜいけないかというと、 gets(buf); /* bufを確保してない! 大変危険 */ *str = *buf; /* これでは、入力してもらった最初の1文字目だけをstrのの先頭にコピーするという意味になる */ str++; /* これでは、strが指し示す次の文字に移動する事になる */ さらに、これの最もいけない点は、「最初にcallocで確保してstrで指し示したメモリの場所が途中から分からなくなる」というものだ。 なんか説明しづらいな・・・・ソースをどんと載せようか。 #include <stdio.h> #include <stdlib.h> void main(void) { int i; char *str; /* こいつはずっとcalloc(160)を指し示す */ char *tmp; /* こちらを10人分保存用に使う */ char *buf; /* 入力バッファ */ char *buftmp; /* こっちはコピー用 */ tmp = str = (char *)calloc(160, sizeof(char)); buf = (char *)calloc(16, sizeof(char)); /* ちゃんとbufも確保してあげる */ if(str == NULL || buf == NULL){ printf("メモリの確保ができません。"); exit(1); } for(i = 0; i < 10 ;i++){ gets(buf); buftmp = buf; /* 下2行と合わせて C言語で文字列のコピーは、自前ではこうしないといけない */ while(*buftmp) { *tmp++ = *buftmp++; } *tmp++ = '\0'; } tmp = str; /* 保存されている先頭に戻る */ for(i = 0; i < 10 ;i++){ printf("%s\n", tmp); /* 文字列として表示するために、*tmpではなくtmpを使う */ while(*tmp++); /* 下1行と合わせて 次のデータを読み出すために頭だし */ tmp++; } free(buf); free(str); } これで一応君が望む動きはするはずだ。できればソース掲載以外の方法でアドバイスをしたかった(そのまんま課題の答えになってしまうし、方法論に広がりが無くなる)のだが、ちょっと今時間が足りないので勘弁していただきたい。後はゆっくりそれぞれの行が何の意味でどんな動きをしているのか吟味すべし。特に、「文字と文字列の違い」、「ポインタと++」に注目しよう。

その他の回答 (5)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.6

主だった問題は既に回答が出ているようなので、それ以外について書きます。 > ただし配列は使用しないものとする。 > printf("%s", *(str)); "%s" も立派な「文字配列」です。

  • mac_res
  • ベストアンサー率36% (568/1571)
回答No.5

1. void main()ではなく、int main(void) 2. gets(3)は、バッファオーバーフローの危険があるので使ってはいけない関数です。fgets(3)を使うべき。 3. void *calloc(size_t nmemb, size_t size)は、 size バイトの要素 nmemb 個からなる配列にメモリを割り当てる関数です。この場合は16byteの要素を持つ配列10個を確保するのだから、calloc(10, 16)が本来の使い方。 4. コメントを入れるより、プログラムの可読性をあげるほうが良い。

  • Senna_FF
  • ベストアンサー率45% (153/334)
回答No.4

ANo.2です。 もたもたしてたら、anmochiさんが全て解決してくれてました。 ソース掲載前の事柄をよく理解されるよう・・・ がんばってください。

  • mac_res
  • ベストアンサー率36% (568/1571)
回答No.3

#include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { int i; char *str; char *buf, *p; if ((buf = (char *) calloc(1, BUFSIZ)) == NULL || (str = (char *) calloc(10, 16)) == NULL) { printf("メモリの確保ができません。"); exit(1); } for (i = 0, p = str; i < 10; i++, p += 16) { printf("%2d:", i+1); fgets(buf, BUFSIZ, stdin); buf[strlen(buf) - 1] = '\0'; strncpy(p, buf, 15); } for (i = 0, p = str; i < 10; i++, p += 16) { printf("%s\n", p); } free(buf); free(str); return(0); }

  • Senna_FF
  • ベストアンサー率45% (153/334)
回答No.2

とりあえず、無理やり直してみた。 ポイントだけ整理して、どこがいけないのかよく検討してみてください。 1.アロケートサイズ・・・110の根拠が不明( 15 + 1(ストッパ分)* 10) 2.gets()関数の仕様を見直そう。 3.ポインタシフト・・・・なぜ1Byteずつ? 4. アロケートポインタをシフトして、なぜそのポインタに対してFree()してしまっているの? {   int i;   char *str;   char *work;   char buf[16];     /*メモリの確保*/   str = (char *)calloc(16 * 10, sizeof(char ));     /*メモリが確保出来なかった時*/   if(str == NULL){     printf("メモリの確保ができません。");     exit(1);   }     work = str;   /*キーボードからの入力*/   for(i = 0; i < 10 ;i++){     memset(buf, 0x00, sizeof(buf));     gets(buf);     strncpy(str, buf, strlen(buf));     str += 16;   }     str = work;   /*名前の出力*/   for(i = 0; i < 10 ;i++){     printf("%s\n", str);     str+=16;   }      /* メモリの開放 */   str = work;   free(str);     exit(0); } 上記のものでも、かなりおかしいですよ。 バッファのオーバーフロー等は、演習用の仕様だとしてもアロケータポインタをシフトするのは あまりよろしくないですね。 今回の場合であれば、インデクス(添え字)をしようすればよいのでは・・・・ そのあたりを考慮して、作り直せば勉強になると思います。

関連するQ&A

  • 配列のコピー

    配列bufの内容をstrにコピーしてgetsを使い 表示させたいのですが、うまくいかず 余計な文字まで出力されます、どのようにすれば うまくいくでしょうか? どなたかアドバイスよろしくお願いしますm(_ _)m #include <stdio.h> void main(){ char buf[256]="message"; char str[256]; int i = 0; while(buf[i] != NULL){ str[i] = buf[i]; i++; } printf(str); }

  • 多元配列について(ANSI C)

    動的多元配列で、 Voidポインタに、多種の型がぶら下がった多元配列を作り、 読み書きをしたいのですがどのようにしたらよろしいでしょうか。 具体的には、 x[0][1]は、intで「2」が入っている x[0][4]は、intで「9」が入っている x[1][2]は、charでしかも文字列の配列で「goo」が入っている x[1][5]は、charでしかも文字列の配列で「教えて」が入っている x[0]は、int型の配列。X[1]は、文字列型の配列。 というようなものです。 一応ソースは作ってみたのですが、int型では問題なくいくのですが、 文字列は、コンパイルはできますが、実行すると予期せぬことが起きます。 #include <stdio.h> #include <stdlib.h> #include <string.h> int main (void) {      void **topPointa;      int * iDataInput;      int * iDataOutput ;      char * chDataInput;      char * chDataInput2;      char * chDataOutput1 ;      // ポインタアドレス用のメモリ確保      topPointa = (void *) calloc (10 , sizeof(void *));      if(topPointa == NULL) {           printf("メモリが確保できません\n");           exit(-1);      }      //int配列のメモリ確保      iDataInput = (int * ) calloc (10 , sizeof(int));      if(iDataInput == NULL) {           printf("メモリが確保できません\n");           exit(-1);      }      iDataInput[0] = 3 ;      iDataInput[1] = 4 ;      topPointa[0] = (void * ) &iDataInput;      //int配列の取り出し      iDataOutput = *(int *) topPointa[0];      printf( "int: %d\n", iDataOutput[0] );      printf( "int: %d\n", iDataOutput[1] );      //char配列 のメモリ確保      chDataInput = (char * ) calloc (10 , sizeof(char *));      if(chDataInput == NULL) {           printf("メモリが確保できません\n");           exit(-1);      }      chDataInput2 = (char * ) calloc (10 , sizeof(char));      if(chDataInput2 == NULL) {           printf("メモリが確保できません\n");           exit(-1);       }      strcpy(chDataInput2 , "hoe");      chDataInput[0] = &chDataInput2;      topPointa[1] = (void * ) &chDataInput;      //char配列の取り出し      chDataOutput1 = *(char *) topPointa[1];      printf( "char: %S\n", chDataOutput1[0] );      free(iDataInput);      free(chDataInput);      free(chDataInput2);      return 0; } 言語は、C言語ANCI Cでお願いします。 以上。よろしくお願いします。

  • 領域の開放がされているかどうかはどうやって調べればいいのでしょう。

    #include <stdio.h> #include <stdlib.h> int main(void){ char *str; printf("&str : %d\n", &str); // ●領域割り当て前のアドレス(NULL) printf("str : %d\n", str); str = (char *)calloc(15, sizeof(char)); // (1)領域割り当て後のアドレス printf("str : %d\n", str); // str領域開放 free(str); // (2)領域解放後のアドレス printf("str : %d\n", str); return 1; } 開放した後は●と同じくNULLになると思っていたのですが、 実際に実験してみて(1)と(2)のアドレスが同じものでした。 アドレスは割り当てたままに見えますが、実際にはちゃんと開放されているのでしょうか? それとも、何か間違えているでしょうか。 よろしくお願いします。

  • プログラミング(関数reverseを作る)

    プログラミングの勉強会で文字列を反転させる関数(例:ABCDE→EDCBA)を作れという課題が出たのですが、以下のように書いたきり進みません。あと、何を書けばいいのでしょうか?教えてください。 #include <stdio.h> void reverse(char *); void reverse2(char *); void main(){ char buf[10]; printf("str :"); fgets(buf, 10, stdin); reverse(buf); printf("rev :%s", buf); return; } //文字列を反転させる関数 void reverse(char *s){ return; }

  • 一番大きい奇数を表示する

    scanf関数を使って数字を10回入力して一番大きなものを表示させるプログラムをつくったのですが、 さらに一番大きな奇数を表示するにはどうすればいいのでしょうか? 偶数=割り切れる 奇数=割り切れない というところまでは分かるのですが、以下のプログラムに奇数を判別するソースを追加するのにはどうすればいいのでしょうか。 #include <stdio.h> int main(void) { char str[1024]; char buf[10]; int i; int w; printf("文字列を10回入力して下さい:\n"); memset(str, 0, sizeof(str)); for (i = 0; i < 10; i++) { memset(buf, 0, sizeof(buf)); printf("input>\n"); scanf("%s", buf); } for (i = 0; i < 10; i++) { if ((buf[i] & 1) == 1) /* 奇数であるか */ { if (strcmp(buf, str) > 0) { strcpy(str, buf); } } } printf("output>\n%s\n" , str); getchar(); }

  • switchとメモリ取得位置

    #include <stdlib.h> #include <string.h> #include <ctype.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE -1 char* get(char **p_str); char *get_line(char buf[]); int comp_rtn(const void *p1, const void *p2); typedef struct { int number; char class_type[10]; char name[15]; char subject[10]; int ten; } my; my *data; void myswap(my *p, my *q); int main(int argc, char* argv[]) { FILE *fp; int field = 0, line = 0; char buf[1000], *str; char *bufFormat; char *bufG; bufG = (char *)malloc(1000); if(bufG == NULL){ printf("メモリ不足"); free(bufG); } int line2 = 0; if((fp=fopen("jjj.txt","r"))==NULL){ printf("ファイルが開けません"); } while(fgets(buf, 1000, fp) != NULL){ line2++; } fclose(fp); if((fp=fopen("jjj.txt","r"))==NULL){   printf("ファイルが開けません"); } data = (my *)malloc(sizeof(my) * line2); if(data == NULL){ printf("メモリ不足"); free(data); } while(fgets(buf,1000,fp) != NULL){ bufFormat =(char *)malloc(strlen(buf) + 1); if(bufFormat == NULL){ printf("メモリ不足"); free(data); } bufFormat = get_line(buf); str = bufFormat; while(*str != '\0'){ bufG = get(&str); switch(field){      case 0: data[line].number = atoi(bufG); break case 1: strcpy(data[line].class_type, bufG); break; case 2: strcpy(data[line].name, bufG); break; case 3: strcpy(data[line].subject, bufG); break; case 4: data[line].ten = atoi(bufG); break; } str++; field++; } line++; field = 0; } fclose(fp); qsort(data,line,sizeof(my),comp_rtn); for(int m = 0; m < line; m++){ printf("%d\n", data[m].number); printf("%s\n", data[m].class_type); printf("%s\n", data[m].name); printf("%s\n", data[m].subject); printf("%d\n", data[m].ten); printf("\n"); } free(data); return 0; } void myswap(my *p, my *q) { my temp; temp = *p; *p = *q; *q = temp; } char *get(char **p_str) { int i; char *str; str = *p_str; static char bufG[1000]; for(i = 0; *str != ',' && *str != '\0' ; i++){ if(*str == '\n'){ bufG[i] = '\0'; } else if(*str == '\\'){ str++; if(*str == 'c'){ bufG[i] = ','; } else if(*str == '"'){ bufG[i] = '"'; } } else{ bufG[i] = *str; } str++; } bufG[i] = '\0'; *p_str = str; return bufG; } char *get_line(char buf[]) { int in_quotation = FALSE, i = 0; char* str = buf; static char bufG[1000]; while(*str != '\0'){ if(*str=='"'){ if(in_quotation == TRUE){ str++; if(*str == '"'){ bufG[i] = '\\'; i++; bufG[i] = '"'; i++; } else{ in_quotation = FALSE; bufG[i] = *str; i++; } } else{ in_quotation = TRUE; } } else{ switch(*str){ case '\n': if(in_quotation == TRUE){ bufG[i] = '\\'; i++; bufG[i] = 'n'; i++; } else{ bufG[i] = *str; i++; } break; case ',': if(in_quotation == TRUE){ bufG[i] = '\\'; i++; bufG[i] = 'c'; i++; } else{ bufG[i] = *str; i++; } break; default: bufG[i] = *str; i++; } } str++; } bufG[i] = '\0'; return bufG; } 構造体メンバをポインタで宣言する方法はもうできていて 配列の方も組んでいるのですが 比較関数など所々省いてますがこのプログラムに対し以下のことをいわれました 1,switch文のfieldの値を数字じゃなく分かりやすいのに変えよ 2,mainのすぐしたの bufG = (char * )malloc(1000)が なかなか使用されないのにここでメモリを取るのはおかしい 3,これを使うまでの間にエラーが発生したときのfreeがない と言われました。 1ですが確かCではswitch文のcase式は整数型定数でなければならない とあるので無理な気もするのですが、方法ありますか? 2に関してはよくわかりません。効率がよくないのでしょうか? どの場所がいいのでしょうか 3に関してはどういうことなのかもわかりません。 この3点について教えて下さい。

  • C言語の変換する関数について教えてください。

    キーボードからローマ字で入力された名前の英文字を変換する関数を定義し、その関数の機能を確認するプログラムを作成する問題について教えてください。 (1)英小文字であればそれを英大文字に変換する関数 (2)英大文字であればそれを英小文字に変換する関数 (3)英小文字であればそれを英大文字に、英大文字であればそれを英小文字に変換する関数 ただし、キーボードから入力された名前を格納する配列と、変換後の名前を格納する配列を別にする。 また、名前は関数main()内で表示する #include <ctype.h> #include <stdio.h> void name_toupper(char str[]) { unsigned i = 0; while (str[i]) { str[i] = toupper(str[i]); i++; } } void name_tolower(char str[]) { unsigned i = 0; while (str[i]) { str[i] = tolower(str[i]); i++; } } int main(void) { char str[100]; printf("文字"); scanf("%s", str); name_toupper(str); printf("大文字: %s\n", str); name_tolower(str); printf("小文字: %s\n", str); return 0; } 自分で作った上のプログラムではKa siと入力すると(1)ではKA、(2)ではkaと表示されsiが消えてしまいます。原因がよくわかりません。 あと(3)ができないし、ただしを満たしているのかもあいまいです。 文字列の入力の形式:char *gets(char *buffer)を用いればどうにかなるのではと思っていますがどうですか? 説明が長くなって申し訳ありませんが教えてください。 よろしくお願いします。

  • 一番大きい奇数を表示する

    scanf関数を使用して、文字列を10回入力し一番大きい文字列を表示するプログラムを作ったのですが、 一番大きい「奇数」を表示するように条件を加えた場合どうすればよいのでしょうか? 偶数=割り切れる 奇数=割り切れない ということまでは分かるのですがその先が分かりません。 一応一番大きい文字列を表示するプログラムを貼っておきます。 #include <stdio.h> int main(void) { char str[1024]; char buf[10]; int i; printf("文字列を10回入力して下さい:\n"); memset(str, 0, sizeof(str)); for (i = 0; i < 10; i++) { memset(buf, 0, sizeof(buf)); printf("input>\n"); scanf("%s", buf); } if (strcmp(buf, str) > 0) { strcpy(str, buf); } printf("output>\n%s\n" , str); getchar(); }

  • C言語の使い方を教えてください。-C言語入門者

    C言語のプログラムの処理の対象となるファイルはどこに保存したらよいのでしょうか?たとえば下記のプログラムで #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { char *line[100], *buf; int i = 0, len, lines; FILE *fp; fp = fopen("textfile.txt", "r"); buf = calloc(128, sizeof(char)); while (i < 100 && fgets(buf, 128, fp) != NULL) { len = strlen(buf); line[i] = calloc(len + 1, sizeof(char)); strcpy(line[i], buf); i++; } fclose(fp); lines = ((i <= 100) ? i :100); free(buf); for(i = 0; i < lines; i++) printf("%s", line[i]); for(i = 0; i < lines; i++) free(line[i]); } で [textfile.txt]という名のファイルをマイドキュメントやコンパイラーを保存しているのと同じ領域に保存してコンパイラーを実行しても所要の結果が得られません。テキストにはプログラムの作り方しか書いてありません。

  • ポインタ(続)

    http://okwave.jp/qa5092628.html の続きです。 #include "stdafx.h" #include <ctype.h> #include <string.h> #include <stdlib.h> typedef struct { int number[6]; char *class_type; char *name; char *subject; } my; int main(int argc, char* argv[]) { FILE *fp; int field = 0, line = 0; char buf[1000], *str; char *bufNum,*bufClass,*bufName,*bufSub; int i; if((fp=fopen("test.txt","r"))==NULL){ printf("ファイルが開けません"); } my *data; data = (my *)calloc(112, sizeof(my)); if(!data){ printf("memoryが足りません\n"); } while(fgets(buf,1000,fp) !=NULL){ str=buf; bufNum = (char *)malloc(strlen(buf) + 1); bufClass = (char *)malloc(strlen(buf) +1); bufName= (char *)malloc(strlen(buf) + 1); bufSub = (char *)malloc(strlen(buf) +1); while(*str != '\0'){ if(*str != ','){ for(i = 0; *str != ',' && *str != '\0' ; i++){ if(*str == '\n'){ } else{ switch(field){ case 0: *bufNum = *Str; data[line].number[line] = atoi(bufNum); break; case 1: *bufClass= *str; data[line].class_type = bufClass; break; case 2: *bufName = *str; data[line].name = bufName; break; case3: *bufSub = *str; data[line].subject = bufSub; break; } } str++; } switch(field){ case 0: bufNum[i] = '\0'; break; case 1: bufClass[i] = '\0'; break; case 2: bufName[i] = '\0';break; case 3: bufSub[i] = '\0'; break; } field++; } else{ str++; } } line++; field = 0; } printf("%s", data[0].name); fclose(fp); return 0; } と作ってみましたがまぁこれも上手く動かないんですが・・ 1、構造体をつくる 2、構造体のメモリをとる 3、ファイルをよみこむ 4、ポインタで宣言した構造体のメンバのメモリをとり実体をつくる 5、一行ずつよみこむ 6、カンマごとに格納 7、格納後終端文字を入れる 格納の区別はカンマごとにfieldを+しfieldの値にて行なう lineは行数 8,printfでテスト表示 終わり という一連のプログラムです。 class_typeまでは正常にでるんですが nameからが入っていません。 他にもここが微妙とかいうのがあったら教えて下さい。 個人的にはbufNum~bufSubのメモリの取り方が大きすぎる とは思うんですが他にいい手もなくて・・

専門家に質問してみよう