perché la liberazione della memoria calloced potrebbe causare l'arresto anomalo del mio progetto VC6?

StackOverflow https://stackoverflow.com/questions/609279

Domanda

Confronta queste due funzioni sostanzialmente identiche. Nel primo, la memoria per buff è allocata usando _alloca. Funziona benissimo. Nel secondo, calloc e free sono usati al posto di _alloca. Questo si blocca.

La cosa strana è che uso la tecnica calloc / free in quasi tutte le altre funzioni di wrapping GMP che ho e funzionano tutte. Qui non lo fanno. Qualche idea?

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;
}
È stato utile?

Soluzione

Aggiungi la registrazione e scarica tutto lungo la strada per trovare ciò che non va. Questo di solito è più efficiente del tentativo di indovinare.

Altri suggerimenti

Potrebbe non essere correlato, ma questo tipo di "funziona in un modo ma non nell'altro" spesso indica un errore che si verifica solo in una situazione ma provoca un errore fatale in un'altra.

Se si sospetta che si verifichi una sovrascrittura della memoria, è possibile provare ad allocare altri 8 byte nel buffer e scrivere sentinel di inizio e fine a 4 byte che si verificano quindi prima di liberare.

Una volta ho trascorso una settimana cercando di capire una cosa simile. Fu un sovraccarico di buffer che spazzò via il puntatore così libero che andava nel bosco. Rational purify ha riscontrato il problema in un minuto.

calloc potrebbe potenzialmente restituire NULL in caso di errore (come mancanza di memoria). Consiglierei di controllare il risultato di qualsiasi funzione di allocazione della memoria rispetto a NULL. Se è NULL, stampa un messaggio e quindi esci (1).

_alloca restituisce la memoria dello stack, quindi camminare oltre la fine potrebbe non sovrascrivere necessariamente qualcosa di importante. Scrivere oltre la fine di un'allocazione di memoria heap probabilmente sovrascriverà qualcosa di importante.

Il tuo codice non fa nulla per garantire che il buffer sia grande almeno quanto res sarebbe formattato dopo aver diviso n1 per n2 (o viceversa, poiché non so quale sia la funzione effettiva fa); garantisce solo che abbia memoria sufficiente per un res inizializzato, che è probabilmente 1. Se n1 / n2 ha più cifre di quelle, benvenuto su crashville.

@johnny ha sottolineato qualcosa di piuttosto imbarazzante, che ha reso necessaria una riscrittura del codice. (Ecco dove sarebbe utile spuntare un commento.)

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

Nelle precedenti incarnazioni, la memoria veniva allocata in base al valore di res nel punto in cui conteneva zero. Quindi stavo cercando di calmare zero byte e liberare non mi piaceva. Nel codice sopra, res contiene effettivamente qualcosa con cui mpz_sizeinbase può funzionare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top