почему освобождение памяти calloc привело к сбою моего проекта VC6? - PullRequest
0 голосов
/ 04 марта 2009

Сравните эти две в значительной степени идентичные функции. Во-первых, память для buff выделяется с помощью _alloca. Это отлично работает. Во втором, calloc и free используются вместо _alloca. Это вылетает.

Странная вещь заключается в том, что я использую технику calloc / free практически во всех остальных функциях обертывания GMP, и все они работают. Здесь они не Есть идеи?

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;
}

Ответы [ 6 ]

1 голос
/ 04 марта 2009

calloc потенциально может вернуть NULL в случае ошибки (например, нехватка памяти). Я бы порекомендовал проверить результат любой функции выделения памяти по NULL. Если значение равно NULL, распечатайте сообщение и выйдите (1).

1 голос
/ 04 марта 2009

Добавьте ведение журнала и сбросьте все по пути, чтобы найти, что идет не так. Обычно это более эффективно, чем пытаться угадать.

1 голос
/ 04 марта 2009

Однажды я провел неделю, пытаясь выяснить похожую вещь. Это был переполнение буфера, которое разбило указатель так свободно, что уходило в лес. Рациональная очистка нашла проблему через минуту.

1 голос
/ 04 марта 2009

Возможно, это не имеет отношения, но этот тип «работает в одну сторону, но не в другую» часто указывает на ошибку, которая просто скрипит в одной ситуации, но приводит к фатальной ошибке в другой.

Если вы подозреваете, что может произойти перезапись памяти, вы можете попробовать выделить дополнительные 8 байтов в буфере и записать 4-байтовые начальные и конечные сторожевые элементы, которые вы затем проверяете перед освобождением.

0 голосов
/ 06 марта 2009

@ Джонни указал на нечто довольно смущающее, что потребовало переписать код. (Вот где пометить комментарий было бы полезно.)

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 может работать.

0 голосов
/ 05 марта 2009

_alloca возвращает стековую память, так что если после ее окончания не обязательно перезаписать что-то важное. Запись после окончания выделения кучи памяти с большей вероятностью перезапишет что-то важное.

Ваш код ничего не делает, чтобы гарантировать, что буфер по крайней мере настолько большой, как res будет отформатирован после деления n1 на n2 (или наоборот, так как я не знаю, что делает настоящая функция); это только гарантирует, что у него достаточно памяти для инициализированного res, который, вероятно, равен 1. Если n1/n2 имеет больше цифр, чем это, добро пожаловать в crashville.

...