Main () è davvero l'inizio di un programma C ++?


131

La sezione $ 3.6.1 / 1 dello standard C ++ recita,

Un programma deve contenere una funzione globale chiamata main , che è l' inizio designato del programma.

Ora considera questo codice,

int square(int i) { return i*i; }
int user_main()
{ 
    for ( int i = 0 ; i < 10 ; ++i )
           std::cout << square(i) << endl;
    return 0;
}
int main_ret= user_main();
int main() 
{
        return main_ret;
}

Questo codice di esempio fa quello che intendo fare, ovvero stampare il quadrato di numeri interi da 0 a 9, prima di entrare nella main()funzione che dovrebbe essere "l'avvio" del programma.

L'ho anche compilato con l' -pedanticopzione, GCC 4.5.0. Non dà alcun errore, nemmeno un avvertimento!

Quindi la mia domanda è

Questo codice è davvero conforme allo standard?

Se è conforme allo standard, non invalida ciò che dice lo standard? main()non è l'inizio di questo programma! user_main()eseguito prima del main().

Capisco che per inizializzare la variabile globale main_ret, use_main()viene eseguita prima ma questa è una cosa completamente diversa; il punto è che, non inficia la dichiarazione citata $ 3.6.1 / 1 dalla standard, come main()non è l' inizio del programma; è in effetti la fine di questo programma!


MODIFICARE:

Come si definisce la parola "inizio"?

Si riduce alla definizione della frase "inizio del programma" . Quindi, come lo definisci esattamente?

Risposte:


85

No, C ++ fa molte cose per "impostare l'ambiente" prima della chiamata di main; tuttavia, main è l'inizio ufficiale della parte "specificata dall'utente" del programma C ++.

Alcune impostazioni dell'ambiente non sono controllabili (come il codice iniziale per impostare std :: cout; tuttavia, alcuni ambienti sono controllabili come blocchi globali statici (per inizializzare variabili globali statiche). controllo prima di main, non hai il controllo completo sull'ordine in cui vengono inizializzati i blocchi statici.

Dopo tutto, il tuo codice è concettualmente "completamente sotto controllo" del programma, nel senso che puoi sia specificare le istruzioni da eseguire sia l'ordine in cui eseguirle. Il multi-threading può riorganizzare l'ordine di esecuzione del codice; ma sei ancora in controllo con C ++ perché hai specificato di avere sezioni di codice da eseguire (possibilmente) fuori servizio.


9
+1 per questo "Nota che poiché non hai il controllo completo prima di main, non hai il controllo completo sull'ordine in cui i blocchi statici vengono inizializzati. Dopo main, il tuo codice è concettualmente" completamente sotto controllo "di il programma, nel senso che è possibile specificare sia le istruzioni da eseguire sia l'ordine in cui eseguirle " . Questo mi porta anche a contrassegnare questa risposta come risposta accettata ... Penso che questi siano punti molto importanti, che giustifica sufficientemente main()come "inizio del programma"
Nawaz,

13
@Nawaz: nota che oltre al controllo totale sull'ordine di inizializzazione, non hai alcun controllo sugli errori di inizializzazione: non puoi rilevare eccezioni in un ambito globale.
André Caron,

@Nawaz: che cosa sono i blocchi globali statici? per favore, puoi spiegarlo usando un semplice esempio? Grazie
Destructor

@meet: gli oggetti dichiarati a livello di spazio dei nomi hanno una staticdurata di archiviazione e, come tali, questi oggetti appartenenti a diverse unità di traduzione possono essere inizializzati in qualsiasi ordine (poiché l'ordine non è specificato dallo standard). Non sono sicuro che questo risponda alla tua domanda, sebbene sia quello che potrei dire nel contesto di questo argomento.
Nawaz,

88

Stai leggendo la frase in modo errato.

Un programma deve contenere una funzione globale chiamata main, che è l'inizio designato del programma.

Lo standard DEFINISCE la parola "start" ai fini del resto dello standard. Non dice che nessun codice viene eseguito prima che mainvenga chiamato. Dice che l'avvio del programma è considerato alla funzione main.

Il tuo programma è conforme. Il tuo programma non è stato "avviato" fino all'avvio di main. Il costruttore viene chiamato prima che il programma "avvii" in base alla definizione di "avvio" nello standard, ma non ha importanza. Un sacco di codice viene eseguito prima che mainvenga mai chiamato in ogni programma, non solo in questo esempio.

Ai fini della discussione, il codice del costruttore viene eseguito prima dell '"avvio" del programma e questo è pienamente conforme allo standard.


3
Ci dispiace, ma non sono d'accordo con la tua interpretazione di tale clausola.
Lightness Races in Orbit,

Penso che Adam Davis abbia ragione, "main" è più simile a una sorta di restrizioni di codifica.
laike9m,

@LightnessRacesinOrbit Non ho mai seguito, ma per me quella frase può essere logicamente ridotta a "una funzione globale chiamata main è l' inizio designato del programma" (enfasi aggiunta). Qual è la tua interpretazione di quella frase?
Adam Davis,

1
@AdamDavis: non ricordo quale fosse la mia preoccupazione. Non riesco a pensare a uno ora.
Razze di leggerezza in orbita,

23

Il tuo programma non si collegherà e quindi non verrà eseguito a meno che non ci sia un main. Tuttavia main () non causa l'avvio dell'esecuzione del programma perché gli oggetti a livello di file hanno costruttori che vengono eseguiti in anticipo e sarebbe possibile scrivere un intero programma che esegua la sua vita prima che venga raggiunto main () e lasciare che main stesso abbia un corpo vuoto.

In realtà per far valere ciò dovresti avere un oggetto che è stato costruito prima di main e il suo costruttore per invocare tutto il flusso del programma.

Guarda questo:

class Foo
{
public:
   Foo();

 // other stuff
};

Foo foo;

int main()
{
}

Il flusso del tuo programma verrebbe effettivamente da Foo::Foo()


13
+1. Tuttavia, se hai più oggetti globali in diverse unità di traduzione, ciò ti metterà rapidamente nei guai poiché l'ordine in cui vengono chiamati i costruttori non è definito. Puoi cavartela con i singoli e l'inizializzazione pigra, ma in un ambiente multithread, le cose diventano molto brutte rapidamente. In una parola, non farlo nel vero codice.
Alexandre C.,

3
Mentre probabilmente dovresti dare a main () un corpo appropriato nel tuo codice e consentirgli di eseguire l'esecuzione, il concetto di oggetti esterni a quello di avvio è su cui si basano molte librerie LD_PRELOAD.
CashCow,

2
@Alex: lo standard dice indefinito, ma come ordine pratico di collegamento (di solito, a seconda del compilatore) controlla l'ordine di inizializzazione.
ThomasMcLeod

1
@Thomas: Sicuramente non proverei nemmeno da remoto a fare affidamento su questo. Inoltre, sicuramente non proverei a controllare manualmente il sistema di compilazione.
Alexandre C.,

1
@Alex: non è più così importante, ma nel passato avremmo usato l'ordine dei link per controllare l'immagine di costruzione in modo da ridurre il paging della memoria fisica. Esistono altri motivi secondari che potresti voler controllare l'ordine di inizializzazione anche quando ciò non influisce sulla semantica del programma, come il test di confronto delle prestazioni di avvio.
ThomasMcLeod

15

Anche tu hai taggato la domanda come "C", quindi, parlando strettamente di C, la tua inizializzazione dovrebbe fallire secondo la sezione 6.7.8 "Inizializzazione" dello standard ISO C99.

Il più rilevante in questo caso sembra essere il vincolo n. 4 che dice:

Tutte le espressioni in un inizializzatore per un oggetto con durata di memorizzazione statica devono essere espressioni costanti o valori letterali di stringa.

Quindi, la risposta alla tua domanda è che il codice non è conforme allo standard C.

Probabilmente vorresti rimuovere il tag "C" se fossi interessato solo allo standard C ++.


4
@ Remo.D potresti dirci cosa c'è in quella sezione. Non tutti noi abbiamo lo standard C :).
UmmaGumma,

2
Dato che sei così esigente: ahimè, ANSI C è obsoleto dal 1989. ISO C90 o C99 sono gli standard pertinenti da citare.
Lundin,

@Lundin: Nessuno è mai abbastanza esigente :) Stavo leggendo ISO C99 ma sono abbastanza sicuro che si applichi anche a C90.
Remo.D

@Un colpo. Hai ragione, ha aggiunto la frase che penso sia più pertinente qui.
Remo.D

3
@Remo: +1 per fornire le informazioni che non sono valide C; non lo sapevo. Ecco come le persone imparano, a volte secondo il piano, a volte per caso!
Nawaz,

10

La sezione 3.6 nel suo insieme è molto chiara sull'interazione maine sulle inizializzazioni dinamiche. L '"avvio designato del programma" non viene utilizzato da nessun'altra parte ed è solo descrittivo dell'intento generale di main(). Non ha senso interpretare quella frase in modo normativo che contraddice i requisiti più dettagliati e chiari della norma.


9

Il compilatore deve spesso aggiungere il codice prima che main () sia conforme allo standard. Perché lo standard specifica che l'inizializzazione di globali / statica deve essere eseguita prima dell'esecuzione del programma. E come detto, lo stesso vale per i costruttori di oggetti posizionati nell'ambito dei file (globali).

Quindi la domanda originale è rilevante anche per C, perché in un programma C avresti ancora a che fare con l'inizializzazione globale / statica prima che il programma possa essere avviato.

Gli standard presumono che queste variabili siano inizializzate tramite "magia", perché non dicono come dovrebbero essere impostate prima dell'inizializzazione del programma. Penso che lo considerassero come qualcosa al di fuori dell'ambito di uno standard del linguaggio di programmazione.

Modifica: vedi ad esempio ISO 9899: 1999 5.1.2:

Tutti gli oggetti con durata di memorizzazione statica devono essere inizializzati (impostati sui valori iniziali) prima dell'avvio del programma. Le modalità e i tempi di tale inizializzazione sono altrimenti non specificati.

La teoria dietro come questa "magia" doveva essere fatta risale alla nascita di C, quando era un linguaggio di programmazione destinato ad essere utilizzato solo per il sistema operativo UNIX, su computer basati su RAM. In teoria, il programma sarebbe in grado di caricare tutti i dati pre-inizializzati dal file eseguibile nella RAM, contemporaneamente al caricamento del programma stesso nella RAM.

Da allora, i computer e il sistema operativo si sono evoluti e il C viene utilizzato in un'area molto più ampia di quanto inizialmente previsto. Un moderno PC OS ha indirizzi virtuali ecc. E tutti i sistemi integrati eseguono il codice dalla ROM, non dalla RAM. Quindi ci sono molte situazioni in cui la RAM non può essere impostata "automagicamente".

Inoltre, lo standard è troppo astratto per sapere qualcosa su stack e memoria di processo, ecc. Anche queste cose devono essere fatte prima dell'avvio del programma.

Pertanto, praticamente ogni programma C / C ++ ha del codice init / "copia-giù" che viene eseguito prima che venga chiamato main, al fine di conformarsi alle regole di inizializzazione degli standard.

Ad esempio, i sistemi incorporati in genere hanno un'opzione chiamata "avvio non conforme ISO" in cui l'intera fase di inizializzazione viene ignorata per motivi di prestazioni, quindi il codice inizia effettivamente direttamente dal principale. Ma tali sistemi non sono conformi agli standard, in quanto non è possibile fare affidamento sui valori di init delle variabili globali / statiche.


4

Il tuo "programma" restituisce semplicemente un valore da una variabile globale. Tutto il resto è codice di inizializzazione. Pertanto, lo standard vale: hai solo un programma molto banale e un'inizializzazione più complessa.



2

Sembra un cavillo di semantica inglese. L'OP si riferisce al suo blocco di codice prima come "codice" e successivamente come "programma". L'utente scrive il codice e quindi il compilatore scrive il programma.


1

main viene chiamato dopo aver inizializzato tutte le variabili globali.

Ciò che lo standard non specifica è l'ordine di inizializzazione di tutte le variabili globali di tutti i moduli e delle librerie collegate staticamente.


0

Sì, principale è il "punto di accesso" di ogni programma C ++, ad eccezione delle estensioni specifiche dell'implementazione. Tuttavia, alcune cose accadono prima dell'inizializzazione principale, in particolare globale, come per main_ret.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.