calloc'edメモリを解放するとVC6プロジェクトがクラッシュするのはなぜですか?
-
03-07-2019 - |
質問
これら2つのほぼ同一の機能を比較します。最初の例では、 buff
のメモリは_allocaを使用して割り当てられます。これは正常に機能します。 2番目では、_allocaの代わりにcallocとfreeが使用されます。これはクラッシュします。
奇妙なことは、私が持っている他のほぼすべてのGMPラッピング関数でcalloc / freeテクニックを使用し、すべてが機能することです。ここではありません。アイデアはありますか?
1:
#define Z(x) mpz_t (x); mpz_init( (x) );
#define BUFF_SIZE (1024 * 32)
BSTR __stdcall IBIGDIV(BSTR p1, BSTR p2 ) {
USES_CONVERSION;
Z(n1);
Z(n2);
Z(res);
char * buff = (char *) _alloca( mpz_sizeinbase( res, 10 ) + 2 );
LPSTR sNum1 = W2A( p1 );
LPSTR sNum2 = W2A( p2 );
mpz_set_str( n1, sNum1, 10 );
mpz_set_str( n2, sNum2, 10 );
if ( mpz_sgn( n2 ) != 0 ) {
mpz_div( res, n1, n2 );
mpz_get_str(buff, 10, res);
} else {
strcpy( buff, "-0" );
}
BSTR bResult = _com_util::ConvertStringToBSTR( buff );
return bResult;
}
2:
#define Z(x) mpz_t (x); mpz_init( (x) );
#define BUFF_SIZE (1024 * 32)
BSTR __stdcall IBIGDIV(BSTR p1, BSTR p2 ) {
USES_CONVERSION;
Z(n1);
Z(n2);
Z(res);
char * buff = (char *) calloc( mpz_sizeinbase( res, 10 ) + 2, sizeof( char ) );
LPSTR sNum1 = W2A( p1 );
LPSTR sNum2 = W2A( p2 );
mpz_set_str( n1, sNum1, 10 );
mpz_set_str( n2, sNum2, 10 );
if ( mpz_sgn( n2 ) != 0 ) {
mpz_div( res, n1, n2 );
mpz_get_str(buff, 10, res);
} else {
strcpy( buff, "-0" );
}
BSTR bResult = _com_util::ConvertStringToBSTR( buff );
free( buff );
return bResult;
}
解決
ログを追加し、途中ですべてをダンプして、問題を見つけます。これは通常、推測しようとするよりも効率的です。
他のヒント
それは無関係かもしれませんが、このタイプの「一方向では機能しますが、他の方法では機能しません」。多くの場合、ある状況ではきしみ音がするが、別の状況では致命的なエラーを引き起こすバグを示しています。
メモリの上書きが発生している疑いがある場合は、バッファに余分な8バイトを割り当て、4バイトの開始センチネルと終了センチネルを書き込んでから、解放する前にチェックすることができます。
私はかつて同様のことを理解しようとして1週間過ごしました。ポインターを破壊したのはバッファオーバーランだったので、森の中に自由が消えていきました。 Rational purifyは1分で問題を発見しました。
callocは、エラー(メモリ不足など)がある場合にNULLを返す可能性があります。 NULLに対してメモリ割り当て関数の結果を確認することをお勧めします。 NULLの場合、メッセージを出力してからexit(1)します。
_alloca
はスタックメモリを返すので、その終わりを超えて踏みつけても、必ずしも重要な何かが上書きされるとは限りません。ヒープメモリ割り当ての終わりを超えて書き込むと、重要なものが上書きされる可能性が高くなります。
n1をn2で割った後(またはその逆)、実際の機能がわからないので、バッファは少なくとも res
がフォーマットされるのと同じ大きさになるようにコードは何もしませんする);初期化された res
(おそらく1)に十分なメモリがあることを確認するだけです。 n1 / n2
がそれ以上の数字を持っている場合、crashvilleへようこそ。
@johnnyは、かなり恥ずかしいことを指摘し、コードの書き直しが必要になりました。 (ここでコメントをチェックできると便利です。)
BSTR __stdcall IBIGDIV(BSTR p1, BSTR p2 ) {
USES_CONVERSION;
Z(n1);
Z(n2);
Z(res);
char * buff;
LPSTR sNum1 = W2A( p1 );
LPSTR sNum2 = W2A( p2 );
mpz_set_str( n1, sNum1, 10 );
mpz_set_str( n2, sNum2, 10 );
if ( mpz_sgn( n2 ) != 0 ) {
mpz_div( res, n1, n2 );
buff = (char *) calloc( mpz_sizeinbase( res, 10 ) + 2, sizeof( char ) );
mpz_get_str(buff, 10, res);
} else {
buff = (char *) calloc( 3, sizeof( char ) );
strcpy( buff, "-0" );
}
BSTR bResult = _com_util::ConvertStringToBSTR( buff );
free( buff );
return bResult;
}
以前の具体化では、メモリは、コード内のゼロの位置で res
の値に従って割り当てられていました。したがって、私はゼロバイトをカロッコにしようとしていたが、フリーはそれを好きではなかった。上記のコードでは、 res
には実際に mpz_sizeinbase
で使用できるものが含まれています。