¿Por qué los estándares de C le permiten no devolver un valor de una función?

7 minutos de lectura

¿Por que los estandares de C le permiten no devolver
usuario8426277

He compilado y ejecutado con éxito el siguiente código en gcc:

#include <stdio.h>

int foo()
{

}

int main()
{
    int i = 12345;
    i = foo();
    printf("i is: %d", i);
}

La salida es:

i is: 0

Entonces gcc me permitió no regresar de la función. foo() y hecho foo() regreso 0.

¿Este comportamiento solo se aplica a gcc o también lo tienen otros estándares de C (según tengo entendido, gcc no se ajusta a ningún estándar de C)?

  • Los diseñadores de compiladores tienen dos opciones. Rechace algún código (válido) porque el compilador no puede probar devuelve un valor a pesar de que lo hace. O permitir que el código nunca devuelva un valor para compilar de todos modos. Es imposible detectar este error a la perfección (consulte el problema de detención en.wikipedia.org/wiki/Halting_problem)

    – Felipe Couling

    15 de junio de 2018 a las 12:38


  • Tratar gcc -Wall.

    – Steve cumbre

    15 de junio de 2018 a las 12:38

  • @SteveSummit No logré que la advertencia desapareciera sin importar las opciones que excluí. creo -Wreturn-type es parte de las advertencias predeterminadas. Compilé con nada más que gcc -std=c11 y recibió la advertencia.

    – Lundin

    15 de junio de 2018 a las 12:44


  • @AnttiHaapala El engaño vinculado fue mucho peor que este, sin una sola respuesta que citara el estándar solicitado por el OP. Mejor cerrar ese viejo como un tonto a esto que al revés. Volví a abrir esto.

    – Lundin

    15 de junio de 2018 a las 12:54


  • @Lundin De acuerdo en que las respuestas en el dup propuesto son deficientes, y las de aquí son mucho mejores.

    – Steve cumbre

    15 de junio de 2018 a las 13:20

1647628690 848 ¿Por que los estandares de C le permiten no devolver
sepp2k

Ninguno de los estándares de C requiere que el compilador produzca un error o una advertencia si a una función le falta una declaración de retorno¹.

En todos los estándares de C, el comportamiento no está definido si el flujo de control alcanza el final de la función no nula sin un return y luego se usa el valor de retorno de la función (como en su código).

Entonces, si por “permitir” quiere decir “especificarlo como un comportamiento legal y bien definido”, ninguno de los estándares C permite su código. Si solo quiere decir “no requiere una implementación para producir un error”, todos lo hacen.

Tenga en cuenta que el valor de i en su programa de ejemplo puede cambiar fácilmente si cambia la definición de foo (sin añadir un return). No puede confiar en que sea 0. No existe una regla que diga “si no hay retorno, devuelva 0”, ni en el estándar ni en la implementación de GCC. De acuerdo con el estándar, no está definido y en GCC será lo que sea que esté en el registro de devolución en ese momento.


¹ En el caso general esto sería indecidible. Uno podría hacer lo que hace, por ejemplo, Java y definir las reglas para que se rechacen algunas definiciones de funciones válidas, pero ninguno de los estándares C hace esto.

  • No, el comportamiento no está indefinido si el flujo de control llega al final de la función no nula sin retorno; no está definido solo si se usa el valor de retorno.

    – Antti Haapala — Слава Україні

    15 de junio de 2018 a las 12:51

  • @AnttiHaapala Tienes razón, agregué esa condición.

    – sepp2k

    15 de junio de 2018 a las 12:53

  • Podría ser útil proporcionar un ejemplo de código que es imposible o difícil de analizar para el compilador (p. ej., cambio sin valor predeterminado en el que se sabe que la expresión de control coincide de alguna manera con alguna etiqueta de caso). Ciertamente es cierto que “en general por lo tanto, el caso es indecidible”, pero muchos lectores probablemente lo verían más claramente con un caso concreto.

    – rico

    15 de junio de 2018 a las 15:59


  • También hay una excepción especial para main — si el flujo de control llega al final de main sin declaración de retorno, es como si devolviera 0.

    – Chris Dodd

    6 mayo 2019 a las 20:29

1647628690 985 ¿Por que los estandares de C le permiten no devolver
Lundin

gcc da una advertencia:

advertencia: el control llega al final de la función no nula [-Wreturn-type]|

(Descargo de responsabilidad: obtengo diferentes resultados de diferentes versiones de gcc. Para estar seguro, compile con -Wall -Wextra -pedantic-errors.)

Lo que sucede en este caso no está cubierto por el estándar C. El estándar simplemente dice que (C11 6.9.1/12):

Si se alcanza el } que termina una función, y la persona que llama utiliza el valor de la llamada de función, el comportamiento no está definido.

En su caso, la persona que llama usa el valor devuelto almacenándolo en i. Entonces, este es un comportamiento indefinido: el comportamiento no está cubierto por el estándar C y el compilador no tiene la obligación de informarle al respecto. Cualquier cosa podría pasar. Por lo tanto, esto debe considerarse como un error en la mayoría de los casos.

Bueno, no hay nada en el estándar que le impida escribir código ilegal, sin embargo, menciona que este es un comportamiento indefinido.

citando C11capítulo §6.9.1

Si el } que finaliza una función, y el autor de la llamada utiliza el valor de la llamada de función, el comportamiento no está definido.

C89 establece (al menos en el borrador):

If a return statement without an expression is 
executed, and the value of the function call 
is used by the caller, the behavior is undefined.

Lo que significa que es legal escribir código como lo hiciste. NO es un error. Es un comportamiento indefinido pero sintácticamente legal. Dado que no es específicamente “ilegal” y está permitido sintácticamente, un compilador que se adhiere a C89 puede muy bien compilarlo y el resultado será indefinido … pero es un programa legítimo en lo que respecta al compilador.

Es por eso que incluso con “-pedante” probablemente no será rechazado. El comportamiento indefinido significa exactamente lo que dice: es indefinido lo que sucede. Tenga en cuenta que los compiladores pueden optimizar completamente el comportamiento indefinido (lo cual está bien, porque de todos modos es un comportamiento indefinido, por lo que el compilador puede hacer lo que quiera).

1647628691 921 ¿Por que los estandares de C le permiten no devolver
marco luzzara

Creo que en este caso el comportamiento indefinido es bastante común entre los compiladores más populares. Si desarmas tu foo función, encontrará estas instrucciones en casi todas las versiones de gcc:

    push    rbp
    mov     rbp, rsp
    nop
    pop     rbp
    ret

Este código no hace nada, exactamente lo que quieres. Sin embargo, ni siquiera sigue las reglas de la convención de llamada siempre que se espera un resultado de una función. El resultado debe ser pasado a la persona que llama por el eax/rax Registrarse. En foo este registro ni siquiera se modifica y esta es la razón por la que obtiene un comportamiento indefinido.


Omitir una devolución es como decir: “No quiero seguir las reglas que me diste”. clang produce un código similar y supongo que otros compiladores (a menos que se apliquen más opciones u optimizaciones) razonan de la misma manera. Nada te impide hacer eso, ni un compilador. El comportamiento indefinido es el precio de esta libertad.


La prueba de que el compilador confía en el código escrito es que el eax/rax el registro se considera realmente como el almacén del resultado de la función. En tu caso eax nunca se ha usado antes en ese código y su último valor es cero. Pero prueba con esto:

int i = 12345;
int m = 12345;
m *= 3;
i = foo();
printf("i is: %d", i);

la salida es probablemente va a ser 37035 incluso si ese es el mel valor de

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad