¿Por qué el flujo del final de una función no nula sin devolver un valor no produce un error de comstackción?

Desde que me di cuenta hace muchos años, que esto no produce un error por defecto (al menos en GCC), siempre me he preguntado por qué?

Entiendo que puede emitir indicadores de comstackción para producir una advertencia, pero ¿no debería ser siempre un error? ¿Por qué tiene sentido que una función no nula no devuelva un valor para ser válida?

Un ejemplo como se solicita en los comentarios:

#include  int stringSize() { } int main() { char cstring[5]; printf( "the last char is: %c\n", cstring[stringSize()-1] ); return 0; } 

… comstack

Los estándares C99 y C ++ no requieren funciones para devolver un valor. La statement de devolución faltante en una función de devolución de valor se definirá (para devolver 0 ) solo en la función main .

El razonamiento incluye que comprobar si cada ruta de código devuelve un valor es bastante difícil, y se puede establecer un valor de retorno con el ensamblador incorporado u otros métodos complicados.

Del borrador C ++ 11 :

§ 6.6.3 / 2

Salir del final de una función […] produce un comportamiento indefinido en una función de devolución de valor.

§ 3.6.1 / 5

Si el control llega al final de main sin encontrar una instrucción return , el efecto es el de ejecutar

 return 0; 

Tenga en cuenta que el comportamiento descrito en C ++ 6.6.3 / 2 no es el mismo en C.


gcc le dará una advertencia si lo llama con la opción -Wreturn-type.

-Wreturn-type Avisa siempre que una función se define con un tipo de retorno que por defecto es int. También advertir acerca de cualquier statement de retorno sin valor de retorno en una función cuyo tipo de retorno no es nulo (caerse al final del cuerpo de la función se considera regresar sin un valor), y sobre un enunciado de retorno con una expresión en una función cuya return-type es nulo.

Esta advertencia es habilitada por -Wall .


Solo como curiosidad, mira lo que hace este código:

 #include  int foo() { int a = 5; int b = a + 1; } int main() { std::cout << foo() << std::endl; } // may print 6 

Este código tiene un comportamiento formalmente indefinido, y en la práctica depende de la convención y la architecture . En un sistema en particular, con un comstackdor particular, el valor de retorno es el resultado de la evaluación de la última expresión, almacenada en el registro eax del procesador de ese sistema.

gcc no comprueba por defecto que todas las rutas de códigos devuelvan un valor porque, en general, esto no se puede hacer. Asume que sabes lo que estás haciendo. Considere un ejemplo común usando enumeraciones:

 Color getColor(Suit suit) { switch (suit) { case HEARTS: case DIAMONDS: return RED; case SPADES: case CLUBS: return BLACK; } // Error, no return? } 

Usted, el progtwigdor, sabe que, salvo un error, este método siempre devuelve un color. gcc confía en que sabes lo que estás haciendo para que no te obligue a poner un retorno en la parte inferior de la función.

javac, por otro lado, intenta verificar que todas las rutas de códigos devuelvan un valor y arroje un error si no puede probar que todas lo hacen. Este error es obligatorio por la especificación del lenguaje Java. Tenga en cuenta que a veces es incorrecto y debe ingresar una statement de devolución innecesaria.

 char getChoice() { int ch = read(); if (ch == -1 || ch == 'q') { System.exit(0); } else { return (char) ch; } // Cannot reach here, but still an error. } 

Es una diferencia filosófica. C y C ++ son lenguajes más permisivos y confiables que Java o C # y, por lo tanto, algunos errores en los idiomas más nuevos son advertencias en C / C ++ y algunas advertencias se ignoran o desactivan de manera predeterminada.

¿Te refieres a por qué el hecho de salir del final de una función de devolución de valor (es decir, salir sin un return explícito) no es un error?

En primer lugar, en C, si una función devuelve algo significativo o no, solo es crítica cuando el código de ejecución realmente utiliza el valor devuelto. Tal vez el idioma no quería forzarte a devolver nada cuando sabes que no vas a usarlo de todos modos la mayor parte del tiempo.

En segundo lugar, aparentemente la especificación del lenguaje no quería obligar a los autores del comstackdor a detectar y verificar todas las posibles rutas de control para la presencia de un return explícito (aunque en muchos casos esto no es tan difícil de hacer). Además, algunas rutas de control pueden conducir a funciones que no retornan : el rasgo generalmente desconocido por el comstackdor. Tales rutas pueden convertirse en una fuente de falsos positivos molestos.

Tenga en cuenta también que C y C ++ difieren en sus definiciones del comportamiento en este caso. En C ++, el simple hecho de salir del final de una función de retorno de valor siempre es un comportamiento indefinido (independientemente de si el código de llamada utiliza el resultado de la función). En C, esto provoca un comportamiento indefinido solo si el código de llamada intenta usar el valor devuelto.

Es legal en C / C ++ no regresar de una función que dice devolver algo. Hay una serie de casos de uso, como llamar a exit(-1) , o una función que lo llama o arroja una excepción.

El comstackdor no va a rechazar el C ++ legal, incluso si lleva a UB si usted le pide que no lo haga. En particular, no pide que se generen advertencias . (Gcc todavía se enciende por defecto, pero cuando se agregan, parece que se alinean con nuevas funciones y no con nuevas advertencias para funciones antiguas)

Cambiar el gcc no-arg predeterminado para emitir algunas advertencias podría ser un cambio radical para los scripts existentes o los sistemas make. Los bien diseñados, ya sea que se -Wall una -Wall y se ocupan de las advertencias, o alternan las advertencias individuales.

Aprender a utilizar una cadena de herramientas de C ++ es una barrera para aprender a ser un progtwigdor de C ++, pero las cadenas de herramientas de C ++ suelen ser escritas por y para expertos.

¿Bajo qué circunstancias no produce un error? Si declara un tipo de devolución y no devuelve algo, me parece un error.

La única excepción en la que puedo pensar es la función main() , que no necesita ninguna statement de return (al menos en C ++; no tengo a mano ninguno de los estándares C). Si no hay devolución, actuará como si return 0; es la última statement.

Parece que necesitas mostrar tus advertencias de comstackción:

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

Creo que esto se debe al código heredado (C nunca requirió statement de retorno, así lo hizo C ++). Probablemente hay una gran base de código que depende de esa “característica”. Pero al menos hay -Werror=return-type bandera en muchos comstackdores (incluyendo gcc y clang).

Es una violación de restricción en c99, pero no en c89. Contraste:

c89:

3.6.6.4 La statement de return

Restricciones

Una statement de return con una expresión no aparecerá en una función cuyo tipo de devolución sea void .

c99:

6.8.6.4 La statement de return

Restricciones

Una statement de return con una expresión no aparecerá en una función cuyo tipo de devolución sea void . Una statement de return sin una expresión solo aparecerá en una función cuyo tipo de devolución sea void .

Incluso en modo --std=c99 , gcc solo lanzará una advertencia (aunque sin necesidad de habilitar indicadores -W adicionales, como se requiere por defecto o en c89 / 90).

Edite para agregar que en c89, “alcanzar el } que termina una función es equivalente a ejecutar una statement de return sin una expresión” (3.6.6.4). Sin embargo, en c99 el comportamiento no está definido (6.9.1).