Confusione sull'inizializzazione dell'array in C

Confusione sull'inizializzazione dell'array in C

TL;DR:Non penso al comportamento di int a[5]={a[2]=1}; è ben definito, almeno in C99.

La parte divertente è che l'unica cosa che ha senso per me è la parte di cui stai chiedendo:a[0] è impostato su 1 perché l'operatore di assegnazione restituisce il valore che è stato assegnato. È tutto il resto che non è chiaro.

Se il codice fosse stato int a[5] = { [2] = 1 } , tutto sarebbe stato facile:questo è un inizializzatore designato che imposta a[2] a 1 e tutto il resto a 0 . Ma con { a[2] = 1 } abbiamo un inizializzatore non designato contenente un'espressione di assegnazione e cadiamo in una tana del coniglio.

Ecco cosa ho trovato finora:

  • a deve essere una variabile locale.

    a[2] = 1 non è un'espressione costante, quindi a deve avere una memorizzazione automatica.

  • a rientra nell'ambito della propria inizializzazione.

    Il dichiarante è a[5] , quindi le variabili rientrano nell'ambito della propria inizializzazione.

  • a è vivo nella propria inizializzazione.

  • C'è un punto di sequenza dopo a[2]=1 .

    Si noti che ad es. in int foo[] = { 1, 2, 3 } il { 1, 2, 3 } part è un elenco di inizializzatori racchiuso tra parentesi graffe, ognuno dei quali ha un punto di sequenza dopo di esso.

  • L'inizializzazione viene eseguita nell'ordine di elenco degli inizializzatori.

  • Tuttavia, le espressioni dell'inizializzatore non vengono necessariamente valutate in ordine.

Tuttavia, ciò lascia ancora alcune domande senza risposta:

  • I punti di sequenza sono rilevanti? La regola di base è:

    a[2] = 1 è un'espressione, ma non l'inizializzazione.

    Ciò è leggermente contraddetto dall'allegato J:

    L'allegato J dice che qualsiasi modifica conta, non solo le modifiche delle espressioni. Ma dato che gli allegati non sono normativi, possiamo probabilmente ignorarlo.

  • Come vengono sequenziate le inizializzazioni degli oggetti secondari rispetto alle espressioni dell'inizializzatore? Tutti gli inizializzatori vengono valutati prima (in un certo ordine), quindi i sottooggetti vengono inizializzati con i risultati (nell'ordine dell'elenco degli inizializzatori)? O possono essere intercalati?

Penso int a[5] = { a[2] = 1 } viene eseguito come segue:

  1. Archiviazione per a viene allocato quando viene inserito il blocco che lo contiene. I contenuti a questo punto sono indeterminati.
  2. Viene eseguito l'(unico) inizializzatore (a[2] = 1 ), seguito da un punto di sequenza. Questo memorizza 1 in a[2] e restituisce 1 .
  3. Quel 1 viene utilizzato per inizializzare a[0] (il primo inizializzatore inizializza il primo sottooggetto).

Ma qui le cose diventano confuse perché gli elementi rimanenti (a[1] , a[2] , a[3] , a[4] ) dovrebbero essere inizializzati su 0 , ma non è chiaro quando:accade prima di a[2] = 1 viene valutato? In tal caso, a[2] = 1 "vincerebbe" e sovrascriverebbe a[2] , ma quell'assegnazione avrebbe un comportamento indefinito perché non esiste un punto di sequenza tra l'inizializzazione zero e l'espressione di assegnazione? I punti di sequenza sono rilevanti (vedi sopra)? O l'inizializzazione zero si verifica dopo che tutti gli inizializzatori sono stati valutati? In tal caso, a[2] dovrebbe essere 0 .

Poiché lo standard C non definisce chiaramente cosa succede qui, credo che il comportamento sia indefinito (per omissione).


Presumibilmente a[2]=1 inizializza a[2] prima, e il risultato dell'espressione viene utilizzato per inizializzare a[0] .

Da N2176 (bozza C17):

Quindi sembrerebbe che emetta 1 0 0 0 0 sarebbe stato anche possibile.

Conclusione:non scrivere inizializzatori che modificano al volo la variabile inizializzata.


Penso che lo standard C11 copra questo comportamento e affermi che il risultato è non specificato , e non credo che C18 abbia apportato modifiche rilevanti in quest'area.

Il linguaggio standard non è facile da analizzare. La sezione pertinente dello standard è §6.7.9 Inizializzazione. La sintassi è documentata come:

Tieni presente che uno dei termini è espressione-assegnazione e da a[2] = 1 è indubbiamente un'espressione di assegnazione, sono consentiti inizializzatori interni per array con durata non statica:

Uno dei paragrafi chiave è:

E un altro paragrafo chiave è:

Sono abbastanza sicuro che il paragrafo §23 indichi che la notazione nella domanda:

int a[5] = { a[2] = 1 };

porta a un comportamento non specificato. L'assegnazione a a[2] è un effetto collaterale e l'ordine di valutazione delle espressioni è in sequenza indeterminata l'una rispetto all'altra. Di conseguenza, non credo che ci sia un modo per fare appello allo standard e affermare che un particolare compilatore lo sta gestendo correttamente o in modo errato.