2008-11-16 19 views
14

Ayer, me encontré escribiendo código como este:¿Qué sucede si no devuelve un valor en C++?

SomeStruct getSomeStruct() 
{ 
    SomeStruct input; 

    cin >> input.x; 
    cin >> input.y; 
} 

Por supuesto olvido para volver realidad la estructura que acaba de crear. Curiosamente, los valores en la estructura que fueron devueltos por esta función se inicializaron a cero (cuando se compila usando g ++). ¿Es solo una coincidencia o se creó otro SomeStruct y se inicializó en algún lugar implícitamente?

Respuesta

7

¿Se creó e inicializó en alguna parte implícitamente SomeStruct?

Piensa en cómo se devuelve la estructura. Si ambos x y y son 32 bits, es demasiado grande para caber en un registro en una arquitectura de 32 bits, y lo mismo se aplica a los valores de 64 bits en una arquitectura de 64 bits (@Denton Gentry explica cómo son los valores más simples devuelto), por lo que debe asignarse en algún lugar. Sería un desperdicio utilizar el montón para esto, por lo que debe asignarse en la pila. Pero no puede estar en el marco de la pila de su función getSomeStruct, ya que eso ya no es válido después de que la función regrese.

En su lugar, el compilador indica a la función llamada dónde colocar el resultado (que probablemente esté en algún lugar de la pila de la persona que llama), pasando la función llamada un puntero oculto al espacio asignado. Por lo tanto, el lugar donde se establece en cero está en la llamada , no en su función getSomeStruct.

También hay optimizaciones como la "optimización del valor nominal de devolución" donde se pueden eliminar copias adicionales.Por lo tanto, si hubiera utilizado el return que falta, el resultado se creará directamente en el espacio asignado por la persona que llama, en lugar de crear un archivo temporal y copiarlo.

Para saber más sobre lo que está sucediendo, debe mirar la función de llamante. ¿Está inicializando (a cero) un SomeStruct "vacío" al que luego asigna el valor de retorno de su función getSomeStruct? ¿O está haciendo otra cosa?

2

Para mí el compilador no lo permitía: http://codepad.org/KkzVCesh

+0

Parece que no lo permitió porque tiene el compilador configurado para que las advertencias se traten como errores. ¿Crees que este podría ser el caso de que yo tenga un nivel de advertencia demasiado bajo en algún lugar? –

20

La caída fuera de la final de una función que se declara para devolver un valor (sin devolver un valor explícitamente) conduce a consecuencias no definidos. Para gcc, debe comenzar con el interruptor de línea de comando -Wall que enciende la mayoría de las advertencias útiles. La advertencia de gcc específica que controla la advertencia que desea es -Wreturn-type (que se incluye en -Wall, solo menciono esto para que esté completa).

Una vez que haya activado las advertencias, también debe usar -Werror para tratar las advertencias como errores y detener la compilación en el punto donde detecta un error.

+1

Siempre me ha sorprendido que esto sea solo una advertencia en lugar de un error. Duele mucho más a menudo de lo que ayuda. –

+0

Era legal no devolver nada en C. Aún no estaba definido, pero al menos era legal. – Jonathan

+0

En particular, si me olvido de devolver un 'std :: vector' u otro objeto que gestione alguna memoria Heap, el objeto quedará en un estado inválido y por lo tanto no sabrá cómo destruirse a sí mismo cuando llegue el momento. Esto hace que la sesión de depuración sea difícil. – Bernard

8

Las convenciones de llamada para las arquitecturas de CPU más modernas especifican un registro particular para pasar un valor de retorno de función al llamador. El llamante realiza la llamada a la función, y luego utiliza el registro especificado como el valor de retorno. Si no devuelve explícitamente un valor, la persona que llama usará, sin embargo, la basura que esté en ese registro.

El compilador también usará todos los registros que tenga disponibles para el cálculo interno dentro de la función. El registro designado para mantener el valor de retorno también se usará para el cálculo incorrecto dentro de la función. Por lo tanto, cuando olvida especificar un valor de retorno, no es inusual encontrar que el valor correcto ha sido devuelto milagrosamente a la persona que llama: el compilador usó ese registro para almacenar su objeto.

Desafortunadamente, incluso un cambio trivial en la función puede hacer que cambie la asignación del registro, por lo que el valor devuelto se convierte en basura verdadera.

1

No recibió ninguna advertencia porque no tenía activado -Wall -Werror. (Como se indica en otras respuestas)

Sin embargo, creo que probablemente obtuvo una estructura llena de cero como resultado porque el objeto de pila se construyó por defecto en la función de llamada, posiblemente con argumentos cero explícitos, o debido a ceros en el ¿apilar?

3

Me parece interesante. Con las opciones por defecto utilizado, los siguientes compiladores tienen el siguiente comportamiento cuando se compila la función GetSomeStruct():

  • Microsoft VC, todas las versiones (desde VC6 de todos modos):

    error C4716: 'getSomeStruct' : must return a value

  • digital Marte :

    Warning 18: implied return of getSomeStruct at closing '}' does not return value

  • Comeau:

    warning: missing return statement at end of non-void function "getSomeStruct"

  • gcc:

    ningún error o advertencia

Dado el siguiente par de frases de la norma (6.6. 3 Párrafo 2):

Una instrucción de retorno sin una expresión se puede utilizar sólo en funciones que no devuelven un valor, es decir, una función con el tipo de retorno void , un constructor (12.1), o un destructor (12.4). ... Salir de al final de una función es equivalente a una devolución sin valor; esto da como resultado en comportamiento indefinido en una función de devolución de valor .

Yo diría que hay pocas razones para que un compilador no dé un error en este caso. ¿Por qué tantos compiladores dan solo una advertencia o ningún diagnóstico en absoluto?

+0

Objeto a la afirmación de que GCC no advierte sobre esto. Ejecute gcc con la opción '-Wall' y obtendrá:' warning: no return statement in function return non-void [-Wreturn-type] ' – Loomchild

Cuestiones relacionadas