Estaba tratando de responder la pregunta this. Según lo sugerido por la respuesta aceptada, el problema con ese código es que no todas las rutas de control devuelven un valor. Probé este código en el compilador VC9 y me dio una advertencia sobre lo mismo. Mi pregunta es: ¿por qué es solo una advertencia y no un error? Además, en caso de que se ejecute la ruta que no devuelve un valor, ¿qué devolverá la función (tiene que devolver algo)? ¿Es solo lo que hay encima de la pila o es el temido comportamiento indefinido otra vez?¿Por qué "no todas las rutas de control devuelven un valor" es advertencia y no es un error?
Respuesta
No devolver un valor de una función que tiene un tipo de devolución no void
da como resultado un comportamiento indefinido, pero no es un error semántico.
La razón de esto, hasta donde puedo determinar, es en gran parte histórica.
C originalmente no tenía void
e implícita int
significaba que la mayoría de las funciones devuelven un int
menos que se declare explícitamente a volver otra cosa, aunque no había ninguna intención de utilizar el valor de retorno.
Esto significa que muchas funciones devolvieron un int pero sin establecer explícitamente un valor de retorno, pero eso estaba bien porque las personas que llaman nunca usarían el valor de retorno para estas funciones.
Algunas funciones devolvieron un valor, pero utilizaron implícitamente int
porque int
era un tipo de devolución adecuado.
Esto significa que el código pre void
tenía un montón de funciones que devuelven nominalmente int
pero que podrían ser declaradas para volver void
y muchas otras funciones que debe devolver un int
y no hay forma clara a notar la diferencia. La aplicación de return
en todas las rutas de código de todas las funciones que no sean void
en cualquier etapa rompería el código heredado.
También existe el argumento de que algunas rutas de código en una función pueden ser inalcanzables, pero esto puede no ser fácil de determinar a partir de un análisis estático simple, entonces ¿por qué imponer un innecesario return
?
El análisis estático se puede explicar fácilmente si se piensa en 'cambiar', mientras que se recomienda encarecidamente que proporcione un 'valor predeterminado', no es necesario y puede que no sea necesario ... pero ¿cómo se supone que el compilador lo sabe? ? Por otro lado ... debes tratar las advertencias como errores al compilar, es mejor. –
Es una advertencia muy útil cuando realmente es un error y se le olvidó el retorno, uno que me ha sorprendido varias veces y las advertencias como errores al menos para este en particular le aseguran que no lo pase por alto. – CashCow
No es un error porque puede ser el comportamiento previsto. Por ejemplo, algunas bibliotecas de encriptación usan datos locales no inicializados como un paso para la siembra. Como los valores de retorno se guardan en convenciones de llamadas y ubicaciones específicas de plataforma, esto puede ayudar en algunas situaciones inusuales (como las anteriores). En este caso, la función devuelve lo que queda en el registro utilizado para devolver el valor devuelto.
¿Puede citar un ejemplo específico de una biblioteca de cifrado que utiliza datos no inicializados como siembra? Parece muy poco probable que una biblioteca competente haga eso ya que los datos no inicializados no son aleatorios en términos criptográficos. –
¿De verdad? ¿Qué bibliotecas de cifrado hacen eso? Confiar en un comportamiento indefinido en una biblioteca de cifrado parece un gran agujero de seguridad potencial. –
Algún día preferiría un número aleatorio generado adecuadamente en lugar de datos no inicializados como una semilla. Si utiliza el depurador MSVC++, verá regularmente que los datos no inicializados tienen valores fijos (en la versión de depuración del programa 0xCCCCCCCC, 0xCDCDCDCD). –
Supongo que es solo una advertencia porque el compilador no siempre puede estar 100% seguro de que es posible no golpear una devolución.
es decir, si tiene que:
-= source1.c =-
int func()
{
if(doSomething())
{
return 0;
}
}
-= source2.c =-
int doSomething()
{
return 1;
}
El compilador en este caso podría no ser capaz de saber que siempre llegará a la devolución, pero sí. Por supuesto, esta es una terrible práctica de programación para confiar en saber cómo funciona el código externo.
En cuanto a lo que realmente se devolverá depende de la plataforma. En los ABI x86, EAX se utiliza para el valor de retorno (hasta 32 bits), por lo que devolverá lo que se haya colocado en ese registro (que podría ser un retorno de otra cosa, un valor temporal o una pérdida total).
En este caso, ¿por qué incluso tiene una declaración if? Este código es simplemente incorrecto y no debe compilarse. Es una advertencia por razones históricas, compatibilidad con versiones anteriores y demás. Ahora C++ debería descartar esto y manejarlo como un error. – Melkon
aquí es otra razón por la que no es un error
la siguiente información le dará la misma advertencia ya que el compilador espera que devuelva algo del bloque catch a pesar de que usted está lanzando no
int foo(){
try{
return bar(0);
} catch(std::exception& ex){
//do cleanup
throw ex;
}
}
int bar(unsigned int i){
if(i == 0){
throw std::string("Value must be greater than 0");
} else{
return 0;
}
}
No espera que regrese si está lanzando, al menos no lo hace VC9, no sé sobre otros compiladores. Sin embargo, si llamas a una función que siempre arroja (a propósito, por supuesto, tienes una rutina que compila mensajes de error y lanza) y la llamas y el compilador no puede "verla", obtienes la advertencia. Por lo tanto, mi solución a continuación. – CashCow
Técnicamente no se garantiza que sea un error si llama a una función y esa función siempre arroja una excepción. Por ejemplo, aquí hay un pseudo código, y sabes que raiseError siempre arroja.
MyClass func(params)
{
if(allIsValid())
{
return myObject;
}
else
{
raiseError(errorInfo);
}
}
Si el compilador no puede ver la implementación de raiseError, no va a saber que la función va a lanzar. Entonces realmente no hay un comportamiento indefinido aquí. Por supuesto, es bueno silenciar el compilador aquí, lo cual se puede hacer escribiendo una declaración de devolución "ficticia" después de raiseError, o un "throw" ficticio. Los llamo "tontos" porque nunca serán alcanzados en la realidad. (También puede suprimir la advertencia si realmente insiste). Sin embargo, no hay ningún error o comportamiento indefinido.
cuenta la situación siguiente:
UINT GenderID(GENDER gender)
{
switch(gender)
{
case MALE:
return MALE_ID;
case FEMALE:
return FEMALE_ID;
}
// default not required because [GENDER] in our 'Matrix' CAN be either M or F
}
un compilador C++ debe permitirá tiene su 'matriz' a su manera; Por lo tanto, no es un error.
Básicamente dice que no es un error porque se le ocurrió un ejemplo artificial que cree que debería funcionar. – Andy
@Andy como ** no es un error ** - no tengo que demostrarlo. Además de que mi ejemplo es muy real, y el objetivo es presentar una situación en la que declararlo, un error sería un error en sí mismo. –
No responde * por qué * está permitido en primer lugar, que es lo que el OP desea saber. Su respuesta es una tautología y solo proporciona otro ejemplo de lo que el OP está preguntando, no responde la pregunta. Lo único que puedo deducir de su declaración de respuesta es que cree que el código debe compilarse porque debería 'dejar que lo haga a su manera'. – Andy
Otro ejemplo en el que puede ser un problema para algunas rutas de control que no devuelven un valor:
enum E : int {A, B};
int foo(E e) {
switch (e) {
case A: return 30;
case B: return 50;
}
}
Es posible que e
no habrá A
o B
, pero la implicación es que siempre será uno de esos valores. Si ese es el caso, entonces el código está bien y no hay problema. Convertir esta advertencia en un error obligatorio requeriría un desorden innecesario e "inalcanzable".
Si desea que la advertencia sea un error de todos modos, puede configurar su compilador para hacer eso con un indicador como/WX o -Werror. Aunque, por supuesto, debe tener en cuenta que los diferentes compiladores pueden hacer diferentes determinaciones sobre lo que no se puede alcanzar, por lo que puede estar arreglando cosas diferentes para compiladores diferentes.
- 1. ¿Por qué este código async/await genera "... no todas las rutas de código devuelven un valor"?
- 2. ¿Por qué "No todas las rutas de código devuelven un valor" con una instrucción switch y una enumeración?
- 3. interruptor + enumeración = no todas las rutas de código devuelven un valor
- 4. ¿En qué caso, esta función no devolverá un valor? ¿Por qué el compilador informa un error?
- 5. ¿Por qué Array.Length es un int, y no es un error
- 6. ¿Por qué obtengo un objeto no es un error iterable?
- 7. Las banderas en VB6 no devuelven un valor correcto
- 8. ¿Por qué System.Enum no es un tipo de valor?
- 9. ¿Por qué no es un error de sintaxis?
- 10. ¿Por qué estas dos funciones no devuelven el mismo valor?
- 11. VB.Net ¿por qué no es esto un error?
- 12. objeto no es un error de valor en la Scala
- 13. ¿Puedo tratar una advertencia específica como un error?
- 14. ¿Por qué readf no devuelve un valor?
- 15. ¿Por qué no es necesario asignar el valor de retorno de un método a una variable?
- 16. ¿Por qué `$ @` no es confiable?
- 17. ¿Por qué no Entity Framework consultas devuelven entidades no guardados
- 18. ¿Por qué es uno Func válida y la otra (casi idéntico) no
- 19. no es un valor válido AllXsd
- 20. ¿Por qué sqrt() no es un método en Numérico?
- 21. ¿Por qué no es un método no Guid.IsNullOrEmpty()
- 22. ¿Por qué no es un patrón de ancho fijo?
- 23. ¿Qué es un Rack - error de "no aceptador"?
- 24. ¿Es 0x9B (155decimal) un personaje de control especial? ¿Por qué falta en las tablas ascii?
- 25. ¿Qué es un yeso no revisado y cómo lo verifico?
- 26. g ++ "no es un tipo de" error
- 27. ¿Por qué un método no es idéntico a sí mismo?
- 28. ¿Por qué no es este un tipo de POD?
- 29. ¿Por qué (a, a) no es un funtor?
- 30. ¿Qué es un error de dominio
La mayoría de los lugares consideran las advertencias como errores. En consecuencia, la mayoría de los compiladores tienen una bandera que le dice al compilador que trate las advertencias como errores. Nunca he tenido un trabajo (aparte de uno trabajando en código heredado) donde no encendimos esta bandera e insistimos en 0 advertencias antes de registrarnos. –
Y 0 errores, solo en caso de que el pensamiento no estuviera implícito. –