Domanda

Mentre il debug di un incidente, mi sono imbattuto in questo problema in qualche codice:

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit;

    ...

err_exit:
    free(p2);
    free(p1);

    return -1;
}

Il problema si verifica quando la prima malloc non riesce. Perché noi saltiamo in tutta l'inizializzazione di p2, contiene dati casuali e la chiamata alla free(p2) può mandare in crash.

mi aspetto / speranza che questo sarebbe stato trattato allo stesso modo come in C ++ in cui il compilatore non consente un goto per saltare attraverso un'inizializzazione.

La mia domanda:? È che salta attraverso un'inizializzazione consentita dalla norma o si tratta di un bug nell'implementazione di gcc di C99

È stato utile?

Soluzione

Si può chiedere gcc avvertirvi quando si salta su una definizione di variabile utilizzando -Wjump-misses-init e quindi è possibile utilizzare -Werror (o, più precisamente, -Werror=jump-misses-init) per forzare gli utenti a che fare con esso. Questo avviso è incluso nel -Wc++-compat in modo che gli sviluppatori del GCC sono consapevoli del fatto che il si comporta in modo diverso codice in C rispetto a C ++.

Si potrebbe anche modificare il codice un po ':

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit_1;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit_2;

    ...

err_exit_2:
    free(p2);
err_exit_1:
    free(p1);

    return -1;
}

... e tenere solo l'associazione di etichette con le variabili inizializzate. Avrete lo stesso problema con chiamando molte altre funzioni con variabili non inizializzate, libero sembra appena essere uno più evidente.

Altri suggerimenti

Un salto del genere è infatti consentito dalla norma, quindi questo non è un bug in GCC. Le liste standard di questa situazione come un avvertimento suggerito nell'allegato I.

L'unica limitazione imposta sui salti in C99 per quanto riguarda la portata è che è illegale saltare in ambito di una variabile di tipo variabile modificata, come un VLA

int main() {
  int n = 5;
  goto label; // <- ERROR: illegal jump
  int a[n];
label:;
}

In altre parole, non è corretto dire che "un salto è solo un salto in C". Salti sono alquanto limitate quando si tratta di entrare portata variabile, anche se non strettamente come in C ++. La situazione che descrivi non è uno di quelli ristretti.

Non si tratta di un bug di gcc. Un salto è solo un salto in C. Non v'è alcuna logica speciale applicata. Il problema è che non si inizializza i puntatori a NULL prima. Se si dovesse fare questo allora si chiamata gratuita saresti free(NULL) che non potrebbe andare in crash. Avviare la funzione con char *p1 = NULL, *p2 = NULL; e tutto andrà bene.

Hmm, non è perché il nuovo standard permette di dichiarazioni di variabili da nessuna parte che è sempre una buona idea usarlo. Nel tuo caso vorrei fare come abbiamo fatto in classica C.

int func()
{
char *p1 = NULL;    /* So we have a definite value */
char *p2 = NULL;

  p1 = malloc(...);
  if(!p1)
    goto err_exit;

  p2 = malloc(...);
  if(!p2)
    goto err_exit;

  ...

  err_exit:
    free(p2);
    free(p1);

  return -1;
}

se compilo questo codice con -O2 bandiera

gcc -Wall -std=c99 -O2 jump.c
avvertimento

ho:

jump.c: In function ‘func’:
jump.c:10: warning: ‘p2’ may be used uninitialised in this function

e nessun avvertimento senza ottimizzazione

AndreyT dice , saltando l'inizializzazione è consentito dalla C99. È possibile risolvere la logica utilizzando le etichette separate per i due fallimenti:

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit_p1;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit;

    ...

err_exit:
    free(p2);
err_exit_p1:
    free(p1);

    return -1;
}

Questo è un modello di serie -. "Primi errori" causa un salto ad una parte successiva del codice di uscita di errore

Utilizzando goto non è un'idea intelligente, e hai trovato una ragione per la quale. Si dovrebbe chiamare un errore funzione di gestione per ogni singolo errore.

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