Por que flui para fora da extremidade de uma função não-void sem retornar um valor não produz um erro do compilador?

StackOverflow https://stackoverflow.com/questions/1610030

  •  05-07-2019
  •  | 
  •  

Pergunta

Desde que eu percebi há muitos anos, que este não produz um erro por padrão (em GCC, pelo menos), eu sempre me perguntei por quê?

Eu entendo que você pode emitir bandeiras do compilador para produzir um aviso, mas não deve ser sempre um erro? Por que será que faz sentido para uma função não-vazio não retornar um valor para ser válido?

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.

Foi útil?

Solução

C99 e os padrões C ++ não exigem funções para retornar um valor. A instrução de retorno ausentes em uma função que retorna o valor será definido (para 0 retorno) somente na função main.

A lógica inclui que verificar se todos os caminhos 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.

A partir C ++ 11 projecto:

§ 6.6.3 / 2

fluindo para fora da extremidade de uma função [...] resulta em um comportamento indefinido em uma função que retorna valor.

§ 3.6.1 / 5

Se o controle chega ao fim da main sem encontrar uma declaração return, o efeito é o de execução

return 0;

Note-se que o comportamento descrito em C ++ 6.6.3 / 2 não é o mesmo em C.


gcc vai lhe dar um aviso se você chamá-lo opção -Wreturn-tipo com.

-Wreturn-tipo avisar, sempre que uma função é definida com um tipo que troca defaults para int. Também alertar sobre qualquer declaração retornar com nenhum retorno de valor em uma função cujo tipo de retorno não é void (cair no final do corpo da função é considerado retorno sem valor), e cerca de um retorno declaração com uma expressão em uma função cujo retorno tipo é nula.

Este aviso é ativado por -Wall .


Apenas como curiosidade, olhar o que este código faz:

#include <iostream>

int foo() {
   int a = 5;
   int b = a + 1;
}

int main() { std::cout << foo() << std::endl; } // may print 6

Este código tem um comportamento formalmente indefinido, e, na prática, é convenção de chamada e arquitetura dependente. Em um sistema em particular, com um compilador particular, o valor de retorno é o resultado da avaliação da expressão passado, armazenado no registo eax de processador que do sistema.

Outras dicas

gcc não por cheque padrão que todos os caminhos de código retornar um valor, porque, em geral, isso não pode ser feito. Ele assume que você sabe o que está fazendo. Considere um exemplo comum utilizando enumerações:

Color getColor(Suit suit) {
    switch (suit) {
        case HEARTS: case DIAMONDS: return RED;
        case SPADES: case CLUBS:    return BLACK;
    }

    // Error, no return?
}

Você o programador sabe que, salvo um erro, esse método sempre retorna uma cor. trusts gcc que você sabe que você está fazendo para que ele não forçá-lo a colocar um retorno na parte inferior da função.

javac, por outro lado, tenta verificar se todos os caminhos de código retornar um valor e gera um erro se não puder provar que tudo o que fazem. Este erro é mandatado pela especificação da linguagem Java. Note que às vezes é errado e você tem que colocar em uma instrução de retorno desnecessário.

char getChoice() {
    int ch = read();

    if (ch == -1 || ch == 'q') {
        System.exit(0);
    }
    else {
        return (char) ch;
    }

    // Cannot reach here, but still an error.
}

É uma diferença filosófica. C e C ++ são mais permissivas e confiando idiomas além do Java ou C # e assim por alguns erros nas línguas mais recentes são avisos em C / C ++ e alguns avisos são ignorados ou desativado por padrão.

Quer dizer, por que flui para fora da extremidade de uma função que retorna o valor (ou seja, sair sem um return explícito) não é um erro?

Em primeiro lugar, em C se uma função retorna algo significativo ou não é apenas crítico quando o código em execução na verdade usos o valor retornado. Talvez a linguagem não queria forçá-lo a retornar nada quando você sabe que você não está indo para usá-lo de qualquer maneira na maior parte do tempo.

Em segundo lugar, aparentemente, a especificação da linguagem não queria forçar os autores do compilador para detectar e verificar todos os caminhos de controle possíveis para a presença de um return explícita (embora em muitos casos isso não é tão difícil de fazer). Além disso, alguns caminhos de controle pode levar para a funções não retornar - a característica que é geralmente não conhecido para o compilador. Tais caminhos pode se tornar uma fonte de irritantes falsos positivos.

Note também que C e C ++ diferem em suas definições do comportamento neste caso. Em C ++ apenas fluindo para fora da extremidade de uma função retornando valor é sempre um comportamento indefinido (independentemente do resultado da função é usado pelo código de chamada). Em C isto provoca um comportamento indefinido somente se as tentativas código de chamada para usar o valor retornado.

É legal em C / C ++ para não retornar de uma função que pretende retornar algo. Há uma série de casos de uso, como exit(-1) chamando, ou uma função que chama-lo ou lança uma exceção.

O compilador não vai rejeitar C ++ legal mesmo que leva a UB se você está pedindo não para. Em particular, você está pedindo para nenhum aviso a ser gerado. (GCC ainda gira em algum por padrão, mas quando adicionado esses parecem se alinhar com novos recursos não novas advertências para recursos antigos)

A alteração do padrão gcc no-arg para emitir algumas advertências poderia ser uma alteração de quebra para scripts existentes ou sistemas fazem. Bem desenhado queridos quer -Wall e lidar com os avisos, ou alternância avisos individuais.

Aprender a utilizar uma ferramenta C ++ cadeia é uma barreira para aprender a ser um programador C ++, mas cadeias de ferramentas C ++ são tipicamente escritas por e para os especialistas.

Em que circunstâncias não é mesmo produzir um erro? Se declara um tipo de retorno e não retorna alguma coisa, que soa como um erro para mim.

A única exceção que eu posso pensar é a função main(), que não precisa de uma declaração return em tudo (pelo menos em C ++; eu não tenho qualquer um dos padrões C útil). Se não há retorno, ele vai agir como se return 0; é a última declaração.

Parece que você precisa para aumentar seus avisos do compilador:

$ gcc -Wall -Wextra -Werror -x c -
int main(void) { return; }
cc1: warnings being treated as errors
<stdin>: In function ‘main’:
<stdin>:1: warning: ‘return’ with no value, in function returning non-void
<stdin>:1: warning: control reaches end of non-void function
$

Creio que isto é por causa do código legado (instrução de retorno C nunca exigiu assim como C ++). Há provavelmente enorme base de código depender de que "recurso". Mas pelo menos há -Werror=return-type bandeira em muitos compiladores (incluindo gcc e clang).

É uma violação de restrição no C99, mas não em c89. Contraste:

c89:

3.6.6.4 A declaração return

Restrições

A declaração return com uma expressão não deve figurar numa função cujo tipo de retorno é void.

c99:

6.8.6.4 A declaração return

Restrições

A declaração return com uma expressão não deve aparecer em uma função cujo tipo de retorno é void. Uma declaração return sem uma expressão só aparecem em uma função cujo tipo de retorno é void.

Mesmo no modo --std=c99, gcc só irá lançar um aviso (embora sem a necessidade de permitir bandeiras -W adicionais, como é exigido por padrão ou em c89 / 90).

Editar para acrescentar que, em c89 ", atingindo o } que termina uma função é equivalente a executar uma instrução return sem uma expressão"(3.6.6.4). No entanto, em c99 o comportamento é indefinido (6.9.1).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top