Pregunta

Ayer, me encontré escribiendo código como este:

SomeStruct getSomeStruct()
{
    SomeStruct input;

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

Por supuesto, olvidando devolver la estructura que acabo de crear. Por extraño que parezca, los valores en la estructura que fue devuelto por esta función se inicializaron a cero (cuando se compila usando g ++). ¿Es solo una coincidencia o se creó y se inicializó otro SomeStruct en algún lugar de manera implícita?

¿Fue útil?

Solución

  

¿Se creó e inicializó otro SomeStruct en algún lugar de manera implícita?

Piensa en cómo se devuelve la estructura. Si tanto x como 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 un Arquitectura de 64 bits (la respuesta de @Denton Gentry menciona cómo se devuelven los valores más simples), por lo que debe asignarse en algún lugar. Sería un desperdicio usar 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.

El compilador, en cambio, hace que la persona que llama le diga 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), al pasarle a la función llamada un puntero oculto al espacio asignado. Entonces, el lugar donde se establece en cero es en la persona que llama , no en la función getSomeStruct .

También hay optimizaciones como la "optimización de retorno de valor con nombre" donde se pueden elidir copias adicionales. Entonces, si hubiera utilizado el return faltante, el resultado se crearía directamente en el espacio asignado por la persona que llama, en lugar de crear un temporal y copiarlo.

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

Otros consejos

La caída del final de una función que se declara que devuelve un valor (sin devolver explícitamente un valor) conduce a consecuencias indefinidas. Para gcc, debe comenzar con el interruptor de línea de comando -Wall que activa las advertencias más útiles. La advertencia específica de gcc que controla la advertencia que desea es -Wreturn-type (que se incluye en -Wall , solo menciono esto para completar).

Una vez que haya activado las advertencias, también debe usar -Werror para tratar las advertencias como errores y hacer que la compilación se detenga en el momento en que detecte un error.

Las convenciones de llamada para la mayoría de las arquitecturas de CPU modernas especifican un registro particular para pasar un valor de retorno de función al llamante. La persona que llama realiza la llamada a la función y luego utiliza el registro especificado como valor de retorno. Si no devuelve un valor explícitamente, la persona que llama utilizará la basura que se encuentre 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 misceláneo dentro de la función. Por lo tanto, cuando olvida especificar un valor de retorno, no es inusual encontrar que el valor correcto se ha devuelto milagrosamente a la persona que llama: el compilador utilizó 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 de retorno se convierte en basura real.

Me parece interesante. Con las opciones predeterminadas utilizadas, los siguientes compiladores tienen el siguiente comportamiento al compilar la función GetSomeStruct () :

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

    error C4716: 'getSomeStruct': debe devolver un valor

  • Marte digital:

    Advertencia 18: el retorno implícito de getSomeStruct en el cierre '}' no devuelve el valor

  • Comeau:

    advertencia: falta la declaración de retorno al final de la función no nula " getSomeStruct "

  • gcc:

    sin error ni advertencia

Dado el siguiente par de oraciones del estándar (6.6.3 Párrafo 2):

  

Una declaración de devolución sin un   la expresión solo se puede usar en   funciones que no devuelven un valor,   es decir, una función con el retorno   tipo void, un constructor (12.1) o un   destructor (12.4). ... fluyendo   el final de una función es equivalente a   un retorno sin valor; esto resulta   en comportamiento indefinido en una   función de retorno de valor.

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

Para mí, el compilador no lo permitió: http://codepad.org/KkzVCesh

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

Sin embargo, creo que probablemente obtuviste 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 bien ¿debido a ceros en la pila?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top