Desde que percebi, há muitos anos, que isso não produz um erro por padrão (pelo menos no GCC), sempre me perguntei por que?
Entendo que você pode emitir sinalizadores de compilador para gerar um aviso, mas isso não deveria sempre ser um erro? Por que faz sentido que uma função não nula não retorne um valor para ser válida?
Um exemplo conforme solicitado nos comentários:
#include <stdio.h>
int stringSize()
{
}
int main()
{
char cstring[5];
printf( "the last char is: %c\n", cstring[stringSize()-1] );
return 0;
}
... compila.
-Werror=return-type
tratará exatamente esse aviso como um erro. Eu apenas ignorei o aviso e os dois minutos de frustração ao localizar umthis
ponteiro inválido me levaram até aqui e a esta conclusão.std::optional
função sem retornar retorna uma opção "verdadeira"Respostas:
Os padrões C99 e C ++ não requerem funções para retornar um valor. A declaração de retorno ausente em uma função de retorno de valor será definida (para retornar
0
) apenas namain
função.A lógica inclui que verificar se todo caminho de código retorna um valor é bastante difícil e um valor de retorno pode ser definido com assembler incorporado ou outros métodos complicados.
Do rascunho do C ++ 11 :
§ 6.6.3 / 2
§ 3.6.1 / 5
Observe que o comportamento descrito em C ++ 6.6.3 / 2 não é o mesmo em C.
O gcc emitirá um aviso se você chamá-lo com a opção -Wreturn-type.
Como curiosidade, veja o que esse código faz:
Esse código possui um comportamento formalmente indefinido e, na prática, está chamando de convenção e arquitetura dependente. Em um sistema específico, com um compilador específico, o valor de retorno é o resultado da avaliação da última expressão, armazenada no
eax
registro do processador desse sistema.fonte
throw
oulongjmp
, o compilador deve exigir um inalcançávelreturn
após a chamada para a função que não retorna? O caso em que não é necessário não é muito comum, e um requisito para incluí-lo mesmo nesses casos provavelmente não teria sido oneroso, mas a decisão de não requerê-lo é razoável.Por padrão, o gcc não verifica se todos os caminhos de código retornam um valor porque, em geral, isso não pode ser feito. Assume que você sabe o que está fazendo. Considere um exemplo comum usando enumerações:
Você, o programador, sabe que, salvo um erro, esse método sempre retorna uma cor. O gcc confia que você sabe o que está fazendo, para não forçar você a colocar um retorno na parte inferior da função.
javac, por outro lado, tenta verificar se todos os caminhos de código retornam um valor e lançam um erro se não puder provar que todos o fazem. Este erro é obrigatório pela especificação da linguagem Java. Observe que algumas vezes está errado e você deve inserir uma declaração de retorno desnecessária.
É uma diferença filosófica. C e C ++ são linguagens mais permissivas e confiáveis do que Java ou C # e, portanto, alguns erros nas linguagens mais recentes são avisos em C / C ++ e alguns avisos são ignorados ou desativados por padrão.
fonte
System.exit()
nunca retorna.System.exit()
nunca retorna. Eu procurei ( java.sun.com/j2se/1.4.2/docs/api/java/lang/… ), e os documentos dizem apenas que "nunca retorna normalmente". Eu me pergunto o que isso significa ...Você quer dizer, por que sair do final de uma função de retorno de valor (ou seja, sair sem um explícito
return
) não é um erro?Primeiramente, em C, se uma função retorna algo significativo ou não é apenas crítica quando o código em execução realmente usa o valor retornado. Talvez o idioma não queira forçá-lo a devolver nada quando você sabe que não vai usá-lo de qualquer maneira na maioria das vezes.
Segundo, aparentemente a especificação da linguagem não queria forçar os autores do compilador a detectar e verificar todos os caminhos de controle possíveis para a presença de um explícito
return
(embora em muitos casos isso não seja tão difícil de executar). Além disso, alguns caminhos de controle podem levar a funções que não retornam - a característica que geralmente não é conhecida pelo compilador. Esses caminhos podem se tornar uma fonte de falsos positivos irritantes.Observe também que C e C ++ diferem em suas definições de comportamento nesse caso. No C ++, apenas sair do final de uma função que retorna valor é sempre um comportamento indefinido (independentemente de o resultado da função ser usado pelo código de chamada). Em C, isso causa comportamento indefinido apenas se o código de chamada tentar usar o valor retornado.
fonte
return
instruções no final demain()
?main
é especial a esse respeito.É legal no C / C ++ não retornar de uma função que afirma retornar algo. Existem vários casos de uso, como chamada
exit(-1)
, ou uma função que chama ou lança uma exceção.O compilador não rejeitará C ++ legal, mesmo que leve ao UB, se você estiver pedindo para não fazê-lo. Em particular, você está solicitando que nenhum aviso seja gerado. (O Gcc ainda ativa alguns por padrão, mas quando adicionados, esses parecem estar alinhados com novos recursos, não com novos avisos para recursos antigos)
Alterar o no-arg gcc padrão para emitir alguns avisos pode ser uma mudança de ponta para scripts existentes ou criar sistemas. Os bem projetados também
-Wall
e lidam com avisos ou alternam avisos individuais.Aprender a usar uma cadeia de ferramentas C ++ é uma barreira para aprender a ser um programador de C ++, mas as cadeias de ferramentas C ++ geralmente são escritas por e para especialistas.
fonte
Makefile
caso-Wall -Wpedantic -Werror
, mas este foi um script de teste único que eu esqueci de fornecer os argumentos.-Wduplicated-cond
parte da-Wall
inicialização do GCC interrompida. Alguns avisos que parecem adequados na maioria dos códigos não são adequados em todo o código. É por isso que eles não estão ativados por padrão.int foo() { exit(-1); }
não retornaint
de uma função que "afirma retornar int". Isso é legal em C ++. Agora, ele não retorna nada ; o fim dessa função nunca é alcançado. Na verdade, chegar ao fimfoo
seria um comportamento indefinido. Ignorar casos de final de processoint foo() { throw 3.14; }
também afirma retornar,int
mas nunca retorna .void* foo(void* arg) { pthread_exit(NULL); }
é bom para a mesma razão (quando o seu uso apenas é viapthread_create(...,...,foo,...);
)Eu acredito que isso é devido ao código legado (C nunca exigiu declaração de retorno, assim como C ++). Provavelmente existe uma enorme base de código contando com esse "recurso". Mas pelo menos existe
-Werror=return-type
sinalizador em muitos compiladores (incluindo gcc e clang).fonte
Em alguns casos limitados e raros, pode ser útil sair do final de uma função não nula sem retornar um valor. Como o seguinte código específico da MSVC:
Esta função retorna pi usando assembly x86. Ao contrário da montagem no GCC, não conheço nenhuma maneira
return
de fazer isso sem envolver a sobrecarga no resultado.Tanto quanto eu sei, os compiladores C ++ convencionais devem emitir pelo menos avisos para código aparentemente inválido. Se eu
pi()
esvaziar o corpo , o GCC / Clang relatará um aviso e a MSVC reportará um erro.As pessoas mencionaram exceções e
exit
em algumas respostas. Essas não são razões válidas. Quer lançar uma exceção, ou chamarexit
, vai não fazer fluir a execução da função fora da final. E os compiladores sabem disso: escrever uma declaração throw ou chamarexit
o corpo vazio depi()
interromperá quaisquer avisos ou erros de um compilador.fonte
void
função depois que o asm inline deixa um valor no registro de valor de retorno. (x87st0
nesse caso, EAX para inteiro. E talvez xmm0 em uma convenção de chamada que retorne float / double em xmm0 em vez de st0). Definir esse comportamento é específico para MSVC; nem mesmo se apegar-fasm-blocks
a suportar a mesma sintaxe torna isso seguro. Veja Does __asm {}; retornar o valor de eax?Sob que circunstâncias não produz um erro? Se ele declara um tipo de retorno e não retorna algo, parece um erro para mim.
A única exceção em que consigo pensar é a
main()
função, que não precisa de nenhumareturn
declaração (pelo menos em C ++; não tenho nenhum dos padrões C à mão). Se não houver retorno, ele atuará como sereturn 0;
fosse a última declaração.fonte
main()
precisa de umreturn
C.return <value>;
declaraçãoreturn 0;
no final demain()
(emain()
somente). Mas é bom estilo para adicionar dereturn 0;
qualquer maneira.Parece que você precisa ativar os avisos do compilador:
fonte
return;
em uma função nula e é uma violação de restrição a ser usadareturn <value>;
em uma função nula. Mas acredito que esse não é o assunto. O OP, como eu o entendi, é sobre sair de uma função não nula sem areturn
(apenas permitindo que o controle flua para o final da função). Não é uma violação de restrição, AFAIK. O padrão apenas diz que é sempre UB em C ++ e, às vezes, UB em C. #É uma violação de restrição em c99, mas não em c89. Contraste:
c89:
c99:
Mesmo no
--std=c99
modo, o gcc emitirá apenas um aviso (embora sem a necessidade de ativar-W
sinalizadores , conforme exigido por padrão ou no c89 / 90).Edite para adicionar isso em c89, "atingir a
}
função que encerra uma função é equivalente a executar umareturn
instrução sem uma expressão" (3.6.6.4). No entanto, em c99, o comportamento é indefinido (6.9.1).fonte
return
declarações explícitas . Ele não cobre cair no final de uma função sem retornar um valor.