Considere o seguinte programa:
#include <iostream>
int main = ( std::cout << "C++ is excellent!\n", 195 );
Usando g ++ 4.8.1 (mingw64) no sistema operacional Windows 7, o programa é compilado e executado corretamente, imprimindo:
C ++ é excelente!
para o console. main
parece ser uma variável global em vez de uma função; como este programa pode ser executado sem a função main()
? Este código está em conformidade com o padrão C ++? O comportamento do programa está bem definido? Também usei a -pedantic-errors
opção, mas o programa ainda compila e executa.
c++
main
language-lawyer
Destruidor
fonte
fonte
195
é o opcode daRET
instrução e que, na convenção de chamada C, o chamador limpa a pilha.main()
função? Na verdade, eles não estão relacionados.)int main = ( std::cout << "C++ is excellent!\n", exit(0),1 );
(e incluindo<cstdlib>
), embora o programa permaneça legalmente mal formado.Respostas:
Antes de entrar no cerne da questão sobre o que está acontecendo, é importante apontar que o programa está malformado de acordo com relatório de defeito 1886: Language linkage for main () :
As versões mais recentes do clang e do gcc tornam isso um erro e o programa não compila ( consulte o exemplo ao vivo do gcc ):
Então, por que não havia diagnóstico nas versões mais antigas do gcc e do clang? Esse relatório de defeito nem tinha uma proposta de resolução até o final de 2014 e, portanto, esse caso só foi explicitamente malformado muito recentemente, o que requer um diagnóstico.
Antes disso, parece que isso seria um comportamento indefinido, uma vez que estão a violar um deve exigência do projeto de C ++ padrão da seção
3.6.1
[basic.start.main] :O comportamento indefinido é imprevisível e não requer um diagnóstico. A inconsistência que vemos ao reproduzir o comportamento é um comportamento indefinido típico.
Então, o que o código está realmente fazendo e por que, em alguns casos, ele produz resultados? Vamos ver o que temos:
Temos
main
que é um int declarado no namespace global e está sendo inicializado, a variável tem duração de armazenamento estático. É definido pela implementação se a inicialização ocorrerá antes que uma tentativa de chamadamain
seja feita, mas parece que o gcc faz isso antes de chamarmain
.O código usa o operador vírgula , o operando esquerdo é uma expressão de valor descartada e é usado aqui apenas para o efeito colateral da chamada
std::cout
. O resultado do operador vírgula é o operando correto que, neste caso, é o prvalue195
atribuído à variávelmain
.Podemos ver que sergej aponta a montagem gerada mostra que
cout
é chamada durante a inicialização estática. Embora o ponto mais interessante para discussão, consulte a sessão do godbolt ao vivo seria este:e o subsequente:
O cenário provável é que o programa salte para o símbolo
main
esperando que o código válido esteja lá e, em alguns casos, apresentará falha de seg . Portanto, se for esse o caso, esperaríamos que armazenar código de máquina válido na variávelmain
pudesse levar a um programa viável , supondo que estejamos localizados em um segmento que permite a execução do código. Podemos ver que esta entrada do IOCCC de 1984 faz exatamente isso .Parece que podemos fazer o gcc fazer isso em C usando ( veja ao vivo ):
Ele falha se a variável
main
não for const presumivelmente porque não está localizada em um local executável, Hat Dica para este comentário aqui que me deu essa idéia.Veja também a resposta FUZxxl aqui para uma versão C específica desta pergunta.
fonte
main
não é um identificador reservado (3.6.1 / 3). Neste caso, acho que o tratamento do VS2013 neste caso (veja a resposta de Francis Cugler) é mais correto no tratamento do que o gcc & clang.De 3.6.1 / 1:
A partir disso, parece que g ++ permite que um programa (presumivelmente como a cláusula "independente") sem uma função principal.
Então, de 3.6.1 / 3:
Então aqui nós aprendemos que é perfeitamente normal ter uma variável inteira chamada
main
.Finalmente, se você está se perguntando por que a saída é impressa, a inicialização do
int main
usa o operador vírgula para executarcout
na inicialização estática e então fornece um valor integral real para fazer a inicialização.fonte
main
para outra coisa:(.text+0x20): undefined reference to
principal '`O gcc 4.8.1 gera o seguinte conjunto x86:
Observe que
cout
é chamado durante a inicialização, não namain
função!.zero 4
declara 4 bytes (inicializados em 0) começando no localmain
, ondemain
é o nome da variável [!] .O
main
símbolo é interpretado como o início do programa. O comportamento depende da plataforma.fonte
195
é o opcode pararet
algumas arquiteturas. Portanto, dizer zero instruções pode não ser preciso.Esse é um programa malformado. Ele trava no meu ambiente de teste, cygwin64 / g ++ 4.9.3.
Do padrão:
fonte
Acredito que isso funcione porque o compilador não sabe que está compilando a
main()
função, por isso compila um inteiro global com efeitos colaterais de atribuição.O formato do objeto em que esta unidade de tradução é compilada não é capaz de diferenciar entre um símbolo de função e um símbolo de variável .
Assim, o vinculador felizmente se vincula ao símbolo principal (variável) e o trata como uma chamada de função. Mas não até o sistema de tempo de execução execute o código de inicialização da variável global.
Quando executei a amostra, ela imprimiu, mas causou uma falha de seg . Suponho que foi quando o sistema de tempo de execução tentou executar uma variável int como se fosse uma função .
fonte
Eu tentei isso em um sistema operacional Win7 de 64 bits usando VS2013 e ele compila corretamente, mas quando tento construir o aplicativo, recebo esta mensagem na janela de saída.
fonte
main()
porque é uma variável do tipoint
Você está fazendo um trabalho complicado aqui. Como principal (de alguma forma) pode ser declarado inteiro. Você usou o operador de lista para imprimir a mensagem e atribuir 195 a ela. Como disse alguém abaixo, que não é confortável com C ++, é verdade. Mas como o compilador não encontrou nenhum nome definido pelo usuário, main, ele não reclamou. Lembre-se de que main não é uma função definida pelo sistema, sua função definida pelo usuário e a coisa a partir da qual o programa começa a ser executado é o Módulo Principal, não main (), especificamente. Novamente, main () é chamado pela função de inicialização que é executada intencionalmente pelo carregador. Então, todas as suas variáveis são inicializadas e, ao inicializar, a saída é assim. É isso aí. Programa sem main () está ok, mas não é padrão.
fonte