昨日は忙しくて返信できませんでしたが。
bitter_foxさん・ISLeさん回答ありがとうございました。
bitter_fox さんが書きました:プログラムが終了すると多くのOSはプログラムが確保したリソースを解放するように制御しているので問題にはなりません。
ですが、実行が継続しているプログラムで幾度も解放のミス(メモリリーク)が起きると、非常にまずいことになります。
freeをしておかなくっても終了時には開放されるとは思うという話は先生から聞いた覚えがあります。
でも、開放されない可能性のために必ずしておかなければならないと。
開放のし忘れは、確かにメモリを使い続けるということなのでよくないと思います。
bitter_fox さんが書きました:ちょっとその教本のfreeの仕方は誤解を招きますね。
リンク先ではfreeすらしていないのですが、それはOSを頼っていることが感じられるので変な矛盾はないですが、教本のfreeの仕方は矛盾してしまっています。
あ・・・リンク先ではfreeを使っていませんでした。
教科書のやり方は矛盾してる・・・んですか?
bitter_fox さんが書きました:複数回mallocした場合のアドレスが連続しているという保証は無いですし、確保した領域分しか解放されないのでそれは誤りです。
そう・・・ですか・・・
bitter_fox さんが書きました:その証拠にタスクマネージャを起動して次のプログラムを実行してみてください。
プロセスタブのメモリの使用量が最初のforと二つ目のforとで違うはずです。
コピペして比べてみました。
私が参考にした値は、ガジェットのCPUメーターのランダムアクセスメモリ(RAM)の%と、タスクマネージャーの該当プロセスのメモリのKです。
(ほかにも起動していたりするので、そのプログラムだけの使用量ではないですが。)
↓値を調べたタイミングとそのときの値。
画像の添付の仕方がわからないのでSSではないですが。
[hr]1.実行する前―52% -K
2.画面上に「Input Any Key(1st time)...」と表示されているとき―52% 728K
3.画面上に「Input Any Key(2st time)...」と表示されているとき―55% 728K
4.画面上に「続行するには何かキーを押してください」と表示されているとき―51% 748K
5.終了した後―51% -K
[hr]RAMで言うと、確かにすべて開放したときより、一部しか開放しなかったときのほうが使われているように見えます。
ですが、メモリで言うと変わらない気がするのですが。
2,3の時点でfreeはすでに実行されているはずなので、使ったメモリのうち全部または一部は開放済みってことですよね?
でもbitter_foxさんのようにメモリの値が変化したりはしませんでした。
3のときにメモリの値が上昇していましたが。
私が確認したタイミングが違っていますでしょうか?
また、教科書のサンプルプログラムにおいてfreeを1回しかしていないとRAMやメモリの値はどうなるのか調べてみました。
コード:
/* */
#include <stdio.h>
#include <stdlib.h>
int main(void){
int **a,total=0,i,j,nrows=3,ncolumns=4;
a=(int **)malloc(nrows*sizeof(int *));
for(i=0;i<nrows;i++)a[i]=(int *)malloc(ncolumns*sizeof(int));
a[0][0]=10;a[0][1]=11;a[0][2]=12;a[0][3]=13;
a[1][0]=20;a[1][1]=21;a[1][2]=22;a[1][3]=23;
a[2][0]=30;a[2][1]=31;a[2][2]=32;a[2][3]=33;
for(i=0;i<nrows;i++)
for(j=0;j<ncolumns;j++)total+=a[i][j];
printf("result=%d\n",total);
free(a);
return 0;
}
コード:
/* */
#include <stdio.h>
#include <stdlib.h>
int main(void){
int **a,total=0,i,j,nrows=3,ncolumns=4;
a=(int **)malloc(nrows*sizeof(int *));
a[0]=(int *)malloc(nrows*ncolumns*sizeof(int));
for(i=0;i<nrows;i++)a[i]=a[0]+i*ncolumns;
a[0][0]=10;a[0][1]=11;a[0][2]=12;a[0][3]=13;
a[1][0]=20;a[1][1]=21;a[1][2]=22;a[1][3]=23;
a[2][0]=30;a[2][1]=31;a[2][2]=32;a[2][3]=33;
for(i=0;i<nrows;i++)
for(j=0;j<ncolumns;j++)total+=a[i][j];
printf("result=%d\n",total);
free(a);
return 0;
}
結果はこうなりました。
[hr]6.実行する前―51% -K
7.上のを実行したあと画面上に「続行するには何かキーを押してください」と表示されているとき―51% 748K
8.上を終了した後下を実行する前―51% -K
9.下のを実行したあと画面上に「続行するには何かキーを押してください」と表示されているとき―51% 748K
10.下を終了した後―51% -K
[hr]RAMにもメモリにも特に違いは見られませんでした。
それは、連続したアドレスで作ろうと連続していないアドレスで作ろうと、freeを1回しかしていないからでしょうか?
教科書のサンプルプログラムは、bitter_foxさんのサンプルプログラムと違って配列ではないポインターですし、同じ動作を何度もさせているわけではないですけれど。
ISLe さんが書きました:かなたん さんが書きました:登録できる個数として100用意したのは、100くらいあれば足りるかなぁっておもったからです。
もし100以上持っている人の場合は、まったく想定してませんでした・・・
また、登録する人やゲーム、本体の名前用の要素数も、だいたいこれくらいあれば足りるだろうと私が勝手に思った数です。
100個で足りるかどうかを問うているわけではありません。
101個目を登録しようとしたとき、プログラムが正しく動作するようになっていませんよ、ということです。
はい。
私は勝手に足りるかなぁと思ったので、でももし足りなくなったらを考えていませんでした。
本当は、そういうことも考えに入れておくべきですよね。
コード:
i=0;
k=-1;
printf("あなたの名前は?\n");
scanf("%s",people);
if(strcmp(list[0],"")==0){
strcpy(list[0],people);
k=0;
}
else{
while(strcmp(list[i],"")!=0 || i>99){
if(strcmp(list[i],people)==0){
k=i;
break;
}
}
if(k==-1){
if(i!=100){
strcpy(list[i],people);
}
else{
printf("ごめんなさい。 これ以上の新規登録はできません。\n");
exit(1);
}
}
}
こんな感じですか?
ISLe さんが書きました:
かなたん さんが書きました:やはりmalloc等で動的にメモリを確保するほうがいいみたいで、mallocで作ることにしたんです。
でも、バグが増える原因になってしまうのですか・・・
ポインタのポインタのポインタなんて使わなくて済みますよってことです。
こんな感じです。
コード:
int n = 100;
char (*namedata)[100][100];
char (*harddata)[100][20];
namedata=(char(*)[100][100])calloc(n, sizeof(char[100][100]));
harddata=(char(*)[100][20] )calloc(n, sizeof(char[100][20] ));
/* mallocと対応する分のfree */
free(namedata);
free(harddata);
バグを増やす原因というのは、一回で済むmallocをわざわざ増やしていることです。
そのせいで解放されないメモリブロックが発生しています。
malloc/freeを使うことが原因ではないです。
bitter_foxさんのサンプルプログラムもそうでしたが、配列のポインタも作れるんですね。
決まっているものはすでに配列で宣言してしまって、決まっていないnの部分だけcallocすればいいんですね。
ISLeさんのサンプルプログラムのように書き変えてみても、特にエラーもなく実行することができました。
ところで、(*namedata)[100][100]や、(char(*)[100][100])calloc―のように配列要素にキャストしているのはなぜですか?
また、宣言の時配列変数名ごと()でくくっているのはなぜですか?
私はこのような使い方をしたことがないので・・・