-7
void str_cpy(char **des,char* src){
   if(*des || *des==src)
     free(*des);
   *des = (char*)malloc(sizeof(char)*(strlen(src)+1));
   if(!*des)
     return ;
   memcpy ( *des, src, (strlen(src)+1)*sizeof(char) );
 }

line2 の free によって memcpy が何もしないのはなぜですか?? malloc の前の free は大丈夫ですか??

4

4 に答える 4

4

いいえ!!!:-)それを使おうとする前にあなたがfree(src)することはOKではありません!(* dest == srcの場合、コードの2行目、つまりfree(* dest)は基本的にfree(src)と同じであり、その時点から適切に割り当てられたメモリを指さないsrcを使用します。ブロック。)

解放されたメモリを使用しようとすると、未定義の動作が発生します。何かが定義されていないときは何でも起こります!freeがsrcの最初のバイトを0に変更したか(この場合strlen(src)== 0)、またはmemcpyがソースメモリが適切に割り当てられていないか、何か他のことが起こっていることを検出した可能性があります。なぜこのような振る舞いをするのかという質問をする価値はありません。ビルド設定(デバッグ/最適化など)を変更したり、別のシステムでプログラムをビルドしたりすると(実際には、使用するコンパイラではなくライブラリであり、ここで違いが生じます)、おそらく異なる結果が得られます。

とにかく、* dest == srcの場合、free、strlen、malloc、および何かをコピーする必要はありません。あなたはただ戻ることができます。

void str_cpy(char **des,char* src){
   if(*des==src) return;
   ...
 }

一般に、関数を使用している人は、内容を変更したり、実際にメモリを解放したりするなど、2番目の引数srcで面白いことを何もしないことを期待します。また、引数の型をconst char*srcに変更します。これにより、srcメモリブロックを解放することはできますが、関数の使用目的が明確になります。

実際、関数のシグネチャには別のより大きな問題があります。* destは、ヒープ内で以前に割り当てられたメモリ(もちろん、* dest!= srcを想定して、free(* dest)がOKである唯一のケース)または以前に割り当てられたスタックメモリ(この場合、free(* dest))を指している可能性があります。 )はOKではありません)が、静的に割り当てられたメモリ(文字列リテラル)を指しているか、割り当てられたメモリ期間を指していない可能性もあります。

標準ライブラリ関数は、このようなシグニチャを使用しないことで(つまり、char **型の引数を使用して)この問題を回避します。

実行していることに最も関連する関数のシグネチャは次のとおりです。

 char * strcpy(char *restrict s1, const char *restrict s2);

 char * strncpy(char *restrict s1, const char *restrict s2, size_t n);

 char * strdup(const char *s1);

詳細については、http://www.manpagez.com/man/3/strncpy/およびhttp://www.manpagez.com/man/3/strncpy/をご覧ください。

これらの関数はいずれも、メモリがどこに割り当てられているかを気にする必要はありません。3つすべてで、引数が有効なポインタである必要があります(静的または動的に割り当てられた(ヒープ/スタック)メモリへ。strdupが実際に行っていることに最も適していると思います(複製された文字列に新しいメモリを割り当てたいようです) .strcpyおよびstrncpyは、新しいメモリを割り当てません(詳細については、上記のドキュメントリンクを参照してください)。

そこに模様が見えますか?strdupは新しく割り当てられたメモリへのポインタを返し(mallocとまったく同じように)、strcpyとstrcpyはchar *引数を取り、メモリを割り当てません。これらの関数はどちらも、関数が抱えている問題、つまり* destが指すメモリの種類を決定する(Cでは実行できない)問題に対処する必要はありません。カーニハンとリッチーがそのように物事を行うことを選んだ理由があります:-)

* destがヒープ内で以前に割り当てられたメモリブロックを指しているか、NULLに等しい必要があることを明示するドキュメントを提供できます。ただし、優れたライブラリ設計者は、ユーザーに責任を負わせるため(ユーザー間違いを犯すため)、そうしません。

ドキュメントに依存すること(つまり、ライブラリ設計者にその責任をユーザーに渡すこと)は危険な道です。問題を完全に回避することが最善です。良いCコードは関数シグネチャから始まります:-)

最後の注意:コードでstrlen()を2回使用しています。パフォーマンス上の理由から、これを1回だけ使用し、その戻り値をローカル変数に格納し、現在strlen()を呼び出している場所でその変数を使用するのが最適です。

于 2012-11-27T13:31:40.287 に答える
3

mallocCでは、、callocまたはによって割り当てられたメモリのみを解放できますrealloc。それ以外のメモリを解放するのはUndefined behaviour

あなたの場合、それが何であるかは明らかではありません*desmalloccallocまたはによって割り当てられているかどうかrealloc

または、環境の呼び出しでこのようにしました

char des[20]= "hello , world \n";

または多分

char * des= "hello . world \n";
于 2012-11-27T13:25:36.637 に答える
1

コメントで指摘されたように、関数にはドキュメントが必要です。入力が正しい場合は、希望どおりに機能します。

これは機能します:

char * str1 = "hello";
char * str2 = malloc(6);   // Of course this is pointless since you'll free it anyway
str_cpy(&str2, str1);

これもそうです:

char * str1 = "hello";
char * str2 = NULL;
str_cpy(&str2, str1);

でもこれは:

char * str1 = "hello";
char * str2;
str_cpy(&str2, str1);

またはこれ:

char * str1 = "hello";
char str2[6];
str_cpy(&str2, str1);

またはこれ:

char * str1 = "hello";
char * str2 = str1;
str_cpy(&str2, str1);

クラッシュして燃えます。コメントで、関数のすべての事前/事後条件に注意する必要があります。

于 2012-11-27T13:35:17.863 に答える
0
void str_cpy(char **des,char* src){
   if(*des || *des==src)
     free(*des);
   *des = (char*)malloc(sizeof(char)*(strlen(src)+1));
   if(!*des)
     return ;
   memcpy ( *des, src, (strlen(src)+1)*sizeof(char) );
 }
  • malloc の前に解放する必要はありません。ガベージ値が含まれている可能性があり、結果としてそのガベージを解放しています (コアダンプ)。
  • ( *des == src ) で des を解放すると、'src' も解放されます。以下では、memcpy で src を使用しています。

正しいアプローチ:

void str_cpy(char **des,char* src){
   *des = (char*)malloc(sizeof(char)*(strlen(src)+1));
   if(!*des)
     return ;
   memcpy ( *des, src, (strlen(src)+1)*sizeof(char) );
 }
于 2012-11-27T13:44:55.007 に答える