Eu sei que existem duas assinaturas diferentes para escrever o método principal -
int main()
{
//Code
}
ou para lidar com o argumento da linha de comando, nós o escrevemos como-
int main(int argc, char * argv[])
{
//code
}
Em C++
Eu sei que podemos sobrecarregar um método, mas em C
como o compilador lidar com essas duas assinaturas diferentes de main
função?
main
método em um único programa emC
(ou, na verdade, em praticamente qualquer linguagem com tal construção).main
- eu recomendo o livro clássico de John R. Levines "Linkers & Loaders".int main(void)
, nãoint main()
(embora eu nunca tenha visto um compilador que rejeite aint main()
forma).()
formulário é obsoleto e não está claro se é mesmo permitidomain
(a menos que a implementação o documente especificamente como um formulário permitido). O padrão C (consulte 5.1.2.2.1 Inicialização do programa) não menciona o()
formulário, que não é exatamente equivalente ao()
formulário. Os detalhes são muito longos para este comentário.Respostas:
Algumas das características da linguagem C começaram como hacks que simplesmente funcionaram.
Múltiplas assinaturas para listas de argumentos principais, bem como de comprimento variável, é um desses recursos.
Os programadores notaram que podem passar argumentos extras para uma função e nada de ruim acontece com o compilador fornecido.
Este é o caso se as convenções de chamada forem tais que:
Um conjunto de convenções de chamada que obedece a essas regras é a passagem de parâmetro baseada em pilha, por meio da qual o chamador exibe os argumentos e eles são empurrados da direita para a esquerda:
Em compiladores onde esse tipo de convenção de chamada é o caso, nada de especial precisa ser feito para oferecer suporte aos dois tipos
main
, ou mesmo tipos adicionais.main
pode ser uma função sem argumentos e, nesse caso, ignora os itens que foram colocados na pilha. Se for uma função de dois argumentos, ele encontraargc
eargv
como os dois itens mais altos da pilha. Se for uma variante de três argumentos específica da plataforma com um ponteiro de ambiente (uma extensão comum), isso também funcionará: encontrará o terceiro argumento como o terceiro elemento do topo da pilha.E assim, uma chamada fixa funciona para todos os casos, permitindo que um único módulo fixo de inicialização seja vinculado ao programa. Esse módulo pode ser escrito em C, como uma função semelhante a esta:
Em outras palavras, este módulo inicial apenas chama um principal de três argumentos, sempre. Se main não leva argumentos, ou apenas
int, char **
, funciona bem, assim como se não leva argumentos, devido às convenções de chamada.Se você fizesse esse tipo de coisa em seu programa, seria não portável e considerado um comportamento indefinido pelo ISO C: declarar e chamar uma função de uma maneira e defini-la de outra. Mas o truque de inicialização de um compilador não precisa ser portátil; não é guiado pelas regras para programas portáteis.
Mas suponha que as convenções de chamada sejam tais que não funcione dessa maneira. Nesse caso, o compilador deve tratar de forma
main
especial. Quando percebe que está compilando amain
função, pode gerar código compatível com, digamos, uma chamada de três argumentos.Ou seja, você escreve isto:
Mas quando o compilador o vê, ele essencialmente executa uma transformação de código para que a função que ele compila se pareça mais com isto:
exceto que os nomes
__argc_ignore
não existem literalmente. Esses nomes não são introduzidos em seu escopo e não haverá nenhum aviso sobre argumentos não utilizados. A transformação do código faz com que o compilador emita o código com a ligação correta, que sabe que deve limpar três argumentos.Outra estratégia de implementação é o compilador ou talvez o linker gerar a
__start
função de forma personalizada (ou o que quer que seja chamado), ou pelo menos selecionar uma das várias alternativas pré-compiladas. As informações podem ser armazenadas no arquivo de objeto sobre qual dos formulários suportadosmain
está sendo usado. O vinculador pode consultar essas informações e selecionar a versão correta do módulo de inicialização que contém uma chamadamain
compatível com a definição do programa. Implementações de C geralmente têm apenas um pequeno número de formas com suporte de,main
portanto, essa abordagem é viável.Compiladores para a linguagem C99 sempre têm que tratar
main
especialmente, até certo ponto, para suportar o hack de que se a função terminar sem umareturn
instrução, o comportamento é como se tivessereturn 0
sido executado. Isso, novamente, pode ser tratado por uma transformação de código. O compilador percebe que uma função chamadamain
está sendo compilada. Em seguida, ele verifica se a extremidade do corpo é potencialmente alcançável. Nesse caso, ele insere umreturn 0;
fonte
NÃO há sobrecarga
main
nem mesmo em C ++. A função principal é o ponto de entrada para um programa e apenas uma única definição deve existir.Para Padrão C
Para C ++ padrão:
O padrão C ++ diz explicitamente "Ela [a função principal] deve ter um tipo de retorno do tipo int, mas de outra forma seu tipo é definido pela implementação", e requer as mesmas duas assinaturas que o padrão C.
Em um ambiente hospedado ( ambiente AC que também oferece suporte às bibliotecas C) - as chamadas do sistema operacional
main
.Em um ambiente não hospedado (um destinado a aplicativos incorporados), você sempre pode alterar o ponto de entrada (ou saída) de seu programa usando as diretivas de pré-processador como
Onde a prioridade é um número integral opcional.
A inicialização do pragma executa a função antes da função principal (prioridade) e a saída do pragma executa a função após a função principal. Se houver mais de uma diretiva de inicialização, a prioridade decide qual será executada primeiro.
fonte
Não há necessidade de sobrecarga. Sim, existem 2 versões, mas apenas uma pode ser usada no momento.
fonte
Esta é uma das estranhas assimetrias e regras especiais da linguagem C e C ++.
Na minha opinião, existe apenas por razões históricas e não há nenhuma lógica séria por trás disso. Observe que
main
é especial também por outros motivos (por exemplo,main
em C ++ não pode ser recursivo e você não pode pegar seu endereço e em C99 / C ++ você pode omitir umareturn
declaração final ).Observe também que mesmo em C ++ não é uma sobrecarga ... ou um programa tem a primeira forma ou a segunda forma; não pode ter ambos.
fonte
return
instrução em C (desde C99).main()
e pegar seu endereço; C ++ aplica limites que C não aplica.argc
quando recorrente (5.1.2.2.1 não especifica limitações emargc
e seargv
aplicam apenas à chamada inicial paramain
).O que é incomum
main
não é que possa ser definido de mais de uma maneira, mas apenas de uma de duas maneiras diferentes.main
é uma função definida pelo usuário; a implementação não declara um protótipo para ele.A mesma coisa é verdadeira para
foo
oubar
, mas você pode definir funções com esses nomes da maneira que desejar.A diferença é que
main
é invocado pela implementação (o ambiente de tempo de execução), não apenas pelo seu próprio código. A implementação não está limitada à semântica de chamada de função C comum, portanto, ela pode (e deve) lidar com algumas variações - mas não é necessária para lidar com infinitas possibilidades. Oint main(int argc, char *argv[])
formulário permite argumentos de linha de comando e,int main(void)
em C ouint main()
C ++, é apenas uma conveniência para programas simples que não precisam processar argumentos de linha de comando.Quanto ao modo como o compilador lida com isso, depende da implementação. A maioria dos sistemas provavelmente tem convenções de chamada que tornam as duas formas efetivamente compatíveis, e quaisquer argumentos passados para um
main
definido sem parâmetros são silenciosamente ignorados. Do contrário, não seria difícil para um compilador ou vinculador tratar de maneiramain
especial. Se você está curioso para saber como funciona no seu sistema , pode consultar algumas listas de montagem.E como muitas coisas em C e C ++, os detalhes são em grande parte resultado da história e de decisões arbitrárias feitas pelos projetistas das linguagens e seus predecessores.
Observe que C e C ++ permitem outras definições definidas pela implementação para
main
- mas raramente há uma boa razão para usá-las. E para implementações independentes (como sistemas embarcados sem sistema operacional), o ponto de entrada do programa é definido pela implementação e nem mesmo é necessariamente chamadomain
.fonte
O
main
é apenas um nome para um endereço inicial decidido pelo vinculador, ondemain
é o nome padrão. Todos os nomes de função em um programa são endereços iniciais onde a função começa.Os argumentos da função são colocados / retirados da pilha, portanto, se não houver nenhum argumento especificado para a função, não haverá argumentos colocados / retirados da pilha. É assim que main pode funcionar com ou sem argumentos.
fonte
onde a variável argc armazena a contagem de dados que são passados e argv é uma matriz de ponteiros para char que aponta para os valores passados do console. Caso contrário, é sempre bom ir com
No entanto, em qualquer caso, pode haver um e apenas um main () em um programa, porque esse é o único ponto onde um programa começa sua execução e, portanto, não pode haver mais de um. (espero que valha a pena)
fonte
Uma pergunta semelhante foi feita antes: Por que uma função sem parâmetros (em comparação com a definição real da função) é compilada?
Uma das respostas com melhor classificação foi:
Então, eu acho que é como
main
é declarado (se você pode aplicar o termo "declarado" amain
). Na verdade, você pode escrever algo assim:e ainda será compilado e executado.
fonte
main
, pois há um problema ainda não mencionado: ainda mais argumentos a favormain
! Adiciona "Unix (mas não Posix.1) e Microsoft Windows"char **envp
(lembro que o DOS permitia isso também, não permitia?), E o Mac OS X e Darwin adicionavam mais um ponteiro char * para "informações arbitrárias fornecidas pelo SO". wikipediaVocê não precisa substituir isto, porque apenas um será usado por vez. Sim, existem 2 versões diferentes da função principal
fonte