En programación de computadoras , una declaración de retorno hace que la ejecución abandone la subrutina actual y se reanude en el punto del código inmediatamente después de la instrucción que llamó a la subrutina, conocido como su dirección de retorno . La dirección de retorno la guarda la rutina de llamada, hoy generalmente en la pila de llamadas del proceso o en un registro . Las declaraciones de retorno en muchos lenguajes de programación permiten que una función especifique un valor de retorno que se devolverá al código que llamó a la función.
En C y C++ , (donde es una expresión ) es una declaración que le dice a una función que devuelva la ejecución del programa a la función que llama e informa el valor de . Si una función tiene el tipo de retorno void , la declaración de retorno se puede usar sin un valor, en cuyo caso el programa simplemente sale de la función actual y regresa a la que llama. [1] [2] Se utiliza una sintaxis similar en otros lenguajes, incluidos Modula-2 [3] y Python . [4]return exp;
exp
exp
En Pascal no hay declaración de devolución. Las funciones o procedimientos regresan automáticamente al llegar a su última declaración. El valor de retorno de una función se proporciona dentro de la función realizando una asignación a un identificador con el mismo nombre que la función. [5] Sin embargo, algunas versiones de Pascal proporcionan una función especial que se puede utilizar para devolver un valor inmediatamente desde una función o, sin parámetros, para devolver inmediatamente desde un procedimiento. [6]Exit(exp);
Al igual que Pascal, FORTRAN II , Fortran 66 , Fortran 77 y versiones posteriores de Fortran especifican valores de retorno mediante una asignación al nombre de la función, pero también tienen una declaración de retorno; esa declaración no especifica un valor de retorno y, para una función, hace que se devuelva el valor asignado al nombre de la función. [5] [7] [8]
En algunos otros idiomas se utiliza un parámetro de salida definido por el usuario en lugar del identificador de función. [9]
Oberon ( Oberon-07 ) tiene una cláusula de devolución en lugar de una declaración de devolución. La cláusula de devolución se coloca después de la última declaración del órgano del procedimiento. [10]
Algunos lenguajes de programación orientados a expresiones , como Lisp , Perl y Ruby , permiten al programador omitir una declaración de retorno explícita, especificando en su lugar que la última expresión evaluada es el valor de retorno de la subrutina. En otros casos, se devuelve un valor nulo si no hay una declaración de retorno explícita: en Python , el valor None
se devuelve cuando se omite la declaración de retorno, [4] mientras que en JavaScript undefined
se devuelve el valor.
En Windows PowerShell, todas las expresiones evaluadas que no se capturan (por ejemplo, asignadas a una variable, convertidas en void o canalizadas a $null ) se devuelven desde la subrutina como elementos de una matriz o como un único objeto en el caso de que solo haya un objeto. no ha sido capturado.
En Perl, el valor o valores de retorno de una subrutina pueden depender del contexto en el que se llamó. La distinción más fundamental es un contexto escalar donde el código de llamada espera un valor, un contexto de lista donde el código de llamada espera una lista de valores y un contexto vacío donde el código de llamada no espera ningún valor de retorno. Una subrutina puede verificar el contexto usando la wantarray
función. Se utiliza una sintaxis especial de retorno sin argumentos para devolver un valor indefinido en un contexto escalar y una lista vacía en un contexto de lista. El contexto escalar se puede dividir en contextos booleanos , numéricos, de cadena y de varios tipos de referencia . Además, se puede devolver un objeto sensible al contexto utilizando una secuencia de retorno contextual, con evaluación diferida de valores escalares.
Muchos sistemas operativos permiten que un programa devuelva un resultado (separado de la salida normal ) cuando finaliza su proceso; estos valores se refieren a estados de salida . La cantidad de información que se puede transmitir de esta manera es bastante limitada y, en la práctica, a menudo se limita a señalar el éxito o el fracaso. Desde dentro del programa, este retorno generalmente se logra llamando a Exit (llamada al sistema) (común incluso en C, donde está disponible el mecanismo alternativo de regresar desde la función principal ).
Las declaraciones de devolución vienen en muchas formas. Las siguientes sintaxis son las más comunes:
En algunos lenguajes ensambladores , por ejemplo el de MOS Technology 6502 , se utiliza el mnemotécnico "RTS" (ReTurn from Subroutine).
Los lenguajes con una declaración de retorno explícita crean la posibilidad de múltiples declaraciones de retorno en la misma función. Si eso es algo bueno o no es controvertido.
Los firmes partidarios de la programación estructurada se aseguran de que cada función tenga una única entrada y una única salida (SESE). Por lo tanto, se ha argumentado [14] que se debe evitar el uso de la declaración return explícita excepto al final textual de una subrutina, considerando que, cuando se usa para "regresar temprano", puede sufrir el mismo tipo de problemas. que surgen para la declaración GOTO . Por el contrario, se puede argumentar que vale la pena usar la declaración return cuando la alternativa es un código más complicado, como un anidamiento más profundo, lo que perjudica la legibilidad.
En su libro de texto de 2004, David Watt escribe que "los flujos de control de múltiples salidas y de entrada única suelen ser deseables". Utilizando la noción marco de secuenciador de Tennent , Watt describe uniformemente las construcciones de flujo de control que se encuentran en los lenguajes de programación contemporáneos e intenta explicar por qué ciertos tipos de secuenciadores son preferibles a otros en el contexto de flujos de control de salidas múltiples. Watt escribe que los gotos (secuenciadores de salto) sin restricciones son malos porque el destino del salto no se explica por sí mismo para el lector de un programa hasta que el lector encuentra y examina la etiqueta o dirección real que es el objetivo del salto. Por el contrario, Watt sostiene que la intención conceptual de un secuenciador de retorno queda clara a partir de su propio contexto, sin tener que examinar su destino. Además, Watt escribe que una clase de secuenciadores conocidos como secuenciadores de escape , definidos como "secuenciador que finaliza la ejecución de un comando o procedimiento que incluye texto", abarca tanto las interrupciones de los bucles (incluidas las interrupciones de varios niveles) como las declaraciones de retorno. Watt también señala que si bien los secuenciadores de salto (gotos) han estado algo restringidos en lenguajes como C, donde el objetivo debe ser un bloque interno o un bloque externo envolvente, esa restricción por sí sola no es suficiente para hacer que la intención de los gotos en C sea propia. -describir y así todavía pueden producir " código espagueti ". Watt también examina en qué se diferencian los secuenciadores de excepción de los secuenciadores de escape y salto; Para obtener más detalles sobre esto, consulte el artículo sobre programación estructurada. [15]
Según estudios empíricos citados por Eric S. Roberts , los estudiantes de programación tenían dificultades para formular soluciones correctas para varios problemas simples en un lenguaje como Pascal, que no permite múltiples puntos de salida. Para el problema de escribir una función para buscar linealmente un elemento en una matriz, un estudio de 1980 realizado por Henry Shapiro (citado por Roberts) encontró que usando solo las estructuras de control proporcionadas por Pascal, solo el 20% de los sujetos dieron la solución correcta. , mientras que ningún sujeto escribió código incorrecto para este problema si se le permitió escribir un retorno desde la mitad de un bucle. [dieciséis]
Otros, incluidos Kent Beck y Martin Fowler, argumentan que una o más cláusulas de protección (declaraciones de retorno de "salida anticipada" condicionales cerca del comienzo de una función) a menudo hacen que una función sea más fácil de leer que la alternativa. [17] [18] [19] [20]
El problema más común en la salida anticipada es que las declaraciones finales o de limpieza no se ejecutan; por ejemplo, la memoria asignada no se desasigna o los archivos abiertos no se cierran, lo que provoca fugas. Estos deben realizarse en cada sitio de devolución, ya que es frágil y puede provocar errores fácilmente. Por ejemplo, en un desarrollo posterior, un desarrollador podría pasar por alto una declaración de retorno y una acción que debería realizarse al final de una subrutina (por ejemplo, una declaración de seguimiento ) podría no realizarse en todos los casos. Los idiomas sin declaración de devolución, como el Pascal estándar, no tienen este problema. Algunos lenguajes, como C++ y Python, emplean conceptos que permiten que se realicen acciones automáticamente al regresar (o generar una excepción), lo que mitiga algunos de estos problemas; a menudo se conocen como "intentar/finalmente" o similares. Funcionalidades como estas cláusulas "finalmente" se pueden implementar mediante un paso al único punto de retorno de la subrutina. Una solución alternativa es utilizar el desenrollado normal de la pila (desasignación de variables) al salir de la función para desasignar recursos, como a través de destructores en variables locales, o mecanismos similares como la declaración "with" de Python.
Algunas implementaciones tempranas de lenguajes como el Pascal y C originales restringieron los tipos que una función puede devolver (por ejemplo, no admiten tipos de registros o estructuras ) para simplificar sus compiladores .
En Java (y lenguajes similares modelados a partir de él, como JavaScript ), es posible ejecutar código incluso después de la declaración return, porque el bloque finalmente de una estructura try-catch siempre se ejecuta. Entonces, si la declaración de retorno se coloca en algún lugar dentro de los bloques try o catch , se ejecutará el código finalmente (si se agrega). Incluso es posible alterar el valor de retorno de un tipo no primitivo (una propiedad de un objeto ya devuelto) porque la salida también ocurre después. [21]
Las declaraciones de prima a devolución son declaraciones de rendimiento : donde una devolución provoca que finalice una subrutina , un rendimiento hace que se suspenda una corutina . La corrutina continuará posteriormente desde donde se suspendió si se vuelve a llamar. Las corrutinas son mucho más complicadas de implementar que las subrutinas y, por lo tanto, las declaraciones de rendimiento son menos comunes que las declaraciones de retorno, pero se encuentran en varios idiomas.
Son posibles varias secuencias de llamada/retorno según el conjunto de instrucciones del hardware, incluidas las siguientes:
CALL
instrucción envía la dirección de la siguiente instrucción a la pila y se ramifica a la dirección especificada. La RETURN
instrucción extrae la dirección de retorno de la pila al puntero de instrucción y la ejecución se reanuda en esa dirección. (Ejemplos: x86 , PDP-11 ) En arquitecturas como Motorola 96000 , el área de la pila se puede asignar en un espacio de direcciones separado, que se denomina 'Espacio de memoria de la pila', [22] distinto del espacio de direcciones de la memoria principal. [23] El NEC μPD7720 también cuenta con una pila con su propio espacio de direcciones separado. [24]CALL
instrucción coloca la dirección de la siguiente instrucción en un registro y se ramifica a la dirección especificada. La RETURN
secuencia de instrucciones coloca la dirección de retorno del registro en el puntero de instrucción y la ejecución se reanuda en esa dirección. (Ejemplos: IBM System/360 y sucesores hasta z/Architecture , la mayoría de las arquitecturas RISC )CALL
instrucción coloca la dirección de la siguiente (o actual ) instrucción en la ubicación de almacenamiento en la dirección de llamada y se ramifica a la dirección especificada+1. La RETURN
secuencia de instrucciones se bifurca a la dirección de retorno mediante un salto indirecto a la primera instrucción de la subrutina. (Ejemplos: IBM 1130 , SDS 9XX , PDP-8 ){{cite book}}
: Mantenimiento CS1: nombres numéricos: lista de autores ( enlace ){{cite book}}
: Mantenimiento CS1: nombres numéricos: lista de autores ( enlace )... mentalidad de un punto de salida ... No sigo la regla sobre un punto de salida de un método.