Risposte:
=====> COMPILATION PROCESS <======
|
|----> Input is Source file(.c)
|
V
+=================+
| |
| C Preprocessor |
| |
+=================+
|
| ---> Pure C file ( comd:cc -E <file.name> )
|
V
+=================+
| |
| Lexical Analyzer|
| |
+-----------------+
| |
| Syntax Analyzer |
| |
+-----------------+
| |
| Semantic Analyze|
| |
+-----------------+
| |
| Pre Optimization|
| |
+-----------------+
| |
| Code generation |
| |
+-----------------+
| |
| Post Optimize |
| |
+=================+
|
|---> Assembly code (comd: cc -S <file.name> )
|
V
+=================+
| |
| Assembler |
| |
+=================+
|
|---> Object file (.obj) (comd: cc -c <file.name>)
|
V
+=================+
| Linker |
| and |
| loader |
+=================+
|
|---> Executable (.Exe/a.out) (com:cc <file.name> )
|
V
Executable file(a.out)
La preelaborazione C è il primo passo nella compilazione. Gestisce:
#define
dichiarazioni.#include
dichiarazioni.Lo scopo dell'unità è convertire il file sorgente C in un file di codice Pure C.
Ci sono sei passaggi nell'unità:
Combina i caratteri nel file sorgente, per formare un "TOKEN". Un token è un insieme di caratteri che non ha "spazio", "tabulazione" e "nuova riga". Pertanto questa unità di compilazione è anche chiamata "TOKENIZER". Inoltre rimuove i commenti, genera la tabella dei simboli e le voci della tabella di riposizionamento.
Questa unità controlla la sintassi nel codice. Ad esempio:
{
int a;
int b;
int c;
int d;
d = a + b - c * ;
}
Il codice precedente genererà l'errore di analisi perché l'equazione non è bilanciata. Questa unità lo controlla internamente generando l'albero del parser come segue:
=
/ \
d -
/ \
+ *
/ \ / \
a b c ?
Pertanto questa unità è anche chiamata PARSER.
Questa unità controlla il significato nelle dichiarazioni. Ad esempio:
{
int i;
int *p;
p = i;
-----
-----
-----
}
Il codice precedente genera l'errore "Assegnazione di tipo incompatibile".
Questa unità è indipendente dalla CPU, ovvero esistono due tipi di ottimizzazione
Questa unità ottimizza il codice nelle seguenti forme:
Ad esempio:
{
int a = 10;
if ( a > 5 ) {
/*
...
*/
} else {
/*
...
*/
}
}
Qui, il compilatore conosce il valore di "a" in fase di compilazione, quindi sa anche che la condizione if è sempre vera. Quindi elimina la parte else nel codice.
Ad esempio:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = a + b + c;
/*
...
*/
}
può essere ottimizzato come segue:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = x + c; // a + b is replaced by x
/*
...
*/
}
Ad esempio:
{
int a;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
a = 10;
/*
...
*/
}
}
Nel codice precedente, se "a" è locale e non viene utilizzato nel ciclo, può essere ottimizzato come segue:
{
int a;
a = 10;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
}
}
Qui, il compilatore genera il codice assembly in modo che le variabili utilizzate più di frequente vengano memorizzate nei registri.
Qui l'ottimizzazione dipende dalla CPU. Supponiamo che se ci sono più salti nel codice, vengono convertiti in uno come:
-----
jmp:<addr1>
<addr1> jmp:<addr2>
-----
-----
Il controllo passa direttamente al file.
Quindi l'ultima fase è il collegamento (che crea eseguibile o libreria). Quando l'eseguibile viene eseguito, le librerie necessarie vengono caricate.
Rappresentazione ASCII:
[Source Code] ---> Compiler ---> [Object code] --*
|
[Source Code] ---> Compiler ---> [Object code] --*--> Linker --> [Executable] ---> Loader
| |
[Source Code] ---> Compiler ---> [Object code] --* |
| |
[Library file]--* V
[Running Executable in Memory]
Spero che questo ti aiuti un po 'di più.
Per prima cosa, segui questo diagramma:
(img source->internet)
Si crea un pezzo di codice e si salva il file (codice sorgente), quindi
Pre -elaborazione: - Come suggerisce il nome, non fa parte della compilazione. Indicano al compilatore di eseguire la pre-elaborazione richiesta prima della compilazione effettiva. È possibile chiamare questa fase Sostituzione del testo o interpretare le direttive speciali del preprocessore indicate con #.
Compilazione : - La compilazione è un processo in cui un programma scritto in una lingua viene tradotto in un'altra lingua mirata. Se sono presenti degli errori, il compilatore li rileverà e li segnalerà.
Assembla : - Il codice di assemblaggio viene tradotto in codice macchina. Puoi chiamare l'assemblatore un tipo speciale di compilatore.
Collegamento : - Se questo pezzo di codice necessita di un altro file sorgente per essere collegato, il linker li collega per renderlo un file eseguibile.
Ci sono molti processi che avvengono dopo di esso. Sì, hai indovinato proprio qui arriva il ruolo del caricatore:
Loader : - Carica il codice eseguibile in memoria; vengono creati il programma e lo stack di dati, il registro viene inizializzato.
Piccole informazioni extra: - http://www.geeksforgeeks.org/memory-layout-of-c-program/ , puoi vedere il layout della memoria laggiù.
Compilatore: è un programma che traduce un programma in linguaggio di alto livello in un programma in linguaggio macchina. Un compilatore è più intelligente di un assemblatore. Controlla tutti i tipi di limiti, intervalli, errori ecc. Ma il tempo di esecuzione del programma è maggiore e occupa una parte maggiore della memoria. Ha una velocità lenta. Perché un compilatore passa attraverso l'intero programma e poi traduce l'intero programma in codici macchina. Se un compilatore viene eseguito su un computer e produce i codici macchina per lo stesso computer, è noto come compilatore autonomo o compilatore residente. D'altra parte, se un compilatore viene eseguito su un computer e produce i codici macchina per un altro computer, è noto come cross compilatore.
Linker: nei linguaggi di alto livello, vengono archiviati alcuni file di intestazione o librerie incorporati. Queste librerie sono predefinite e contengono funzioni di base essenziali per l'esecuzione del programma. Queste funzioni sono collegate alle librerie da un programma chiamato Linker. Se il linker non trova una libreria di una funzione, informa il compilatore e quindi il compilatore genera un errore. Il compilatore richiama automaticamente il linker come ultimo passaggio nella compilazione di un programma. Non integrato nelle librerie, collega anche le funzioni definite dall'utente alle librerie definite dall'utente. Di solito un programma più lungo è diviso in sottoprogrammi più piccoli chiamati moduli. E questi moduli devono essere combinati per eseguire il programma. Il processo di combinazione dei moduli viene eseguito dal linker.
Loader: Loader è un programma che carica i codici macchina di un programma nella memoria di sistema. In Computing, un caricatore è la parte di un sistema operativo che è responsabile del caricamento dei programmi. È una delle fasi essenziali nel processo di avvio di un programma. Perché inserisce i programmi in memoria e li prepara per l'esecuzione. Il caricamento di un programma implica la lettura del contenuto del file eseguibile in memoria. Una volta completato il caricamento, il sistema operativo avvia il programma passando il controllo al codice del programma caricato. Tutti i sistemi operativi che supportano il caricamento del programma dispongono di caricatori. In molti sistemi operativi il caricatore risiede permanentemente in memoria.
Wikipedia dovrebbe avere una buona risposta, ecco i miei pensieri:
*
*
Linker and Loader di LinuxJournal spiega questo concetto con chiarezza. Spiega anche come è nato il nome classico a.out. (output assemblatore)
Un breve riassunto,
c program --> [compiler] --> objectFile --> [linker] --> executable file (say, a.out)
abbiamo l'eseguibile, ora dai questo file al tuo amico o al tuo cliente che ha bisogno di questo software :)
quando eseguono questo software, diciamo digitandolo nella riga di comando ./a.out
execute in command line ./a.out --> [Loader] --> [execve] --> program is loaded in memory
Una volta che il programma è stato caricato nella memoria, il controllo viene trasferito a questo programma facendo puntare il PC (contatore del programma) alla prima istruzione di a.out
Leggerà il file sorgente che può essere di tipo .c o .cpp ecc e lo tradurrà in un file .o chiamato come file oggetto.
Combina i diversi file .o che possono essere generati per più file sorgente in un file eseguibile (formato ELF in GCC). Esistono due tipi di collegamento:
Un programma che carica il file eseguibile nella memoria primaria della macchina.
Per uno studio dettagliato su queste tre fasi di esecuzione del programma in Linux, leggere questo .
le modifiche al compilatore verificano la presenza di errori nel codice sorgente e lo modificano in codice oggetto. questo è il codice eseguito dal sistema operativo.
Spesso non scrivi un intero programma in un singolo file, quindi il linker collega tutti i tuoi file di codice oggetto.
il tuo programma non verrà eseguito a meno che non sia nella memoria principale
Il linker e l'interprete si escludono a vicenda l'interprete che ottiene il codice riga per riga ed esegue riga per riga.
Compilatore Converte il codice sorgente nel codice oggetto.
Linker Combina più file oggetto in un unico file di programma eseguibile.
Loader Carica il file eseguibile nella memoria principale.
Un compilatore è un programma speciale che elabora le istruzioni scritte in un particolare linguaggio di programmazione e le trasforma in linguaggio macchina o "codice" utilizzato dal processore di un computer
Un compilatore traduce righe di codice dal linguaggio di programmazione in linguaggio macchina.
Un linker crea un collegamento tra due programmi.
Un caricatore carica il programma in memoria nel database principale, nel programma, ecc.
Compilatore: è un software di sistema che corregge l'errore di programmi, file oggetto, messaggi ecc
Linker: è un software di sistema che combina uno o più file oggetto ed eventualmente un codice libreria in qualche libreria exicutable o in una lista di errori
Caricatore: un programma che carica il file eseguibile nella memoria principale della macchina