Declaração de devolução - Return statement

Na programação de computador , uma instrução de retorno faz com que a execução saia da sub - rotina atual e continue no ponto no código imediatamente após a instrução que chamou a sub-rotina, conhecido como seu endereço de retorno . O endereço de retorno é salvo pela rotina de chamada, hoje geralmente na pilha de chamadas do processo ou em um registrador . As instruções de retorno em muitas linguagens permitem que uma função especifique um valor de retorno a ser passado de volta ao código que chamou a função.

Visão geral

Em C e C ++ , (onde é uma expressão ) é uma instrução que diz a uma função para retornar a execução do programa para a função de chamada e relatar o valor de . Se uma função tiver o tipo de retorno void , a instrução return pode ser usada sem um valor, caso em que o programa apenas interrompe a função atual e retorna para a que a chamou. return exp;expexp

Em Pascal, não há instrução de retorno. (No entanto, em Pascals mais recentes, o pode ser usado para retornar um valor imediatamente. Sem parâmetros, ele apenas interrompe o procedimento.) Uma sub-rotina retorna automaticamente quando a execução atinge sua última instrução executável. Os valores podem ser retornados atribuindo a um identificador que tem o mesmo nome da sub-rotina, uma função na terminologia Pascal. Desta forma, o identificador da função é utilizado para chamadas recursivas e como detentor do resultado; isso é sintaticamente semelhante a um parâmetro de saída explícito . A mesma sintaxe é usada no Fortran 66 e no Fortran 77, embora uma instrução de retorno tenha sido adicionada no FORTRAN II . Em algumas outras linguagens, uma variável de resultado definida pelo usuário é usada em vez do identificador de função. Exit(exp);

Oberon ( Oberon-07 ) tem uma cláusula de retorno em vez de uma instrução de retorno. A cláusula return é colocada após a última instrução do corpo do procedimento. Isso permite a verificação em tempo de compilação do retorno adequado e do valor de retorno do procedimento.

Algumas linguagens de programação orientadas a expressões , como Lisp , Perl e Ruby , permitem que o programador omita uma instrução de retorno explícita, especificando em vez disso que a última expressão avaliada é o valor de retorno da sub-rotina.

Em outros casos, um valor Nulo é retornado se não houver uma instrução de retorno explícita: em Python , o valor Noneé retornado quando a instrução de retorno é omitida, enquanto em JavaScript o valor undefinedé retornado.

No Windows PowerShell, todas as expressões avaliadas que não são capturadas (por exemplo, atribuídas a uma variável, convertidas para void ou canalizadas para $ null ) são retornadas da sub-rotina como elementos em uma matriz ou como um único objeto no caso de apenas um objeto não foi capturado.

Em Perl, um valor ou valores de retorno de uma sub-rotina pode depender do contexto em que foi chamada. A distinção mais fundamental é um contexto escalar onde o código de chamada espera um valor, um contexto de lista onde o código de chamada espera uma lista de valores e um contexto vazio onde o código de chamada não espera nenhum valor de retorno. Uma sub-rotina pode verificar o contexto usando a wantarrayfunção. Uma sintaxe especial de retorno sem argumentos é usada para retornar um valor indefinido no contexto escalar e uma lista vazia no contexto da lista. O contexto escalar pode ser dividido em contextos booleanos , numéricos, de string e de vários tipos de referência . Além disso, um objeto sensível ao contexto pode ser retornado usando uma sequência de retorno contextual, com avaliação lenta de valores escalares.

Muitos sistemas operacionais permitem que um programa retorne um resultado (separado da saída normal ) quando seu processo termina; esses valores são chamados de códigos de retorno ou, mais especificamente, status de saída . A quantidade de informações que podem ser transmitidas dessa forma é bastante limitada; na prática, muitas vezes restrita a sinalizar o sucesso ou o fracasso. De dentro do programa, esse retorno é normalmente obtido chamando Exit (chamada do sistema) (comum mesmo em C, onde o mecanismo alternativo de retorno da função principal está disponível).

Sintaxe

As declarações de retorno têm várias formas. As seguintes sintaxes são as mais comuns:

Língua Declaração de Devolução Se o valor for omitido, Return
Ada , Bourne shell , C , C ++ , Java , PHP , C # , JavaScript , D
return value;
no shell Bourne, valor de saída do último comando executado na função

em C e C ++, comportamento indefinido se a função retornar valor

em PHP, retorna NULL

em Javascript, retorna o valor undefined

em Java e C #, não permitido se a função retornar valor

BASIC
RETURN
Lisp
(return value)
valor da última declaração
Perl , Ruby
return @values;
return $value;
return;

ou uma sequência de retorno contextual

valor da última declaração
PL / I
return(expression);
return;

comportamento indefinido se o procedimento for declarado como retornando um valor
Pitão
return value
None
Conversa fiada
^ value
Tcl
return
return $value
return -code error "Error message"

ou alguma combinação mais complicada de opções

valor da última declaração
Visual Basic .NET
Return value
Windows PowerShell
return value;
objeto
montagem x86
ret
conteúdo do eax register (por convenções)

Em algumas linguagens de montagem , por exemplo, para o MOS Technology 6502 , o mnemônico "RTS" (ReTurn da subrotina) é usado.

Declarações de retorno múltiplas

Linguagens com uma instrução de retorno explícita criam a possibilidade de várias instruções de retorno na mesma função. Se isso é bom ou não, é controverso.

Fortes adeptos da programação estruturada garantem que cada função tenha uma única entrada e uma única saída (SESE). Assim, argumentou-se que se deve evitar o uso da declaração de retorno explícito, exceto no final textual de uma sub-rotina, considerando que, quando ela é usada para "retornar mais cedo", pode sofrer do mesmo tipo de problemas que surgem para a declaração GOTO . Por outro lado, pode-se argumentar que usar a instrução return vale a pena quando a alternativa é um código mais complicado, como aninhamento mais profundo, prejudicando a legibilidade.

Em seu livro de 2004, David Watt escreve que "fluxos de controle de múltiplas saídas de entrada única são freqüentemente desejáveis". Usando a noção de sequenciador da estrutura de Tennent , Watt descreve uniformemente as construções de fluxo de controle encontradas nas linguagens de programação contemporâneas e tenta explicar por que certos tipos de sequenciadores são preferíveis a outros no contexto de fluxos de controle de múltiplas saídas. Watt escreve que os gotos irrestritos (sequenciadores de salto) são ruins porque o destino do salto não é autoexplicativo para o leitor de um programa até que o leitor encontre e examine o rótulo ou endereço real que é o alvo do salto. Em contraste, Watt argumenta que a intenção conceitual de um sequenciador de retorno é clara em seu próprio contexto, sem ter que examinar seu destino. Além disso, Watt escreve que uma classe de sequenciadores conhecidos como sequenciadores de escape , definidos como "sequenciador que encerra a execução de um comando ou procedimento envolvente textualmente", abrange tanto as quebras de loops (incluindo quebras de vários níveis) quanto as instruções de retorno. Watt também observa que, embora os sequenciadores de salto (gotos) tenham sido um tanto restritos em linguagens como C, onde o alvo deve ser um bloco interno ou um bloco externo abrangente, essa restrição por si só não é suficiente para tornar a intenção de gotos em C self -descrevendo e assim eles ainda podem produzir " código espaguete ". Watt também examina como os sequenciadores de exceção diferem dos sequenciadores de escape e salto; para obter detalhes sobre isso, consulte o artigo sobre programação estruturada .

De acordo com estudos empíricos citados por Eric S. Roberts , os alunos programadores tiveram dificuldade em formular soluções corretas para vários problemas simples em uma linguagem como o Pascal , que não permite vários pontos de saída. Para o problema de escrever uma função para pesquisar linearmente um elemento em um array, um estudo de 1980 por Henry Shapiro (citado por Roberts) descobriu que usando apenas as estruturas de controle fornecidas por Pascal, a solução correta foi dada por apenas 20% dos sujeitos , embora nenhum sujeito tenha escrito um código incorreto para esse problema, se tiver permissão para escrever um retorno do meio de um loop.

Outros, incluindo Kent Beck e Martin Fowler, argumentam que uma ou mais cláusulas de guarda - declarações de retorno de "saída antecipada" condicional perto do início de uma função - geralmente tornam uma função mais fácil de ler do que a alternativa.

O problema mais comum na saída antecipada é que as instruções de limpeza ou finais não são executadas - por exemplo, a memória alocada não é desalocada ou os arquivos abertos não são fechados, causando vazamentos. Isso deve ser feito em cada local de retorno, que é frágil e pode facilmente resultar em bugs. Por exemplo, em um desenvolvimento posterior, uma instrução de retorno pode ser ignorada por um desenvolvedor, e uma ação que deve ser executada no final de uma sub-rotina (por exemplo, uma instrução de rastreamento ) pode não ser executada em todos os casos. Linguagens sem uma instrução de retorno, como Pascal padrão , não têm esse problema. Algumas linguagens, como C ++ e Python, empregam conceitos que permitem que ações sejam executadas automaticamente após o retorno (ou lançamento de exceção), o que atenua alguns desses problemas - esses são frequentemente conhecidos como "try / finally" ou similar. Funcionalidades como essas cláusulas "finalmente" podem ser implementadas por um goto para o único ponto de retorno da sub-rotina. Uma solução alternativa é usar o desenrolamento normal da pilha (desalocação de variável) na saída da função para cancelar a alocação de recursos, como por meio de destruidores em variáveis ​​locais ou mecanismos semelhantes, como a instrução "with" do Python.

Algumas implementações anteriores de linguagens, como Pascal e C originais, restringiam os tipos que podem ser retornados por uma função (por exemplo, não suportando tipos de registro ou estrutura ) para simplificar seus compiladores .

Em Java - e em linguagens semelhantes modeladas a partir dele, como JavaScript - é possível executar código mesmo após a instrução return, porque o bloco finally de uma estrutura try-catch é sempre executado. Portanto, se a instrução return for colocada em algum lugar dos blocos try ou catch , o código em finally (se adicionado) será executado. É ainda possível alterar o valor de retorno de um tipo não primitivo (uma propriedade de um objeto já retornado) porque a saída ocorre posteriormente também.

Declarações de rendimento

As declarações Cousin to return são declarações de rendimento : onde um return faz com que uma sub- rotina termine, um yield faz com que uma co- rotina seja suspensa. A co-rotina continuará mais tarde de onde foi suspensa se for chamada novamente. As corrotinas são significativamente mais envolvidas na implementação do que as sub-rotinas e, portanto, as declarações de rendimento são menos comuns do que as declarações de retorno, mas são encontradas em várias linguagens.

Sequências de chamada / retorno

Uma série de sequências de chamada / retorno possíveis são possíveis dependendo do conjunto de instruções de hardware, incluindo o seguinte:

  1. A CALLinstrução empurra o endereço da próxima instrução na pilha e desvia para o endereço especificado. A RETURNinstrução mostra o endereço de retorno da pilha para o ponteiro de instrução e a execução é retomada naquele endereço. (Exemplos x86, PDP-11)
  2. A CALLinstrução coloca o endereço da próxima instrução em um registrador e desvia para o endereço especificado. A RETURNsequência de instruções coloca o endereço de retorno do registrador no ponteiro de instrução e a execução é retomada naquele endereço. (Exemplo IBM System / 360)
  3. A CALLinstrução coloca o endereço da próxima (ou atual ) instrução no local de armazenamento no endereço de chamada e se ramifica para o endereço especificado + 1. A RETURNsequência de instruções desvia para o endereço de retorno por um salto indireto para a primeira instrução da sub-rotina. (Exemplos IBM 1130, SDS9XX)

Veja também

Referências