Che cosa è esattamente un’unità di traduzione in C

La definizione comunemente usata di un’unità di traduzione è ciò che viene dopo la pre-elaborazione (inclusioni di file di intestazione, macro, ecc. Insieme al file sorgente). Questa definizione è abbastanza chiara e lo standard C, 5.1.1.1, C11, dice:

Non è necessario che tutti i programmi AC vengano tradotti contemporaneamente. Il testo del programma è conservato in unità chiamate file sorgenti, (o file di pre-elaborazione) in questo Standard Internazionale. Un file sorgente insieme a tutte le intestazioni e i file sorgente inclusi tramite la direttiva di pre-elaborazione #include è noto come unità di traduzione preprocessing. Dopo la pre-elaborazione, un’unità di traduzione di pre-elaborazione è chiamata unità di traduzione.

Leggendo la prima frase più da vicino:

Non è necessario che tutti i programmi AC vengano tradotti contemporaneamente.

il che implica (per la mia lettura), un programma C può essere tradotto allo stesso senza necessariamente suddividerli in più file sorgente di pre-elaborazione. Anche alla fine dello stesso paragrafo, lo standard dice:

Le unità di traduzione possono essere tradotte separatamente e successivamente collegate per produrre un programma eseguibile.

che può essere (e in genere lo è) interpretato come compilazione di singoli file object e infine collegarli per produrre un singolo programma eseguibile. Tuttavia, se si può fare una domanda fuori dall’affermazione precedente e chiedere: vuol dire che un’implementazione è libera di considerare più file sorgente come una singola unità di traduzione, specialmente per un’invocazione come:

 gcc file1.c file2.c -o out 

dove il compilatore ha accesso all’intera fonte?

In particolare, se un’implementazione tratta file1.c + file2.c (sopra) come singola unità di traduzione, può essere considerata non conforms?

Nella seconda riga hai citato:

Il testo del programma è conservato in unità chiamate file sorgenti, (o file di pre-elaborazione) in questo Standard Internazionale

Se ci sono due file sorgente, ci sono due file di pre-elaborazione e quindi due unità di traduzione di pre-elaborazione e quindi due unità di traduzione. Uno corrispondente a ciascun file sorgente.

Lo standard non definisce il file sorgente . Immagino che il compilatore potrebbe dire “Sto inventando la mia versione di ‘file sorgente’ dichiarando che file1.c e file2.c non sono file sorgente dopo tutto!” e concatenarli, ma ciò sarebbe in contrasto con le aspettative dei programmatori. Penso che sarebbe difficile sostenere che file1.c non sia un file sorgente.

Tuttavia, se si può fare una domanda fuori dalla dichiarazione di cui sopra e chiedere: vuol dire che un’implementazione è libera di considerare più file sorgente come una singola unità di traduzione

No. La definizione è chiara:

Un file sorgente insieme a tutte le intestazioni e i file sorgente inclusi tramite la direttiva di pre-elaborazione #include è noto come unità di traduzione preprocessing. Dopo la pre-elaborazione, un’unità di traduzione di pre-elaborazione è chiamata unità di traduzione.

Un’unità di traduzione è il risultato della pre-elaborazione di un file sorgente e dei suoi include. Il fatto che tu possa tradurre due unità di traduzione contemporaneamente non significa che tu possa trattarle come un’unica unità di traduzione.

I compilatori sono liberi di tradurre diversi file sorgente allo stesso tempo, ma non possono cambiare la loro semantica.

La traduzione di più file insieme sarà probabilmente un po ‘più veloce (poiché il compilatore inizia solo una volta) e consentirà una migliore ottimizzazione dell’intero programma: il codice sorgente delle funzioni chiamate in altre unità di traduzione è quindi disponibile nel punto di chiamata da altre unità di traduzione. Il compilatore può ispezionare il codice chiamato e utilizzare le informazioni, per quanto ansible con una singola unità di traduzione. Dal manuale gcc 6.3.0:

Il compilatore esegue l’ottimizzazione in base alla conoscenza che ha del programma. La compilazione di più file contemporaneamente in un’unica modalità file di output consente al compilatore di utilizzare le informazioni acquisite da tutti i file durante la compilazione di ciascuno di essi.

Le funzioni chiamate possono essere ispezionate per assenza di aliasing, costanza fattuale di oggetti appuntiti, ecc., Consentendo al compilatore di eseguire ottimizzazioni che sarebbero errate nel caso generale.

E, naturalmente, tali funzioni possono essere sottolineate.

Ma ci sono semantiche di unità di traduzione (preprocessing) (che corrispondono ai file sorgente dopo la pre-elaborazione, secondo la tua citazione standard) che il compilatore deve rispettare. @Malcolm ne ha menzionato una, variabili file-statiche. Il mio istinto è che potrebbero esserci altri, più sottili problemi riguardanti le dichiarazioni e l’ordine di dichiarazione.

Un altro problema ovvio relativo al problema dell’ambito del codice sorgente definisce. Dalla bozza n1570, 6.10.3.5:

La definizione di una macro dura (indipendentemente dalla struttura a blocchi) fino a quando non si incontra una direttiva #undef corrispondente o (se non si incontra nessuno) fino alla fine dell’unità di traduzione di preprocessing.

Entrambe le questioni vietano la semplice concatenazione di file sorgente C; il compilatore deve inoltre applicare una logica rudimentale.

Un’unità di traduzione indica un file C punto. A tutti gli effetti, incluso il punto h associato. Raramente le direttive #include vengono utilizzate per aggiungere altri tipi di file o altri file dot C.

le variabili statiche sono visibili solo all’interno dell’unità di traduzione. È molto comune disporre di alcune funzioni pubbliche con collegamento esterno e molte funzioni statiche e elementi di dati t supportati. Quindi un’unità di traduzione C è un po ‘come una class C ++ singleton. Se il compilatore non gestisce correttamente la statica, non è conforms.

Generalmente viene creato un file object per ogni unità di traduzione, che viene quindi collegato dal linker. Questo non è in realtà richiesto dallo standard, ma è il modo naturale e ovvio di fare le cose in un ambiente in cui i file sono poco costosi da creare e la compilazione è relativamente lenta.