TL; DR: Non credo che il comportamento di int a[5]={a[2]=1};
sia ben definito, almeno in C99.
La parte divertente è che l'unico bit che ha senso per me è la parte su 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: è un'impostazione di inizializzazione designata a[2]
per 1
e tutto il resto per 0
. Ma con { a[2] = 1 }
abbiamo un inizializzatore non designato contenente un'espressione di assegnazione, e cadiamo in una tana di coniglio.
Ecco cosa ho trovato finora:
a
deve essere una variabile locale.
6.7.8 Inizializzazione
- Tutte le espressioni in un inizializzatore per un oggetto che ha una durata di memorizzazione statica devono essere espressioni costanti o stringhe letterali.
a[2] = 1
non è un'espressione costante, quindi a
deve avere la memorizzazione automatica.
a
è nell'ambito della propria inizializzazione.
6.2.1 Scopo degli identificatori
- I tag di struttura, unione ed enumerazione hanno un ambito che inizia subito dopo la comparsa del tag in uno specificatore di tipo che dichiara il tag. Ogni costante di enumerazione ha un ambito che inizia subito dopo la comparsa del relativo enumeratore di definizione in un elenco di enumeratori. Qualsiasi altro identificatore ha uno scopo che inizia subito dopo il completamento del suo dichiaratore.
Il dichiaratore è a[5]
, quindi le variabili rientrano nell'ambito della propria inizializzazione.
a
è vivo nella propria inizializzazione.
6.2.4 Durate di conservazione degli oggetti
Un oggetto cui identificatore è dichiarata in alcun legame e senza l'identificatore della classe di archiviazione static
ha durata di memorizzazione automatica .
Per un tale oggetto che non ha un tipo di array di lunghezza variabile, la sua durata si estende dall'ingresso nel blocco a cui è associato fino a quando l'esecuzione di quel blocco non termina in alcun modo. (L'immissione di un blocco racchiuso o la chiamata di una funzione sospende, ma non termina, l'esecuzione del blocco corrente.) Se il blocco viene inserito ricorsivamente, viene creata ogni volta una nuova istanza dell'oggetto. Il valore iniziale dell'oggetto è indeterminato. Se per l'oggetto è specificata un'inizializzazione, essa viene eseguita ogni volta che si raggiunge la dichiarazione nell'esecuzione del blocco; in caso contrario, il valore diventa indeterminato ogni volta che viene raggiunta la dichiarazione.
C'è un punto di sequenza dopo a[2]=1
.
6.8 Dichiarazioni e blocchi
- Una piena espressione è un'espressione che non fa parte di un'altra espressione o di un declarator. Ciascuno dei seguenti è un'espressione completa: un inizializzatore ; l'espressione in una dichiarazione di espressione; l'espressione di controllo di un'istruzione di selezione (
if
o switch
); l'espressione di controllo di una dichiarazione while
o do
; ciascuna delle espressioni (facoltative) di una for
dichiarazione; l'espressione (facoltativa) in return
un'istruzione. La fine di un'espressione completa è un punto della sequenza.
Si noti che per esempio nel int foo[] = { 1, 2, 3 }
la { 1, 2, 3 }
parte è una lista brace-chiuso di initializers, ciascuno dei quali ha un punto sequenza dopo.
L'inizializzazione viene eseguita nell'ordine dell'elenco degli inizializzatori.
6.7.8 Inizializzazione
- Ogni elenco di inizializzatori racchiuso tra parentesi graffe ha un oggetto corrente associato . Quando non sono presenti designazioni, i suboggetti dell'oggetto corrente vengono inizializzati in ordine in base al tipo di oggetto corrente: elementi della matrice in ordine crescente di pedici, membri della struttura in ordine di dichiarazione e il primo membro denominato di un'unione. [...]
- L'inizializzazione deve avvenire nell'ordine dell'elenco degli inizializzatori, ogni inizializzatore fornito per un particolare oggetto secondario sovrascrive qualsiasi inizializzatore elencato in precedenza per lo stesso oggetto secondario; tutti i suboggetti che non sono inizializzati esplicitamente devono essere inizializzati implicitamente come gli oggetti che hanno una durata di memorizzazione statica.
Tuttavia, le espressioni dell'inizializzatore non vengono necessariamente valutate in ordine.
6.7.8 Inizializzazione
- L'ordine in cui si verificano gli effetti collaterali tra le espressioni dell'elenco di inizializzazione non è specificato.
Tuttavia, ciò lascia ancora alcune domande senza risposta:
I punti della sequenza sono anche rilevanti? La regola di base è:
6.5 Espressioni
- Tra il punto della sequenza precedente e quello successivo un oggetto deve avere il suo valore memorizzato modificato al massimo una volta dalla valutazione di un'espressione . Inoltre, il valore precedente deve essere letto solo per determinare il valore da memorizzare.
a[2] = 1
è un'espressione, ma non lo è l'inizializzazione.
Ciò è leggermente contraddetto dall'allegato J:
J.2 Comportamento indefinito
- Tra due punti di sequenza, un oggetto viene modificato più di una volta, oppure viene modificato e il valore precedente viene letto diversamente che per determinare il valore da memorizzare (6.5).
L'allegato J dice che ogni modifica conta, non solo le modifiche tramite espressioni. Ma dato che gli allegati non sono normativi, possiamo probabilmente ignorarlo.
Come vengono sequenziate le inizializzazioni dei suboggetti rispetto alle espressioni di inizializzazione? Tutti gli inizializzatori vengono valutati per primi (in un certo ordine), quindi i suboggetti vengono inizializzati con i risultati (nell'ordine dell'elenco degli inizializzatori)? O possono essere interfogliati?
Penso int a[5] = { a[2] = 1 }
sia eseguito come segue:
- La memoria per
a
viene allocata quando viene inserito il blocco contenitore. I contenuti sono indeterminati a questo punto.
- L '(unico) inizializzatore viene eseguito (
a[2] = 1
), seguito da un punto di sequenza. Questo memorizza 1
in a[2]
e ritorna 1
.
- Che
1
viene utilizzato per inizializzare a[0]
(la prima inizializzazione inizializza il primo sotto-oggetto).
Ma qui le cose si fanno sfumata perché gli elementi rimanenti ( a[1]
, a[2]
, a[3]
, a[4]
) dovrebbero essere inizializzato a 0
, ma non è chiaro quando: lo fa accadere prima a[2] = 1
viene valutata? 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 dell'assegnazione? I punti della sequenza sono anche rilevanti (vedi sopra)? O si verifica zero inizializzazione dopo che tutti gli inizializzatori sono stati valutati? Se è così, a[2]
dovrebbe finire per essere 0
.
Poiché lo standard C non definisce chiaramente cosa succede qui, credo che il comportamento non sia definito (per omissione).
a[2]=1
restituisce1
.