Domanda

Uno dei miei compagni di classe mi ha inviato un codice e mi ha chiesto cosa c'era che non andava.Era qualcosa del genere:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *d_array, number, divisor_count, i, size = 1;
    char answer;

    d_array = (int*) malloc(size * sizeof(int));

    do
    {
        printf("\nEnter a number:  ");
        scanf("%d", &number);
        divisor_count = 0;
        for(i = 2; i < number; i++)
            if (number % i == 0) divisor_count++;
        if(divisor_count == 0)
        {
            realloc(d_array,(size + 1) * sizeof(int));
            d_array[size - 1] = number;
            size++;
        }
        printf("\nIs there another number? y/n ");
        getchar();
        answer = getchar();
    } while (answer == 'y');

    for(i = 0; i < size - 1; i++)
        printf("\n%d", d_array[i]);

    return 0;
} 

Dovrebbe ottenere i numeri dall'utente e mantenere quelli primi e stamparli alla fine.L'output sul mio computer è qualcosa del tipo:

Enter a number:  3
Is there another number? y/n y
Enter a number:  5
Is there another number? y/n y
Enter a number:  8
Is there another number? y/n y
Enter a number:  7
Is there another number? y/n y
Enter a number:  2
Is there another number? y/n n
4072680
5
7
2

C'erano altre cose nel codice ma il problema più grande ovviamente non è l'assegnazione del valore restituito da realloc().Ma la cosa strana è, che è la mia domanda, perché questo codice mostra il primo numero primo sbagliato e gli altri corretti?È possibile che l'indirizzo dell'array dinamico cambi, ma perché il secondo e il resto sono corretti e non il primo?

Modificare: Ok, il motivo per cui l'ho chiesto è stato cercare di capire il comportamento di realloc() in questo codice, se disponi di buone risorse per favore condividi.Quando si rialloca la memoria (quando si libera quella vecchia), realloc() modifica il contenuto della vecchia posizione di memoria?

È stato utile?

Soluzione

Questo ti mostrerà un po' cosa sta succedendo:

#include <stdio.h>
#include <stdlib.h>

void dbg_print_array(unsigned sz, const int * array, const char * label) {
     fprintf(stderr, "{%s:\t%p:\t", label, array);
     while (sz--) {
         fprintf(stderr, " %x ", *array++);
     }
     fprintf(stderr, "}\n");
}

int main()
{
    int *d_array, number, divisor_count, i, size = 1;
    char answer;

    d_array = (int*) malloc(size * sizeof(int));
    dbg_print_array(size, d_array, "Initial");

    do
    {
        printf("\nEnter a number:  ");
        scanf("%d", &number);
        divisor_count = 0;
        for(i = 2; i < number; i++)
            if (number % i == 0) divisor_count++;
        if(divisor_count == 0)
        {
            int * p;
            dbg_print_array(d_array, size, "pre-realloc");
            p = realloc(d_array,(size + 1) * sizeof(int));
            dbg_print_array(d_array, size+1, "post-realloc (d_array)");
            dbg_print_array(p, size+1, "post-realloc (p)");
            d_array[size - 1] = number;
            size++;
        }
        printf("\nIs there another number? y/n ");
        getchar();
        answer = getchar();
    } while (answer == 'y');

    for(i = 0; i < size - 1; i++)
        printf("\n%d", d_array[i]);

    return 0;
} 

Per quanto riguarda il motivo per cui questi dati vengono scritti così presto, è difficile dirlo.È probabile che diverse implementazioni di allocazione dell'heap si comportino in modo molto diverso per questo.Poiché la riallocazione viene eseguita in passaggi così piccoli (l'array cresce di 1 ogni volta), realloc verrà chiamato spesso.

Poiché anche se tu ed io normalmente potremmo pensare allo spazio heap non allocato come inutilizzato, le funzioni di allocazione e liberazione dell'heap memorizzano lì alcuni dati per stare al passo con le cose.Dato che ogni chiamata a realloc legge questi dati e il programma fornito scrive sui dati che realloc probabilmente presuppone siano di proprietà delle routine di allocazione dell'heap, allora potrebbe leggere qualcosa che questo programma ha sovrascritto (cancella ogni volta la fine dello spazio originariamente allocato attraverso il circuito).Dopo aver letto questi dati danneggiati realloc può prendere una decisione in base a ciò che legge, risultando in chissà cosa.A questo punto del programma dovresti considerare ogni comportamento come non definito perché i presupposti di base per il funzionamento normale non sono più validi.

modificare

Esaminando l'output del codice sopra dovresti essere in grado di determinare quando realloc in realtà restituisce un puntatore diverso da quello che è stato passato (la mia ipotesi è di fare spazio per l'ultimo intero letto, nel tuo esempio, perché malloc probabilmente arrotondato a 16 byte per la prima allocazione - anche perché realloc non l'ho fatto abort, probabilmente perché non è mai stato passato il puntatore non valido).

L'adiacente post-riallocazione le istruzioni print avranno indirizzi diversi (il primo numero stampato per loro) quando realloc non ha restituito lo stesso puntatore passato.

Altri suggerimenti

Sempre fare questo:

void* new_ptr = realloc(ptr, new_size);
if(!new_ptr) error("Out of memory");
ptr = new_ptr;

La ragione è che realloc() potrebbe non essere in grado di adattarsi alle dimensioni richieste entro il blocco è già stato allocato. Se ha bisogno di passare a un altro blocco di memoria, sarà copiare i dati dalla memoria precedentemente allocato, liberare il vecchio blocco di memoria, e restituire il nuovo.

Inoltre, se i rendimenti) (realloc NULL, vuol dire che non è riuscito. In tal caso, la memoria a cui punta ptr deve essere liberato a un certo punto, in caso contrario si avrà una perdita di memoria. In altre parole, non fare questo:

ptr = realloc(ptr, new_size);

Se si invoca un comportamento indefinito, allora non c'è modo di spiegare i risultati senza guardare il sistema operativo e le parti interne del compilatore.

Non è una risposta molto soddisfacente lo so. Ma allora, se si potrebbe descrivere la logica dietro di esso non sarebbe davvero essere chiamato un comportamento indefinito!

Il problema è che dopo realloc la memoria puntato da d_array è considerata libera, in modo che il codice scrive in realtà per la memoria libera. Sembra che nel frattempo la memoria viene allocata per qualcosa di diverso (usato da scanf?), Per cui il suo inizio viene sovrascritta. Naturalmente, questo è completamente undefined: qualsiasi parte della memoria liberata può essere sovrascritta in qualsiasi momento

.

Dal momento che ti sembra di essere per lo più curioso di sapere il motivo per cui si ottiene ripetibile (anche se errato) in uscita dal comportamento indefinito, ecco uno scenario che potrebbe tradursi in quello che stai vedendo. Penso che la comprensione dei meccanismi che stanno alla base comportamento indefinito può essere utile - basta tenere a mente che questo è ipotetica, e potrebbe non essere in realtà il motivo per cui si sta vedendo quello che stai vedendo. Per comportamento non definito i risultati che vedete potrebbe cambiare da una compilazione a quello successivo o una corsa a quello successivo (per un caso strano in cui basta cambiare il nome di una variabile in un programma che ha avuto un comportamento indefinito cambiato l'output del programma, vedere < a href = "https://stackoverflow.com/questions/4575697/unexpected-output-from-bubblesort-program-with-msvc-vs-tcc/4577565#4577565"> uscita imprevisto dal programma bubblesort con MSVC vs TCC ).

  1. d_array viene inizializzato con una chiamata al malloc(size * sizeof(int)) prima che il ciclo do / while viene immesso. Il puntatore d_array è mai cambiato dopo questo punto (anche se potrebbe punto non è più necessario memoria allocata, come vedremo più avanti)

  2. la prima volta che un valore viene memorizzato, realloc() si chiama, ma i reperti di libreria che non ha bisogno di cambiare il blocco originariamente dato e restituisce il valore che è stato passato ad esso. Così 3 viene memorizzato all'inizio di questo blocco. Si noti che questo è ancora un bug, dal momento che il programma non può sapere se o non d_array è ancora valido perché hai ignorato il valore restituito da realloc().

  3. Quando si entra in 5, un'altra chiamata a realloc() è fatta. Questa volta, la biblioteca decide che non deve allocare un blocco diverso. Lo fa (dopo aver copiato il contenuto di ciò punti d_array a). Parte di questo riallocazione risultati nella d_array il blocco è che punta a essere stato liberato, e la contabilità della biblioteca sovrascrive il 3 che era in questo blocco con 4072680. Forse un puntatore a qualcosa di cure Biblioteca Informazioni - chi lo sa. La cosa principale è che il blocco ora appartiene alla biblioteca di nuovo, e (non) può fare quello che vuole con esso.

  4. Ora 5 viene scritto d_array + 1 (che non è un'operazione valida, dal momento che i punti di blocco d_array a è stato liberato). Così d_array[0] == 4072680 e d_array[1] == 5.

  5. da questo punto in poi, tutti i valori memorizzati attraverso d_array andare a blocco che è stato liberato, ma per qualsiasi motivo la libreria non si accorge il danneggiamento di heap che sta accadendo. Basta fortuna (sfortuna se si vuole trovare bug). Nulla viene mai scritto a blocchi che realloc() potrebbe essere in realtà riassegnati.

Nota - come ho detto, tutto questo è una possibile spiegazione per il comportamento. I dati reali potrebbero essere diversi, e in realtà non importa. Una volta che ti sei collegato a un'allocazione di memoria che è stato liberato (se leggere o scrivere), tutte le scommesse sono spenti. La regola linea di fondo per comportamento non definito è che tutto va bene.

provare che:

d_array = realloc(d_array,(size + 1) * sizeof(int));

fatto e ha funzionato bene sul mio computer.

Se si utilizza gcc un gcc -Wall -o darebbe l'avvertimento che stavate cercando.

si realloc (), ma non si utilizza la nuova memoria allocata. Semplicemente parlando. Si punta ancora al vecchio (a seconda se realloc() utilizzato lo stesso blocco o spostato in un posto diverso). Se tu fossi "fortunato" e lo stesso blocco è stato utilizzato appena continuare a scrivere, altrimenti si sta scrivendo per il vecchio posto e finire la scrittura in un luogo non si dovrebbe (a causa realloc() free () s il vecchio blocco internamente. realloc() doesn 't cambiare la variabile puntatore, appena riassegnato lo spazio. è necessario modificare l'indirizzo di memoria con il risultato di realloc() restituito. E come i commenti hanno detto. è necessario verificare la presenza di realloc()ation di successo. il nuovo spazio allocato contiene i dati del vecchio tampone (a patto che la nuova memoria allocata è più grande di quello vecchio). il contenuto della vecchia memoria non vengono modificati, ma il vecchio posto viene liberato.

Credo che bisogna usare realloc in questo modo:

d_array = realloc (d_array, (size + 1) * sizeof (int));

e non solo:

realloc (d_array, (size + 1) * sizeof (int));

Un altro problema che vedo è che size = 1 inizialmente, quindi la prima volta che il codice viene eseguito si sta facendo questo:

realloc (d_array, (size + 1) * sizeof (int));

size + 1 = 2 (l'allocazione di memoria per 2 numeri interi, ma è necessario un solo.) La soluzione potrebbe essere a partire dimensione in 0.

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