Esta función de C siempre debería devolver falso, pero no lo hace

Resuelto Dimitri Podborski asked hace 8 años • 4 respuestas

Hace mucho tiempo me encontré con una pregunta interesante en un foro y quiero saber la respuesta.

Considere la siguiente función C:

f1.c

#include <stdbool.h>

bool f1()
{
    int var1 = 1000;
    int var2 = 2000;
    int var3 = var1 + var2;
    return (var3 == 0) ? true : false;
}

Esto siempre debería regresar falsedesde var3 == 3000. La mainfunción se ve así:

C Principal

#include <stdio.h>
#include <stdbool.h>

int main()
{
    printf( f1() == true ? "true\n" : "false\n");
    if( f1() )
    {
        printf("executed\n");
    }
    return 0;
}

Dado que f1()siempre debería devolver false, uno esperaría que el programa imprimiera solo un valor falso en la pantalla. Pero después de compilarlo y ejecutarlo, también se muestra ejecutado :

$ gcc main.c f1.c -o test
$ ./test
false
executed

¿Porqué es eso? ¿Este código tiene algún tipo de comportamiento indefinido?

Nota: lo compilé con gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2.

Dimitri Podborski avatar Apr 07 '16 19:04 Dimitri Podborski
Aceptado

Como se señaló en otras respuestas, el problema es que lo usa gccsin opciones del compilador configuradas. Si hace esto, el valor predeterminado será lo que se llama "gnu90", que es una implementación no estándar del antiguo estándar C90 retirado de 1990.

En el antiguo estándar C90 había un defecto importante en el lenguaje C: si no se declaraba un prototipo antes de usar una función, el valor predeterminado sería int func ()(donde ( )significa "aceptar cualquier parámetro"). Esto cambia la convención de llamada de la función func, pero no cambia la definición real de la función. Dado que el tamaño de booly intson diferentes, su código invoca un comportamiento indefinido cuando se llama a la función.

Este comportamiento peligroso y sin sentido se solucionó en el año 1999, con el lanzamiento del estándar C99. Se prohibieron las declaraciones de funciones implícitas.

Desafortunadamente, GCC hasta la versión 5.xx todavía usa el antiguo estándar C de forma predeterminada. Probablemente no haya ninguna razón por la que quieras compilar tu código como algo que no sea C estándar. Por lo tanto, debes decirle explícitamente a GCC que debería compilar tu código como código C moderno, en lugar de una basura GNU no estándar de más de 25 años. .

Solucione el problema compilando siempre su programa como:

gcc -std=c11 -pedantic-errors -Wall -Wextra
  • -std=c11le dice que haga un intento a medias de compilar de acuerdo con el estándar C (actual) (conocido informalmente como C11).
  • -pedantic-errorsle dice que haga de todo corazón lo anterior y que proporcione errores de compilación cuando escriba código incorrecto que viole el estándar C.
  • -Wallsignifica darme algunas advertencias adicionales que sería bueno tener.
  • -Wextrasignifica darme algunas otras advertencias adicionales que sería bueno tener.
Lundin avatar Apr 07 '2016 13:04 Lundin

No tienes un prototipo declarado f1()en main.c, por lo que está implícitamente definido como int f1(), lo que significa que es una función que toma un número desconocido de argumentos y devuelve un archivo int.

Si inty boolson de diferentes tamaños, esto dará como resultado un comportamiento indefinido . Por ejemplo, en mi máquina, intson 4 bytes y boolun byte. Dado que la función está definida para regresar bool, coloca un byte en la pila cuando regresa. Sin embargo, dado que implícitamente se declara que regresa intdesde main.c, la función de llamada intentará leer 4 bytes de la pila.

Las opciones predeterminadas de los compiladores en gcc no le dirán que está haciendo esto. Pero si compilas con -Wall -Wextra, obtendrás esto:

main.c: In function ‘main’:
main.c:6: warning: implicit declaration of function ‘f1’

Para solucionar este problema, agregue una declaración for f1en main.c, antes de main:

bool f1(void);

Tenga en cuenta que la lista de argumentos está establecida explícitamente en void, lo que le indica al compilador que la función no toma argumentos, a diferencia de una lista de parámetros vacía que significa un número desconocido de argumentos. La definición f1en f1.c también debería cambiarse para reflejar esto.

dbush avatar Apr 07 '2016 12:04 dbush