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 compilación para generar 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álido?
Un ejemplo según lo solicitado en los comentarios:
#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á solo esa advertencia como un error. Simplemente ignoré la advertencia y el par de minutos de frustración que encontraron unthis
puntero inválido me llevaron aquí y a esta conclusión.std::optional
función sin regresar devuelve un "verdadero" opcionalRespuestas:
Los estándares C99 y C ++ no requieren funciones para devolver un valor. La declaración de retorno faltante en una función de retorno de valor se definirá (para devolver
0
) solo en lamain
función.La lógica incluye que verificar si cada ruta de código devuelve un valor es bastante difícil, y un valor de retorno podría establecerse con un ensamblador incorporado u otros métodos complicados.
Del borrador de C ++ 11 :
§ 6.6.3 / 2
§ 3.6.1 / 5
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.
Solo como curiosidad, mira lo que hace este código:
Este código tiene un comportamiento formalmente indefinido y, en la práctica, llama a convenciones y depende de la arquitectura . En un sistema particular, con un compilador particular, el valor de retorno es el resultado de la última evaluación de expresión, almacenada en el
eax
registro del procesador de ese sistema.fuente
throw
olongjmp
, ¿debe el compilador requerir un acceso inalcanzablereturn
después de la llamada a la función que no regresa? El caso en que no es necesario no es muy común, y el requisito de incluirlo incluso en tales casos probablemente no hubiera sido oneroso, pero la decisión de no requerirlo es razonable.gcc no verifica por defecto que todas las rutas de código 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:
Usted, el programador, sabe que, salvo un error, este método siempre devuelve un color. gcc confía en que sabes lo que estás haciendo, por lo que no te obliga a poner un retorno en la parte inferior de la función.
javac, por otro lado, intenta verificar que todas las rutas de código devuelvan un valor y arroja 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 está mal y debe ingresar una declaración de devolución innecesaria.
Es una diferencia filosófica. C y C ++ son lenguajes más permisivos y confiables que Java o C #, por lo que algunos errores en los lenguajes más nuevos son advertencias en C / C ++ y algunas advertencias se ignoran o desactivan de manera predeterminada.
fuente
System.exit()
nunca regresa.System.exit()
nunca regresa. Lo busqué ( java.sun.com/j2se/1.4.2/docs/api/java/lang/… ), y los documentos simplemente dicen que "normalmente nunca regresa". Me pregunto lo que eso significa ...Es decir, ¿por qué fluir del final de una función de retorno de valor (es decir, salir sin un explícito
return
) no es un error?En primer lugar, en C si una función devuelve algo significativo o no solo es crítico cuando el código de ejecución realmente usa el valor devuelto. Tal vez el lenguaje no quería forzarlo a devolver nada cuando sabe que no lo va a usar de todos modos la mayor parte del tiempo.
En segundo lugar, al parecer, la especificación del lenguaje no quería obligar a los autores del compilador a detectar y verificar todas las rutas de control posibles para la presencia de un código explícito
return
(aunque en muchos casos esto no es tan difícil de hacer). Además, algunas rutas de control pueden conducir a funciones que no regresan , el rasgo que el compilador generalmente no conoce. Tales caminos 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 ++, simplemente fluir al 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 utilizar el valor devuelto.
fuente
return
declaraciones desde el final demain()
?main
es especial en ese sentido.Es legal bajo C / C ++ no regresar de una función que dice devolver algo. Hay varios casos de uso, como llamar
exit(-1)
o una función que lo llama o lanza una excepción.El compilador no va a rechazar C ++ legal incluso si conduce a UB si le está pidiendo que no lo haga. En particular, solicita que no se generen advertencias . (Gcc todavía activa algunos de forma predeterminada, pero cuando se agregan, estos parecen alinearse con las nuevas funciones, no con nuevas advertencias para las funciones antiguas)
Cambiar el gcc predeterminado sin argumentos para emitir algunas advertencias podría ser un cambio importante para los scripts existentes o los sistemas de fabricación. Bien diseñados, ya sea
-Wall
con advertencias o alternar advertencias individuales.Aprender a usar una cadena de herramientas C ++ es una barrera para aprender a ser un programador C ++, pero las cadenas de herramientas C ++ generalmente están escritas por y para expertos.
fuente
Makefile
lo tengo funcionando-Wall -Wpedantic -Werror
, pero este fue un script de prueba único al que olvidé proporcionar los argumentos.-Wduplicated-cond
formar parte de la-Wall
rutina de arranque GCC rota. Algunas advertencias que parecen apropiadas en la mayoría de los códigos no lo son en todos los códigos. Es por eso que no están habilitados por defecto.int foo() { exit(-1); }
no regresaint
de una función que "dice devolver int". Esto es legal en C ++. Ahora, no devuelve nada ; el final de esa función nunca se alcanza. En realidad, llegar al final defoo
sería un comportamiento indefinido. Ignorando los casos de fin de proceso,int foo() { throw 3.14; }
también afirma regresarint
pero nunca lo hace.void* foo(void* arg) { pthread_exit(NULL); }
está bien por la misma razón (cuando su único uso es a través depthread_create(...,...,foo,...);
)Creo que esto se debe al código heredado (C nunca requirió la declaración de retorno, al igual que C ++). Probablemente hay una enorme base de código que se basa en esa "característica". Pero al menos hay una
-Werror=return-type
bandera en muchos compiladores (incluidos gcc y clang).fuente
En algunos casos limitados y raros, fluir del final de una función no nula sin devolver un valor podría ser útil. Como el siguiente código específico de MSVC:
Esta función devuelve pi usando el ensamblaje x86. A diferencia del ensamblaje en GCC, no conozco ninguna forma
return
de hacerlo para esto sin involucrar gastos generales en el resultado.Hasta donde yo sé, los compiladores de C ++ convencionales deberían emitir al menos advertencias para código aparentemente inválido. Si hago que el cuerpo esté
pi()
vacío, GCC / Clang informará una advertencia y MSVC informará un error.La gente mencionó excepciones y
exit
en algunas respuestas. Esas no son razones válidas. O bien lanzar una excepción, o llamarexit
, será no hacer que la ejecución de la función fluya fuera de la final. Y los compiladores lo saben: escribir una declaración de lanzamiento o llamarexit
al cuerpo vacío depi()
detendrá cualquier advertencia o error de un compilador.fuente
void
función después de que asm en línea deja un valor en el registro de valor de retorno. (x87st0
en este caso, EAX para entero. Y tal vez xmm0 en una convención de llamada que devuelve flotante / doble en xmm0 en lugar de st0). La definición de este comportamiento es específica de MSVC; ni siquiera sonar-fasm-blocks
para soportar la misma sintaxis hace que esto sea seguro. Ver ¿Tiene __asm {}; devolver el valor de eax?¿En qué circunstancias no produce un error? Si declara un tipo de retorno y no devuelve algo, me parece un error.
La única excepción que se me ocurre es la
main()
función, que no necesita unareturn
declaración (al menos en C ++; no tengo ninguno de los estándares de C a la mano). Si no hay devolución, actuará como si fuerareturn 0;
la última declaración.fuente
main()
necesita unareturn
en C.return <value>;
declaraciónreturn 0;
al final demain()
(ymain()
solo). Pero es un buen estilo agregar dereturn 0;
todos modos.Parece que necesitas subir las advertencias de tu compilador:
fuente
return;
en una función no nula, y es una violación de restricción usarreturn <value>;
en una función nula. Pero ese, creo, no es el tema. El OP, como lo entendí, se trata de salir de una función no nula sin unreturn
(solo permitiendo que el control fluya desde el final de la función). No es una violación de restricción, AFAIK. El estándar solo dice que siempre es UB en C ++ y, a veces, UB en C.Es una violación de restricción en c99, pero no en c89. Contraste:
c89:
c99:
Incluso en
--std=c99
modo, gcc solo lanzará una advertencia (aunque sin necesidad de habilitar-W
banderas adicionales , como se requiere por defecto o en c89 / 90).Edite para agregar eso en c89, "alcanzar el
}
que termina una función es equivalente a ejecutar unareturn
declaración sin una expresión" (3.6.6.4). Sin embargo, en c99 el comportamiento es indefinido (6.9.1).fuente
return
declaraciones explícitas . No cubre la caída del final de una función sin devolver un valor.