Respostas:
=====> 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)
O pré-processamento de C é a primeira etapa da compilação. Ele lida com:
#define
afirmações.#include
afirmações.O objetivo da unidade é converter o arquivo de origem C em um arquivo de código C puro.
Existem seis etapas na unidade:
Ele combina caracteres no arquivo de origem, para formar um "TOKEN". Um token é um conjunto de caracteres que não possui 'espaço', 'tab' e 'nova linha'. Portanto, esta unidade de compilação também é chamada de "TOKENIZER". Também remove os comentários, gera a tabela de símbolos e as entradas da tabela de realocação.
Esta unidade verifica a sintaxe no código. Por ex:
{
int a;
int b;
int c;
int d;
d = a + b - c * ;
}
O código acima irá gerar o erro de análise porque a equação não está balanceada. Esta unidade verifica isso internamente, gerando a árvore do analisador da seguinte maneira:
=
/ \
d -
/ \
+ *
/ \ / \
a b c ?
Portanto, esta unidade também é chamada de PARSER.
Esta unidade verifica o significado das declarações. Por ex:
{
int i;
int *p;
p = i;
-----
-----
-----
}
O código acima gera o erro "Atribuição de tipo incompatível".
Esta unidade é independente da CPU, ou seja, existem dois tipos de otimização
Esta unidade otimiza o código nas seguintes formas:
Por ex:
{
int a = 10;
if ( a > 5 ) {
/*
...
*/
} else {
/*
...
*/
}
}
Aqui, o compilador sabe o valor de 'a' em tempo de compilação, portanto, ele também sabe que a condição if é sempre verdadeira. Portanto, ele elimina a outra parte do código.
Por ex:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = a + b + c;
/*
...
*/
}
pode ser otimizado da seguinte forma:
{
int a, b, c;
int x, y;
/*
...
*/
x = a + b;
y = x + c; // a + b is replaced by x
/*
...
*/
}
Por ex:
{
int a;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
a = 10;
/*
...
*/
}
}
No código acima, se 'a' for local e não for usado no loop, ele pode ser otimizado da seguinte forma:
{
int a;
a = 10;
for (i = 0; i < 1000; i++ ) {
/*
...
*/
}
}
Aqui, o compilador gera o código assembly para que as variáveis usadas com mais frequência sejam armazenadas nos registradores.
Aqui, a otimização depende da CPU. Suponha que se houver mais de um salto no código, eles serão convertidos em um como:
-----
jmp:<addr1>
<addr1> jmp:<addr2>
-----
-----
O controle salta para diretamente.
Em seguida, a última fase é Linking (que cria executável ou biblioteca). Quando o executável é executado, as bibliotecas que ele requer são carregadas.
Representação 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]
Espero que isso ajude você um pouco mais.
Primeiro, examine este diagrama:
(img source->internet)
Você cria um trecho de código e salva o arquivo (código-fonte), então
Pré - processamento : - Como o nome sugere, não faz parte da compilação. Eles instruem o compilador a fazer o pré-processamento necessário antes da compilação real. Você pode chamar essa fase de Substituição de Texto ou interpretar as diretivas especiais do pré-processador indicadas por #.
Compilação : - Compilação é um processo no qual um programa escrito em um idioma é traduzido para outro idioma de destino. Se houver alguns erros, o compilador irá detectá-los e relatá-los.
Assemble : - O código Assemble é traduzido em código de máquina. Você pode chamar assembler de um tipo especial de compilador.
Vinculação : - Se esse trecho de código precisar que algum outro arquivo de origem seja vinculado, vincule-o ao linker para torná-lo um arquivo executável.
Muitos processos acontecem depois disso. Sim, você adivinhou, aqui vem o papel do carregador:
Loader : - Carrega o código executável na memória; o programa e a pilha de dados são criados, o registro é inicializado.
Pequenas informações extras: - http://www.geeksforgeeks.org/memory-layout-of-c-program/ , você pode ver o layout da memória ali.
Compilador: é um programa que traduz um programa de linguagem de alto nível em um programa de linguagem de máquina. Um compilador é mais inteligente que um montador. Ele verifica todos os tipos de limites, faixas, erros etc. Mas o tempo de execução do programa é maior e ocupa uma parte maior da memória. Tem velocidade lenta. Porque um compilador passa por todo o programa e então traduz todo o programa em códigos de máquina. Se um compilador é executado em um computador e produz os códigos de máquina para o mesmo computador, ele é conhecido como autocompilador ou compilador residente. Por outro lado, se um compilador é executado em um computador e produz os códigos de máquina para outro computador, ele é conhecido como compilador cruzado.
Linker: Em linguagens de alto nível, alguns arquivos de cabeçalho ou bibliotecas embutidos são armazenados. Essas bibliotecas são predefinidas e contêm funções básicas essenciais para a execução do programa. Essas funções são vinculadas às bibliotecas por um programa chamado Linker. Se o vinculador não encontrar uma biblioteca de uma função, ele informa ao compilador e, em seguida, o compilador gera um erro. O compilador invoca automaticamente o vinculador como a última etapa na compilação de um programa. Não integrado em bibliotecas, ele também vincula as funções definidas pelo usuário às bibliotecas definidas pelo usuário. Normalmente, um programa mais longo é dividido em subprogramas menores chamados módulos. E esses módulos devem ser combinados para executar o programa. O processo de combinação dos módulos é feito pelo vinculador.
Loader: Loader é um programa que carrega códigos de máquina de um programa na memória do sistema. Em computação, um carregador é a parte de um sistema operacional responsável por carregar programas. É uma das etapas essenciais no processo de início de um programa. Porque ele coloca programas na memória e os prepara para execução. Carregar um programa envolve a leitura do conteúdo do arquivo executável na memória. Assim que o carregamento estiver concluído, o sistema operacional inicia o programa, passando o controle para o código do programa carregado. Todos os sistemas operacionais que suportam o carregamento do programa possuem carregadores. Em muitos sistemas operacionais, o carregador é residente permanentemente na memória.
A Wikipedia deve ter uma boa resposta, eis o que penso:
*
*
Linkers and Loaders do LinuxJournal explica esse conceito com clareza. Também explica como surgiu o nome clássico a.out. (saída do montador)
Um breve resumo,
c program --> [compiler] --> objectFile --> [linker] --> executable file (say, a.out)
temos o executável, agora dê este arquivo para seu amigo ou para seu cliente que está precisando deste software :)
quando eles executam este software, digamos, digitando-o na linha de comando ./a.out
execute in command line ./a.out --> [Loader] --> [execve] --> program is loaded in memory
Uma vez que o programa é carregado na memória, o controle é transferido para este programa fazendo com que o PC (contador de programa) aponte para a primeira instrução de a.out
Ele lerá o arquivo de origem que pode ser do tipo .c ou .cpp etc e o traduzirá para um arquivo .o chamado de arquivo objeto.
Ele combina os vários arquivos .o que podem ser gerados para vários arquivos de origem em um arquivo executável (formato ELF no GCC). Existem dois tipos de vinculação:
Um programa que carrega o arquivo executável na memória primária da máquina.
Para um estudo detalhado sobre esses três estágios de execução do programa no Linux, leia isto .
as alterações do compilador verificam se há erros no código-fonte e o transformam em código-objeto. esse é o código que o sistema operacional executa.
Freqüentemente, você não escreve um programa inteiro em um único arquivo, então o linker vincula todos os seus arquivos de código-objeto.
seu programa não será executado a menos que esteja na memória principal
Linker & Interpreter são Interpreter mutuamente exclusivos, obtendo o código linha por linha e executando linha por linha.
Compilador Ele converte o código-fonte em código-objeto.
Linker Ele combina os vários arquivos de objeto em um único arquivo de programa executável.
Carregador Carrega o arquivo executável na memória principal.
Um compilador é um programa especial que processa instruções escritas em uma linguagem de programação específica e as transforma em linguagem de máquina ou "código" que o processador de um computador usa
Um compilador traduz linhas de código da linguagem de programação em linguagem de máquina.
Um Linker cria um link entre dois programas.
Um carregador carrega o programa na memória do banco de dados principal, programa, etc.
Compilador: é um software de sistema que corrige erros de programas, arquivos de objetos, mensagens etc.
Linker: é um software de sistema que combina um ou mais arquivos de objetos e possível algum código de biblioteca em alguma biblioteca executável ou uma lista de erros
Carregador: um programa que carrega o arquivo executável na memória primária da máquina