• 締切済み

動的なメモリ管理(複数の使用済みのメモリブロックの解放)

氏名をmalloc()した配列に格納後,氏名アドレスと生年月日を組み合わせた構造体をさらにmallocし,そのアドレスをポインタ配列に登録していく。生年月日は,YYYYMMDD形式でキーボード入力されたデータを数値変換してlong型の構造体メンバーに格納する。 下記のプログラムでどこで使用済みのメモリブロックを解放すればよいか教えてください。お願いします。 #include <stdio.h> #include <string.h> #include <stdlib.h> #define DEBUG 0 /* debug mode 0:off 1:on */ #define BUFFERSIZE 11 #define MAX_PERSON 10 #define MAX_CHARS 10 #define BBUFFERSIZE 8 // YYYYMMDD typedef struct { char *name; //氏名 long birth; // 生年月日 YYYYMMDD }PERSON; int main(void){ char str[BUFFERSIZE]; /*氏名一時保存の配列,*/ char *st; //氏名保存配列 PERSON *person[MAX_PERSON]; /*構造体のアドレスを保存する配列*/ PERSON per[MAX_PERSON];//構造体保存配列 char bir[BBUFFERSIZE]; int count; int i; int j; int l; //文字列の長さ int top_index = 0; int bot_index; int day; int mon; int num ; int ch; char *tmp; printf("*** 入力された氏名をソートし、表示します ***\n"); printf("*** 最大入力件数10件(1文字目'0'で入力終了) ***\n"); putchar('\n'); for (i = 0; i < MAX_PERSON ; i++) { printf("氏名入力(10文字まで有効) > "); //氏名をmalloc()した配列に格納 fgets(str, BUFFERSIZE, stdin); l = strlen(str); if (str[l-1] == '\n'){ str[l-1] = '\0'; } else { while ( getchar() != '\n'){ } } if (str[0] == '0'){ break; } st = (char*)malloc(sizeof(char) * (strlen(str)+1)); strcpy(st,str);//\0も含まれてコピー printf("累計 : %d\n", i+1); //氏名アドレスと生年月日を組み合わせた構造体をmalloc() //そのアドレスをポインタ配列に登録 per[i].name = st; //生年月日入力 printf( "生年月日入力(YYYYMMDD) > " ); while(1){ l = 0; while ((ch = getchar()) != '\n' && ch != EOF) { if (l < 8) { bir[l] = ch; } l++; } num = 0; //数値に変換してlong型の構造体のメンバに保存 for ( j = 0; j < 8; j++){ num = num * 10 + (bir[j] - '0'); } printf("num : %d\n",num); per[i].birth = num; break; } person[i] = (PERSON*)malloc(sizeof(per[i])); person[i] = &per[i]; } count = i; printf("データ表示\n"); for (i = 0 ;i < count ; i++ ){ printf("%d : %s\n", i+1, person[i]->name); printf("%08d\n", person[i]->birth); } return 0; }

みんなの回答

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.2

★うーん。『動的なメモリ管理』と同じような質問だね。 ・基本的に malloc で動的確保したデータは必要なくなったときに解放します。  person 構造体を main() 関数の最後にある『データ表示』で使っていますので、  その後に解放する仕組みを記述します。要するに『動的なメモリ管理』の回答と  同じです。return する前に記述します。 ・それで解放するときに person 構造体の name データを先にメモリ解放します。  その後、person 構造体のメモリを解放します。 その他: ・main() 関数が長すぎます。  大きく、データ入力部、メモリ確保部、データ表示部、メモリ解放部の4つに分けて  記述しましょう。分けるときは関数を作って分けます。  データ入力部…data_input()  メモリ確保部…data_malloc()  データ表示部…data_print()  メモリ解放部…data_free()  こんな感じで名前を付けて処理を分けます。  そして main() 関数で呼び出します。    int main(void)  {   /* 宣言部(変数,構造体など) */   PERSON *person[ MAX_PERSON ];   PERSON input;   int n;      /* 10件分のデータ入力部 */   for ( n = MAX_PERSON ; data_input(&input,n) ; n-- ){    /*    data_malloc() 関数を使ってメモリ確保部を記述    */   }   /* データの表示部 */   data_print(); ←この関数1つで表示させる      /* データの解放部 */   data_free( person ); ←この関数1つで解放させる   return( 0 );  } ・上記の main() 関数を参考にサブ関数に分けて記述してみて下さい。  main() 関数はあまり長くしない方が見やすいですよ。  関数の戻り値や引数の並びを考えて作り直すことをお勧めします。  試行錯誤でまずは4つのサブ関数を作って main() 関数で呼び出すように書き換えて下さい。 最後に: ・質問のどこでメモリブロックを解放するかは、確保したデータを利用しなくなったら  そのとき、速やかに解放します。今回のプログラムでは main() しか使われていないため  もし、記述するとすれば main() の最後の return(0) する前に解放ルーチンを記述します。 ・main() 関数の場合はプログラムの終了と同時にメモリが自動的に解放されますから明示的に  free() 関数で解放しなくても良いです。ただ、malloc、free のお勉強と思いますので対で  確保→データ処理、データ表示→解放というステップでデータが不必要になったときに解放  すれば良い。 ・以上。参考に!

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.1

確保した領域にアクセスしないことが確定した時点です。 今回の場合、return 0; の直前でありましょう。 もっとも、free()で明示的に解放しなくても、 プログラムの終了時に自動的に解放する、という話もあります。

関連するQ&A

  • 動的なメモリ管理

    下記のプログラムで、確保したメモリブロックをどこで開放していいかわかりません。教えてください。お願いします。 #include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFFERSIZE 11 #define MAX_PERSON 10 #define MAX_CHARS 10 int main(void){ char *name[10]; //氏名 int count; int i; int l; //文字列の長さ int top_index = 0; int bot_index; char *tmp; printf("*** 入力された氏名をソートし、表示します ***\n"); printf("*** 最大入力件数10件(1文字目'0'で入力終了) ***\n"); putchar('\n'); for (i = 0; i < MAX_PERSON ; i++) { printf("氏名入力(10文字まで有効) > "); name[i] = malloc(sizeof(char) * BUFFERSIZE); fgets( name[i], BUFFERSIZE, stdin); l = strlen(name[i]); if (name[i][l-1] == '\n'){ name[i][l-1] = '\0'; } else { while ( getchar() != '\n'){ } } if (name[i][0] == '0'){ break; } printf("累計 : %d\n", i+1); } count = i; //ソート前 /*printf("ソート前\n"); for (i = 0 ;i < count ; i++ ){ printf("%s\n", name[i]); } */ bot_index = count -1; //シェーカーソート while (1) { int last_swap_index; // 順方向のスキャン last_swap_index = top_index; for ( i = top_index; i < bot_index; i++){ if(strcmp(name[i],name[i+1]) > 0 ){ //ポインタ配列の要素の交換 tmp = name[i]; name[i] = name[i+1]; name[i+1]=tmp; //実体交換 //tmp = (char*)malloc((strlen(name[i])+1)*sizeof(char)); //strcpy(tmp,name[i]); //strcpy(name[i],name[i+1]); //strcpy(name[i+1],tmp); last_swap_index = i; } } //後方のスキャン範囲を狭める bot_index = last_swap_index; if (top_index == bot_index){ break; } // 逆方向のスキャン last_swap_index = bot_index; for ( i = bot_index; i > top_index; i--){ if(strcmp(name[i],name[i-1]) < 0 ){ //実体交換 //tmp = (char*)malloc((strlen(name[i])+1)*sizeof(char)); //strcpy(tmp,name[i]); //strcpy(name[i],name[i-1]); //strcpy(name[i-1],tmp); //要素の交換 tmp = name[i]; name[i] = name[i-1]; name[i-1]=tmp; last_swap_index = i; } } //前方のスキャン範囲を狭める top_index = last_swap_index; if (top_index == bot_index){ break; } } printf("+++++データ表示+++++\n"); for (i = 0 ;i < count ; i++ ){ printf("%2d : %s\n", i+1, name[i]); } return 0; }

  • ポインタ配列の問題で、、。

    ポインタ配列の問題ですが、このmain関数でどこかがおかしいのですが、どこをどう変更すれば正常に動作するのかわかりません。どなたかお願いします(注)string入力文字は9文字以下です。 #include <stdio.h> #define NUM 5 main(void) {     char *str[NUM];     int i;     for(i = 0; i < NUM; i++)     {      printf("string ->"); scanf("%s", str[i]);      }      for(i = 0; i < NUM; i++)      {        printf("str[%d] -> %s\n", i, str[i]);      } }

  • ポインタ配列

    ポインタ配列によるひとつのプログラムを組もうと思っています。 で、以下のようなプログラムを作ってみました。 1:#include<stdio.h> 2:#define NUM 5 3:main(void){ 4: char *str[NUM]; 5: int i; 6: for(i=0;i<NUM;i++){ 7: printf("string --->"); 8: scanf("%s",str[i]); 9: } 10: for(i=0;i<NUM;i++){ 11: printf("str[%d] --> %c\n",i,str[i]); 12: } 13:} これなのですが、8行目のscanf文でコンパイルエラーではなく、実行エラーが出ます。どのようにすれば動くようになるのでしょうか? 入力する文字は、9文字以下を想定しています。

  • 数式を読みこんで答えをかえすプログラム

    C言語で、「キーボードから入力された数式(加法、減法限定)の答えをかえすプログラム」をつくりたいのですが。。。 ※4+35+287-43など #include<stdio.h> #include<string.h> #define NUM 40 int main(void){ char str[NUM]; int i, result, x; printf("Please input an expression:\n"); scanf("%s", str); x = strlen(str); result = 0; for(i=0; i<=x; i++){ if((str[i] == '+') || (str[i] == '-')){ // printf(","); } printf("%c", str[i]); } } printfで+と-を「,」で区切って、それをヒントにそれぞれ配列の中に入れるのかな、というところまでは出来たのですが。。。 解り易い解説をつけて下さるとありがたいです。よろしくお願いします。

  • 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言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問です! ファイルを開いた後でエラーとなるのですが、何が足りないのでしょうか? ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int k,i=0; int num[10]; char na[10][20]; fp=fopen(argv[1],"r"); if(fp==NULL){ printf("ファイルを開けません\n"); return 1; }else{ printf("開けた\n"); } while(fgets(str,sizeof str,fp)!=NULL){ tp=strtok(str," "); num[i]=atoi(tp); tp=strtok(NULL," "); strcpy(na[i],tp); i++; } printf("%d\n%s\n",num[0],na[0]); printf("%d\n%s\n",num[1],na[1]); fclose(fp); return 0; }

  • 構造体

    下記プログラムの2次元配列を構造体の配列に作り変え,構造体を利用して生年月日の項目を追加し,形式は日を除いたYYYY/MMで持ち,生年月日の入出力は,YYYY/MM形式で行い,西暦が数字4桁で,西暦と月の間に'/'があり,月が01~12の範囲の数字の2桁になっている7文字の入力のみ受け付け,正しく入力されるまでそれ以外は再入力させたい。あとdo-while文をつかっている箇所をwhile文に直したいです.自力でやったのですが,わかりませんでした. どのようにしたらよいか教えてください. お願いします. include <stdio.h> #define BUFFERSIZE 11 #define MAX_PERSON 10 #define MAX_CHARS 10 int main(void) { char name[10][BUFFERSIZE]; int c; int count = 0; int i; int j; for (i = 0; i < MAX_PERSON; i++) { printf("氏名入力 : "); j = 0; /* 氏名の1文字目が'0'なら入力を終了 */ if ((c= getchar()) == '0'){ break; } if (c == '\n') { /* 改行のみの入力は再入力 */ do { printf( "再入力\n" ); /*再入力*/ printf("氏名入力 : "); } while ((c = getchar()) == '\n'); } name[i][j++] = c; /*1文字目を格納*/ if (name[i][0] == '0') { break; } while ((c = getchar()) != '\n' && c != EOF) { if (j < BUFFERSIZE - 1) { name[i][j++] = c; } } name[i][j] = '\0'; count++; /* 実際に入力した人数を記録*/ printf("累計 : %d \n", count); } /* 氏名と生年月日を出力したいです */ for (i = 0; i < count; i++) { for (j = 0; j < MAX_CHARS; j++) { if (name[i][j] == '\0'){ break; } putchar(name[i][j]); } putchar('\n'); } return 0; }

  • またまたテストの復習なんですが・・・

    処理内容 基数、および変換対象数値(10進数)を入力させ、それに応じた基数変換を行う。その結果を画面に出力する。 ソースプログラム #include<stdio.h> #define MAX_OUT (16) int InputNum(int, int); char ChangeNum(int); /* メイン処理 */ main(){ int i; /* カウンタ */ int num; /* 対象数値 */ int num_base /* 基数 */ int num_next, num_out; char str[MAX_OUT]; /* 変換後文字格納用 */ while (1){ printf("基数を入力してください(2~32)\n"); printf("(2~32以外の入力で終了)\n"); num_base=InputNum(2,32); if (-1==num_base) { break; } printf("対象の数値を入力してください(0~65535)\n"); printf("(0~65535以外の入力で終了)\n"); num=InputNum(0,65535); if (-1==num) { break; } /* 変換開始 */ ( 1 ) for (i=0; i<MAX_OUT; i++){ /* 変換終了か判定する */ if (num_base>num_next){ /* 変換後文字の格納 */ str[i++]=ChangeNum(num_next); break; } /* 数値文字を抽出 */ num_out=( 2 ) str[i]=ChangeNum(num_out); /* 次の行へ */ num_next=( 3 ) } /* 変換結果を出力 */ printf("対象数値%dの%d進数表現は\n",num, num_base); for (i -= 1; 0<=i; i--) { printf("%c", str[i]); } printf("\nです\n"); } printf("終了します\n"); } /* 数値入力処理 */ int InputNum(int min, int max){ int ret; scanf("%d", &ret); if ((min>ret) || (max<ret)){ ret=-1; } return ret; } /* 数値を文字に変換 */ char ChargeNum(int num){ char ret=0; if (10>num){ ret=num+'0'; } else{ ret=( 4 )+'A'; } return ret; } という問題なんですが、(1)~(4)の答えと説明をどなたかして頂けないでしょうか?どうぞよろしくお願いします!ちなみにwhile(1)は無限ループです。

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

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #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));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • なぜエラーになるのでしょう。

    #include <stdio.h> #include <stdlib.h> #include <string.h> #define STR_MAX 512 #define LINE_MAX 30000 int main(void){ // ※↓がエラー原因※ char str[LINE_MAX][STR_MAX]; printf("%d\n", sizeof(int)); printf("%d\n", sizeof(int[10])); printf("%d\n", sizeof(char[10])); printf("%d\n", sizeof(char[STR_MAX])); printf("%d\n", sizeof(char[LINE_MAX])); printf("%d\n", sizeof(char[LINE_MAX][STR_MAX])); printf("%d\n", sizeof(long[STR_MAX][LINE_MAX])); printf("%d\n", sizeof(str)); return 1; } というプログラムを実行すると「問題が発生したため~を終了します。ご不便をかけて~」という問題が発生して終了してしまいます。 原因は、 printf("%d\n", sizeof(str)); の部分であるのですが、 char str[LINE_MAX][STR_MAX]; という宣言でメモリ使用量が大きすぎるということでしょうか? しかし、 printf("%d\n", sizeof(char[LINE_MAX][STR_MAX])); printf("%d\n", sizeof(long[STR_MAX][LINE_MAX])); でも正常に実行できているのに・・・ もともとは、何万行もあるテキストファイルを配列に一度格納し、それらを編集して出力しようと思っていたのですが、毎回エラーが発生するので、調べたところ配列宣言の部分 char str[LINE_MAX][STR_MAX]; にエラーがあるということが分かったのですが、原因が分かっても理由がさっぱり分かりません。 googleで配列の上限について調べたり、仕様について調べたのですが、ほしい回答が得られず質問させてもらいました。 分かる方いましたら教えてください。 ちなみにExcelのマクロを組んだときにdim StrTemp(512, 30000) as stringと宣言して Option Explicit Function SampleTest() Dim StrTemp(512, 30000) As String Dim i, j As Integer For i = 1 To 512 For j = 1 To 30000 StrTemp(i, j) = "SampleTest" Next j Next i End Function と処理をすることはできたので、パソコンのメモリ容量が足りないとは思えないのです。

専門家に質問してみよう