プロが教えるわが家の防犯対策術!

malloc関数(strtok関数の自作版)につきまして分からないことがあります。
以下のプログラムにmallo関数がフリーする最適な位置を明示しなさいといわれました。
文字が分離した時にfreeすると助言されたのですが
いまいち理解できません・・・。
条件式の中で使用するともいわれていました。(おそらくif文・・・。)
色んな意見を参考にしたいので詳しい方助言のほうよろしくおねがいします。

なおプログラムはほかの箇所を変更したり、他の場所でもmalloc関数を使用することが認められています。

またfreeする場所はメイン関数ではなくあくまでもstrtok関数の中で宣言するようです。
よろしくお願いします。



#include <string.h>
#include <stdio.h>
#include <stdlib.h>
main(){
char* s2 = ",/";
char* result;
char* r1;
char* r2;
char* r3;
char* r4;
result = strtok("//123//,45/,678,9/","/,");
r1 = strtok(NULL, s2);
r2 = strtok(NULL, s2);
r3 = strtok(NULL, s2);
r4 = strtok(NULL, s2);
printf("%s\n",result);
printf("%s\n",r1);
printf("%s\n",r2);
printf("%s\n",r3);
printf("%s\n",r4);

return;

}
char *strtok(char *s1, const char *s2)
{
int i,len;
char *str1, *str2 , *str3;
static char *tok ;
static char* mstr;

if(s1 != NULL) {
str1 = s1;
}
else {
str1 = tok;
}

str2 = str1 + strspn(str1, s2); /* strspnを利用 */
if (*str2 == '\0') {
return (NULL);
}

len = 1;
i = 0;
while(*(str2 + i) != '\0'){
len++;
i++;
}

mstr = (char*)malloc(sizeof(char)*len);
if(mstr == (NULL)) {
return 0;
}

i = 0;
while(*(str2 + i) != '\0'){
*(mstr + i) = *(str2 + i );
i++;
}
*(mstr + i ) = '\0';

str3 = mstr + strcspn(mstr, s2); /* strcspnを利用 */

if (*str3 != '\0'){
*str3 = '\0';
str3 = str3 + 1;
}
tok = str3;

return (mstr);
}

A 回答 (7件)

簡単な問題です。



strtok()の中で確実にfreeできます!

ちょっとした「脳トレ」です。
mallocの後半しか見ていないと、
「罠」にはまり、
mainでやるしかないとか思うようになります。

「static」がキーワードです。
staticがあれば、関数を抜けても前の値を保持します。
ポインタであれば、前に代入されたアドレスを保持します。

ですから
strtok()の先頭でfreeすれば良い!!

これが正解!

大丈夫か?

検討します。
まずfree(NULL)は安全である
(何もしない)
事が保証されています。

staticなポインタはNULLで初期化されます。

つまり、最初の呼び出し時のfreeは無いのと同じ。
mallocで代入されます。

return でアドレスを返し関数を抜けますが
staticなので
mstrは割り当てられたアドレスを覚えています。
次回の呼び出しで再度(でたらめな値で)初期化される
ことはありません。
これがstaticの付く/付かないの大きな差になります。

言わば、ここに「大ヒント」があるのです!!!!!!

こうして2回目の呼び出しのfreeでは
前回の呼び出し時に割り当てられたアドレスが
無事freeに渡り、正しく解放されます。

free後はmallocまでmstrに一切アクセスしてませんので
問題ありません。

こうして2回目のmalloc時には既にmstrは解放済みであることが
分かるので再度mstrに割り当てても
「メモリーリーク」にならないことが分かります。

これを何度か繰り返します。
最終の呼び出しを検討する必要があります。
最終のstrtok呼び出しの条件はなにか?

*str2 == '\0’

がその条件です。

与えられたトークンを最後まで切りだし完了した
と言うことです。

この時はmallocせずに抜けています。
つまり、最終の呼び出しでmstrにmallocされて終了する
メモリーリークについて全く心配ない事が分かります。

こうして全てのmallocの前にmstrが有効なアドレスを指しておらず、
最終回の呼び出しではmallocが回避され
全部のmallocされた領域が確実にfreeされることが分かります。
「メモリリーク」はありません。

また、freeしたメモリを再度mallocする前にアクセスする
「アクセス違反」の心配も有りません。

安心して使えるのではないでしょうか?
    • good
    • 0
この回答へのお礼

丁寧なコメントありがとうございます!!
ぜひ参考にさせていただきます!

お礼日時:2010/04/24 16:39

main()の流れの方を見ればすぐにわかりますが、



> result = strtok("//123//,45/,678,9/","/,");
> r1 = strtok(NULL, s2);
> r2 = strtok(NULL, s2);
> r3 = strtok(NULL, s2);
> r4 = strtok(NULL, s2);

result,r1,r2,r3,r4「それぞれに」malloc()で確保しているのでstrtok()内でfree()してしまうとこの流れの終了後にr4以外の領域は解放済みになってしまいます。
つまり、strtok()内で正常動作を保ちつつ解放する手段は「ありません」。
    • good
    • 0

No.5さんの案通りにstrtok()の先頭でmstrをfreeした場合、


質問のmain関数の
printf("%s\n",result);
の実行時点でresultはfreeされていますから
「アクセス違反」となる可能性があります。

それだけでなく、この自作strtok()内のtok変数もmstrと同じ
malloc領域をポイントしていますから、そこがfreeされた後、
str1 = tok;
strspn(str1, s2);
が実行されて「アクセス違反」となる可能性があります。

他の人が言われている通り、mallocで確保した領域を
returnで渡したのなら、呼び出し元のmain関数でfreeするしか
ありません。
    • good
    • 0

「strtok()の自作」と言うからにはstring.hのstrtok()と置き換えてそのまま使えるのが前提だと思いますが、既に言われている通り標準関数のstrtok()は


・最初に与えた第一引数の文字列を破壊しながらトークン分割を行う(ので文字列リテラルを渡せるかどうかは環境依存、不可能な環境が圧倒的に多い)
・上記性質からstatic変数を内部に持っておけばmalloc()を使う必要はない、というか変に使うことで実装をややこしくしているだけ
です。

動作的にも、分割できない場合には本来「残りの全て」を返さなければならないところでNULLを返している(NULLが帰るのは「残りの文字列がない」場合)とか標準関数との互換がなく、free()の位置を考える以前の問題です。

で、このコードでfree()を追加するならmain()の中で、戻り値をfree()します。
free()は引数がNULLなら何もしないので、malloc()で確保済みのアドレスかNULLしか受け取らないこの実装で条件判断が要るかというと……?
    • good
    • 0

最近、あちこちのプログラムQAで同じような質問があるのは学校の課題か何かだろうか?


http://soudan1.biglobe.ne.jp/qa5839814.html
の時も書いたが、ANSI準拠ならmallocは使用しない。
ANSI「風」でmallocを利用するとしても、returnでmallocで確保している領域を返している限り、freeできる個所はない。

それと
>いまいち理解できません・・・。
に対して助言。
まず、「自分で」ソースにコメントをふってみる。
頭だけでは理解できない部分も理解できる可能性がある。
そして、質問する際は「コメントを追記した」ソースを提示し「理解があっているか」も聞く。
    • good
    • 0

そもそもC標準ライブラリのstrtok()関数は、


malloc()で確保した領域を返却する関数ではありません。

第1パラメータに指定された文字列領域を区切り記号で区切られた
文字列に分割する関数であり、malloc()は使わずに実現します。
したがって、free()も不要です。
(参考ソース)
https://research.microsoft.com/en-us/um/redmond/ …

また、第1パラメータには文字列定数は指定できない仕様です。
http://sometime.minidns.net/programming/c/strtok …
の「strtok関数の特徴と注意事項」の解説も参考にしてください。

もし、本来のstrtok()と異なる仕様の関数を作りたいのなら、
関数名を変えて、その関数の仕様を説明して質問した方が
よいと思います。
(他人にアドバイスを求める時に混乱されないように)
    • good
    • 0

この形だと, malloc した領域のアドレスを return で返してるから, strtok の中では free できないのでは

?
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!