c gcc compiler-errors

¿Por qué gcc permite que los argumentos pasen a una función definida para estar sin argumentos?



compiler-errors (4)

No entiendo por qué compila este código?

#include <stdio.h> void foo() { printf("Hello/n"); } int main() { const char *str = "bar"; foo(str); return 0; }

gcc ni siquiera lanza una advertencia de que estoy pasando demasiados argumentos a foo (). ¿Es este comportamiento esperado?


En C, una función declarada con una lista de parámetros vacía acepta un número arbitrario de argumentos al ser llamados, que están sujetos a las promociones aritméticas habituales. Es responsabilidad del que llama asegurarse de que los argumentos proporcionados sean apropiados para la definición de la función.

Para declarar una función tomando cero argumentos, debe escribir void foo(void); .

Esto es por razones históricas; originalmente, las funciones C no tenían prototipos, ya que C evolucionó de B , un lenguaje sin tipo. Cuando se agregaron prototipos, las declaraciones originales sin tipo se dejaron en el idioma para compatibilidad con versiones anteriores.

Para que gcc advierta sobre listas de parámetros vacías, use -Wstrict-prototypes :

Advertir si una función se declara o define sin especificar los tipos de argumentos. (Se permite una definición de función antigua sin una advertencia si está precedida por una declaración que especifica los tipos de argumentos).


Por razones heredadas, declarar una función con () para una lista de parámetros significa esencialmente "averiguar los parámetros cuando se llama a la función". Para especificar que una función no tiene parámetros, use (void) .

Editar: Siento que estoy acumulando reputación en este problema por ser viejo. Para que los niños sepan cómo era la programación, aquí está mi primer programa . (No C; muestra con qué tuvimos que trabajar antes de eso).


Tanto el estándar C99 (6.7.5.3) como el estándar C11 (6.7.6.3) establecen:

Una lista de identificadores declara solo los identificadores de los parámetros de la función. Una lista vacía en un declarador de función que es parte de una definición de esa función especifica que la función no tiene parámetros. La lista vacía en un declarador de funciones que no es parte de una definición de esa función especifica que no se proporciona información sobre el número o los tipos de los parámetros.

Como la declaración de foo es parte de una definición, la declaración especifica que foo toma 0 argumentos, por lo que la llamada foo (str) es al menos moralmente incorrecta. Pero como se describe a continuación, hay diferentes grados de "incorrecto" en C, y los compiladores pueden diferir en cómo manejan ciertos tipos de "errores".

Para tomar un ejemplo un poco más simple, considere el siguiente programa:

int f() { return 9; } int main() { return f(1); }

Si compilo lo anterior usando Clang:

tmp$ cc tmp3.c tmp3.c:4:13: warning: too many arguments in call to ''f'' return f(1); ~ ^ 1 warning generated.

Si compilo con gcc 4.8 no recibo ningún error o advertencia, incluso con -Wall. Una respuesta previa sugería usar prototipos de -Wstrict, que informa correctamente que la definición de f no está en forma de prototipo, pero este no es realmente el punto. Los estándares C permiten una definición de función en una forma no prototipo como la anterior y los estándares indican claramente que esta definición especifica que la función toma 0 argumentos.

Ahora hay una restricción (C11 Sec. 6.5.2.2):

Si la expresión que denota la función llamada tiene un tipo que incluye un prototipo, el número de argumentos estará de acuerdo con el número de parámetros.

Sin embargo, esta restricción no se aplica en este caso, ya que el tipo de la función no incluye un prototipo. Pero aquí hay una declaración posterior en la sección de semántica (no una "restricción"), que sí se aplica:

Si la expresión que denota la función llamada tiene un tipo que no incluye un prototipo ... Si el número de argumentos no es igual al número de parámetros, el comportamiento no está definido.

Por lo tanto, la llamada a función da como resultado un comportamiento indefinido (es decir, el programa no es "estrictamente conforme"). Sin embargo, el Estándar solo requiere una implementación para informar un mensaje de diagnóstico cuando se infringe una restricción real, y en este caso, no hay violación de una restricción. Por lo tanto, gcc no está obligado a informar un error o advertencia para ser una "implementación conforme".

Entonces, creo que la respuesta a la pregunta, ¿por qué lo permite gcc ?, es que no se requiere que gcc reporte nada, ya que esto no es una violación de restricción. Además, gcc no afirma informar todo tipo de comportamiento indefinido, incluso con -Wall o -Wpedantic. Es un comportamiento indefinido, lo que significa que la implementación puede elegir cómo manejarlo, y gcc ha elegido compilarlo sin advertencias (y aparentemente solo ignora el argumento).


void foo() { printf("Hello/n"); } foo(str);

en C, este código no infringe una restricción (lo haría si estuviera definido en su forma de prototipo con void foo(void) {/*...*/} ) y como no hay ninguna violación de restricción, el compilador no está requerido para emitir un diagnóstico.

Pero este programa tiene un comportamiento indefinido de acuerdo con las siguientes reglas de C:

De:

(C99, 6.9.1p7) "Si el declarador incluye una lista de tipos de parámetros, la lista también especifica los tipos de todos los parámetros, dicho declarador también sirve como un prototipo de función para llamadas posteriores a la misma función en la misma unidad de traducción. Si el declarante incluye una lista de identificadores, 142) los tipos de los parámetros se declararán en una lista de declaraciones siguiente. "

la función foo no proporciona un prototipo.

De:

(C99, 6.5.2.2p6) "Si la expresión que denota la función llamada tiene un tipo que no incluye un prototipo [...] Si el número de argumentos no es igual al número de parámetros, el comportamiento no está definido".

la llamada a la función foo(str) es un comportamiento indefinido.

C no exige que la implementación emita un diagnóstico para un programa que invoca un comportamiento indefinido, pero su programa sigue siendo un programa erróneo.