Eu pergunto porque meu compilador parece pensar assim, embora eu não.
echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall
O Clang não emite nenhum aviso ou erro com isso, e o gcc emite apenas o aviso manso:, 'main' is usually a function [-Wmain]
mas apenas quando compilado como C. Especificar um -std=
não parece importar.
Caso contrário, ele compila e vincula corretamente. Mas na execução, ele termina imediatamente com SIGBUS
(para mim).
Lendo as (excelentes) respostas em O que main () deve retornar em C e C ++? e um rápido grep pelas especificações da linguagem, certamente me pareceria que uma função principal é necessária. Mas a verborragia do gcc's -Wmain
('main' geralmente é uma função) (e a falta de erros aqui) parece possivelmente sugerir o contrário.
Mas por que? Existe algum uso estranho ou “histórico” para isso? Alguém sabe o que dá?
Meu ponto, suponho, é que realmente acho que isso deveria ser um erro em um ambiente hospedado, hein?
gcc -std=c99 -pedantic ...
-pedantic
ou nenhum-std
. Meu sistemac99
também compila isso sem aviso ou erro ...main
, que provavelmente não funcionará. Se você inicializar main com o valor "correto", ele pode realmente retornar ...main
)main=195;
Respostas:
Uma vez que a pergunta está marcada duas vezes como C e C ++, o raciocínio para C ++ e C seria diferente:
xyz
e uma função global independentexyz(int)
. No entanto, o nomemain
nunca é mutilado.Isso é o que está acontecendo aqui: o vinculador espera encontrar o símbolo
main
, e ele o faz. Ele "liga" aquele símbolo como se fosse uma função, porque não o conhece melhor. A parte da biblioteca de tempo de execução que passa o controle paramain
solicitar ao vinculadormain
, portanto, o vinculador fornece o símbolomain
, permitindo que a fase de vinculação seja concluída. É claro que isso falha em tempo de execução, porquemain
não é uma função.Aqui está outra ilustração do mesmo problema:
arquivo xc:
arquivo yc:
compilando:
Isso compila e provavelmente seria executado, mas é um comportamento indefinido, porque o tipo de símbolo prometido ao compilador é diferente do símbolo real fornecido ao vinculador.
No que diz respeito ao aviso, acho que é razoável: C permite construir bibliotecas que não têm
main
função, portanto, o compilador libera o nomemain
para outros usos se você precisar definir uma variávelmain
por algum motivo desconhecido.fonte
main
não está sujeito a alteração de nome (ao que parece) em C ++, seja ou não uma função.main
como qualquer coisa diferente de uma função. A resposta oferece uma explicação para ambas as partes.main
não é uma palavra reservada é apenas um identificador predefinido (comocin
,endl
,npos
...), então você poderia declarar uma variável chamadamain
, inicialize-o e, em seguida, imprimir o seu valor.Claro:
main()
função (bibliotecas).EDITAR
Algumas referências:
main
não é uma palavra reservada (C ++ 11):C ++ 11 - [basic.start.main] 3.6.1.3
Palavras reservadas em linguagens de programação .
As palavras reservadas podem não ser redefinidas pelo programador, mas os predefineds geralmente podem ser substituídos em alguma capacidade. É o caso de
main
: existem escopos em que uma declaração que usa esse identificador redefine seu significado.fonte
main()
função, mas você não pode vinculá-la como um programa. O que está acontecendo aqui é que um programa "válido" está sendo vinculado sem ummain()
, apenas ummain
.cin
eendl
não estão no namespace padrão - eles estão nostd
namespace.npos
é um membro destd::basic_string
.main
é reservado como um nome global. Nenhuma das outras coisas que você mencionou, nemmain
, são predefinidas.main
é permitido. C ++ diz "Uma implementação não deve predefinir a função principal" e C diz "A implementação não declara nenhum protótipo para esta função."É
int main;
um programa C / C ++ válido?Não está totalmente claro o que é um programa C / C ++.
É
int main;
um programa C válido?Sim. Uma implementação independente é permitida para aceitar tal programa.
main
não precisa ter nenhum significado especial em um ambiente independente.É não válido em um ambiente hospedado.
É
int main;
um programa C ++ válido?Idem.
Por que ele trava?
O programa não precisa fazer sentido em seu ambiente. Em um ambiente independente, a inicialização e o encerramento do programa, e o significado de
main
, são definidos pela implementação.Por que o compilador me avisa?
O compilador pode avisá-lo sobre o que quiser, desde que não rejeite programas em conformidade. Por outro lado, o aviso é tudo o que é necessário para diagnosticar um programa não conforme. Uma vez que esta unidade de tradução não pode fazer parte de um programa hospedado válido, uma mensagem de diagnóstico é justificada.
É
gcc
um ambiente independente ou hospedado?Sim.
gcc
documenta o-ffreestanding
sinalizador de compilação. Adicione e o aviso desaparece. Você pode querer usá-lo ao construir, por exemplo, kernels ou firmware.g++
não documenta tal bandeira. O fornecimento parece não ter efeito sobre o programa. É provavelmente seguro presumir que o ambiente fornecido pelo g ++ está hospedado. A ausência de diagnóstico neste caso é um bug.fonte
É um aviso, pois não é tecnicamente proibido. O código de inicialização usará a localização do símbolo "principal" e saltará para ela com os três argumentos padrão (argc, argv e envp). Isso não acontece, e no momento do link não pode verificar se é realmente uma função, nem mesmo se tem esses argumentos. É também por isso que int main (int argc, char ** argv) funciona - o compilador não sabe sobre o argumento envp e ele simplesmente não é usado, e é uma limpeza do chamador.
Como uma piada, você poderia fazer algo como
em uma máquina x86 e, ignorando avisos e coisas semelhantes, ele não apenas compilará, mas também funcionará.
Alguém usou uma técnica semelhante a esta para escrever um executável (mais ou menos) que roda em múltiplas arquiteturas diretamente - http://phrack.org/issues/57/17.html#article . Também foi usado para vencer o IOCCC - http://www.ioccc.org/1984/mullender/mullender.c .
fonte
int main __attribute__ ((section (".text")))= 0xC3C3C3C3;
É um programa válido?
Não.
Não é um programa, pois não possui partes executáveis.
É válido compilar?
Sim.
Pode ser usado com um programa válido?
Sim.
Nem todo código compilado precisa ser executável para ser válido. Exemplos são bibliotecas estáticas e dinâmicas.
Você efetivamente construiu um arquivo de objeto. Não é um executável válido; no entanto, outro programa pode se vincular ao objeto
main
no arquivo resultante, carregando-o em tempo de execução.Isso deve ser um erro?
Tradicionalmente, C ++ permite que o usuário faça coisas que podem parecer não ter uso válido, mas que se encaixam na sintaxe da linguagem.
Claro, isso poderia ser reclassificado como um erro, mas por quê? Que propósito isso serviria, mas o aviso não?
Enquanto houver uma possibilidade teórica de essa funcionalidade ser usada no código real, é muito improvável que ter um objeto não funcional chamado
main
resulte em um erro de acordo com a linguagem.fonte
main
. Como pode um programa válido, que deve ter uma função externamente visível chamadamain
, vincular a ele?main
função.int main;
definição não estará visível.Eu gostaria de acrescentar às respostas já fornecidas, citando os padrões de linguagem atuais.
É 'int main;' um programa C válido?
Resposta curta (minha opinião): somente se sua implementação usar um "ambiente de execução independente".
Todas as citações seguintes de C11
5. Meio Ambiente
5.1.2 Ambientes de execução
5.1.2.1 Ambiente autônomo
5.1.2.2 Ambiente hospedado
5.1.2.2.1 Inicialização do programa
Destes, o seguinte é observado:
Em um ambiente de execução independente, eu diria que é um programa válido que não permite que a inicialização aconteça, porque não há nenhuma função presente para isso, conforme exigido em 5.1.2. Em um ambiente de execução hospedado, embora seu código introduza um objeto denominado main , ele não pode fornecer um valor de retorno, então eu argumentaria que não é um programa válido neste sentido, embora alguém pudesse argumentar antes que se o programa não fosse destinado a ser executado (em pode querer fornecer dados apenas, por exemplo), então ele simplesmente não permite fazer exatamente isso.
É 'int main;' um programa C ++ válido?
Resposta curta (minha opinião): somente se sua implementação usar um "ambiente de execução independente".
Citação de C ++ 14
3.6.1 Função principal
Aqui, ao contrário do padrão C11, menos restrições se aplicam ao ambiente de execução independente, já que nenhuma função de inicialização é mencionada, enquanto para um ambiente de execução hospedado, o caso é praticamente o mesmo que para C11.
Novamente, eu diria que, para o caso hospedado, seu código não é um programa C ++ 14 válido, mas tenho certeza de que é para o caso independente.
Como minha resposta considera apenas o ambiente de execução , acho que a resposta de dasblinkenlicht entra em cena, já que a mutilação de nomes que ocorre no ambiente de tradução ocorre de antemão. Aqui, não estou tão certo de que as citações acima sejam observadas tão estritamente.
fonte
O erro é seu. Você não especificou uma função chamada
main
que retorna umint
e tentou usar seu programa em um ambiente hospedado.Suponha que você tenha uma unidade de compilação que defina uma variável global chamada
main
. Isso pode ser legal em um ambiente independente porque o que constitui um programa é deixado para a implementação em ambientes independentes.Suponha que você tenha outra unidade de compilação que define uma função global chamada
main
que retorna umint
e não recebe argumentos. Isso é exatamente o que um programa em um ambiente hospedado precisa.Tudo está bem se você usar apenas a primeira unidade de compilação em um ambiente independente e apenas usar a segunda em um ambiente hospedado. E se você usar os dois em um programa? Em C ++, você violou a regra de uma definição. Esse é um comportamento indefinido. Em C, você violou a regra que determina que todas as referências a um único símbolo devem ser consistentes; se não forem, é um comportamento indefinido. O comportamento indefinido é um "saia da prisão, grátis!" cartão para desenvolvedores de uma implementação. Qualquer coisa que uma implementação faça em resposta a um comportamento indefinido é compatível com o padrão. A implementação não precisa avisar, muito menos detectar, comportamento indefinido.
E se você usar apenas uma dessas unidades de compilação, mas usar a errada (que é o que você fez)? Em C, a situação é clara. A falha em definir a função
main
em uma das duas formas padrão em um ambiente hospedado é um comportamento indefinido. Suponha que você não tenha definidomain
nada. O compilador / vinculador não precisa dizer nada sobre esse erro. Que eles se queixem é uma gentileza da parte deles. Que o programa C compilado e vinculado sem erros é sua culpa, não do compilador.É um pouco menos claro em C ++ porque a falha em definir a função
main
em um ambiente hospedado é um erro, e não um comportamento indefinido (em outras palavras, deve ser diagnosticado). No entanto, a única regra de definição em C ++ significa que os vinculadores podem ser bastante burros. O trabalho do vinculador é resolver referências externas e, graças à regra de definição única, o vinculador não precisa saber o que esses símbolos significam. Você forneceu um símbolo denominadomain
, o vinculador está esperando ver um símbolo denominadomain
, portanto, está tudo certo no que diz respeito ao vinculador.fonte
Para C, até agora, é o comportamento definido pela implementação.
Conforme a ISO / IEC9899 diz:
fonte
Não, este não é um programa válido.
Para C ++, isso foi recentemente explicitamente malformado pelo relatório de defeito 1886: Link de linguagem para main () que diz:
e parte da resolução incluiu a seguinte alteração:
Podemos encontrar esse texto no último rascunho do padrão C ++ N4527, que é o rascunho C ++ 1z.
As versões mais recentes do clang e do gcc agora tornam isso um erro ( veja ao vivo ):
Antes deste relatório de defeito, era um comportamento indefinido que não requer um diagnóstico. Por outro lado, um código malformado requer um diagnóstico, o compilador pode tornar isso um aviso ou um erro.
fonte
main()
.) Eu entendo a razão para não permitirmain()
uma especificação de ligação explícita, mas não entendo que obrigue amain()
ter uma ligação C ++ . É claro que a norma não diretamente endereço como lidar com ABI ligação / desconfiguração do nome, mas na prática (por exemplo, com Itanium ABI) isso iria manglemain()
para_Z4mainv
. o que estou perdendo?