¿Por qué compila una función sin parámetros (en comparación con la definición de la función real)?


Acabo de encontrarme con el código C de alguien que me confunde en cuanto a por qué se está compilando. Hay dos puntos que no entiendo.

Primero, el prototipo de función no tiene parámetros comparados con la definición de función real. En segundo lugar, el parámetro en la definición de la función no tiene un tipo.

#include <stdio.h>

int func();

int func(param)
{
    return param;
}

int main()
{
    int bla = func(10);    
    printf("%d", bla);
}

¿por Qué funciona esto? Lo he probado en un par de compiladores, y funciona bien.

Author: AdmiralJonB, 2012-12-19

11 answers

Todas las demás respuestas son correctas, pero solo para completar

Una función se declara de la siguiente manera:

  return-type function-name(parameter-list,...) { body... }

Return-type es el tipo de variable que devuelve la función. Esto no puede ser un tipo de matriz o un tipo de función. Si no se da, entonces int se supone.

Function-name es el nombre de la función.

Parameter-list es la lista de parámetros que toma la función separados por comas. Si no se dan parámetros, entonces la función no toma ninguna y debe definirse con un conjunto vacío de paréntesis o con la palabra clave void. Si no hay ningún tipo de variable delante de una variable en la lista de parámetros, entonces se asume int. Arrays y las funciones no se pasan a funciones, sino que se convierten automáticamente por punteros. Si la lista termina con puntos suspensivos (,...), entonces no hay un número establecido de parámetros. Nota: el encabezado stdarg.h puede ser se utiliza para acceder a argumentos cuando se utilizan puntos suspensivos.

Y de nuevo por el bien de la integridad. De la especificación C11 6:11:6 (página: 179)

El uso de declaradores de función con paréntesis vacíos (no prototype-format parameter type declarators) is an obsolescent feature .

 268
Author: Krishnabhadra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-08 11:17:25

En C func() significa que puede pasar cualquier número de argumentos. Si no quieres argumentos entonces tienes que declarar como func(void). El tipo que está pasando a su función, si no se especifica por defecto es int.

 158
Author: Tony The Lion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 10:50:19

int func(); es una declaración de función obsoleta de los días en que no había un estándar C, es decir, los días de K&R C (antes de 1989, el año en que se publicó el primer estándar "ANSI C").

Recuerde que no había prototipos en K&R C y la palabra clave void aún no se inventó. Todo lo que podía hacer era decirle al compilador sobre el tipo de retorno de una función. La lista de parámetros vacía en K&R C significa" un número no especificado pero fijo " de argumentos. Fijo significa que debe llamar a la función con el mismo número de args cada vez (a diferencia de una función variadic como printf, donde el número y el tipo pueden variar para cada llamada).

Muchos compiladores diagnosticarán esta construcción; en particular gcc -Wstrict-prototypes le dirá que "la declaración de funciones no es un prototipo", lo cual es perfecto, porque se ve como un prototipo (especialmente si está envenenado por C++!), pero no lo es. Es una declaración de tipo de retorno K&R C de estilo antiguo.

Regla general: Nunca deje vacía una declaración de lista de parámetros vacía, use int func(void) para ser específico. Esto convierte la declaración de tipo de retorno K&R en un prototipo C89 apropiado. Los compiladores son felices, los desarrolladores son felices, los comprobadores estáticos son felices. Los engañados por^W^Wfond de C++ pueden encogerse, sin embargo, porque necesitan escribir caracteres adicionales cuando intentan ejercer sus habilidades en idiomas extranjeros: -)

 55
Author: Jens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-26 22:47:45
  • La lista de parámetros vacía significa "cualquier argumento", por lo que la definición no es incorrecta.
  • Se asume que el tipo que falta es int.

Consideraría que cualquier compilación que pase esto carece de nivel de advertencia/error configurado, sin embargo, no tiene sentido que esto permita el código real.

 53
Author: unwind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-21 14:35:10

Es K&R declaración y definición de función de estilo. De la norma C99 (ISO/IEC 9899:TC3)

Sección 6.7.5.3 Declaradores de funciones (incluidos los prototipos)

Una lista de identificadores declara solo los identificadores de los parámetros de la función. Un vacío list in a function declarator that is part of a definition of that function specifies that the la función no tiene parámetros. La lista vacía en un declarador de función que no es parte de un definición de esa función especifica que no hay información sobre el número o los tipos de parámetros se suministra. (Si ambos tipos de función son "estilo antiguo", los tipos de parámetros no se comparan.)

Sección 6.11.6 Declaradores de funciones

El uso de declaradores de función con paréntesis vacíos (no el parámetro prototype-format declaradores de tipo) es una característica obsoleta .

Sección 6.11.7 Definiciones de funciones

El uso de la función definiciones con identificador de parámetro separado y listas de declaraciones (no prototype-format parameter type and identifier declarators) es una característica obsolescente.

Que el estilo antiguo significa K & R estilo

Ejemplo:

Declaración: int old_style();

Definición:

int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}
 29
Author: Lei Mou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-23 15:07:36

C asume int si no se da ningún tipo en la función devuelve el tipo y la lista de parámetros. Solo para esta regla siguiendo cosas extrañas son posibles.

Una definición de función se ve así.

int func(int param) { /* body */}

Si es un prototipo escribes

int func(int param);

En prototype solo se puede especificar el tipo de parámetros. El nombre de los parámetros no es obligatorio. So

int func(int);

También si no se especifica el tipo de parámetro, pero el nombre int se asume como tipo.

int func(param);

Si vas más lejos, seguir también funciona.

func();

El compilador asume int func() cuando escribes func(). Pero no ponga func() dentro de un cuerpo de función. Eso será una llamada a la función

 15
Author: Shiplu Mokaddim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-23 15:08:29

Como dijo @Krishnabhadra, todas las respuestas anteriores de otros usuarios, tienen una interpretación correcta, y solo quiero hacer un análisis más detallado de algunos puntos.

En el Viejo-C como en ANSI-C el "untyped formal parameter", tome la dimencion de su registro de trabajo o capacidad de profundidad de instrucción (registros de sombra o ciclo acumulativo de instrucción), en un MPU de 8 bits, será un int16, en un MPU de 16 bits y así será un int16 y así sucesivamente, en el caso elija compilar opciones como: - m32.

Aunque parece una implementación más sencilla a alto nivel, Para pasar múltiples parámetros, el trabajo del programador en el paso de tipo de datos de control dimencion, se vuelve más exigente.

En otros casos, para algunas arquitecturas de microprocesadores, los compiladores ANSI personalizados, aprovecharon algunas de estas características antiguas para optimizar el uso del código, obligando a la ubicación de estos "parámetros formales sin tipo" a trabajar dentro o fuera del trabajo regístrese, hoy en día se obtiene casi lo mismo con el uso de" volátil "y"registro".

Pero cabe señalar que los compiladores más modernos, no hacer ninguna distinción entre los dos tipos de declaración de parámetros.

Ejemplos de una compilación con gcc bajo linux:

principal.c

main2.c

main3.c  
En cualquier caso, la declaración del prototipo localmente no sirve de nada, porque no hay llamada sin referencia de parámetros a este prototipo será negligente. Si utiliza el sistema con "untyped formal parameter", para una llamada externa, proceda a generar un tipo de datos de prototipo declarativo.

Así:

int myfunc(int param);
 11
Author: RTOSkit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-13 04:35:21

En cuanto al tipo de parámetro, ya hay respuestas correctas aquí, pero si desea escucharlo del compilador, puede intentar agregar algunas banderas (las banderas son casi siempre una buena idea de todos modos).

Compilando tu programa usando gcc foo.c -Wextra obtengo:

foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]

Extrañamente -Wextra no captura esto para clang (no reconoce -Wmissing-parameter-type por alguna razón, tal vez para los históricos mencionados anteriormente) pero -pedantic lo hace:

foo.c:5:10: warning: parameter 'param' was not declared, 
defaulting to type 'int' [-pedantic]
int func(param)
         ^
1 warning generated.

Y para el problema del prototipo como se dijo de nuevo anteriormente int func() se refiere a parámetros arbitrarios a menos que lo defina como int func(void) que luego le dará los errores como se espera:

foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here

O en clang como:

foo.c:5:5: error: conflicting types for 'func'
int func(param)
    ^
foo.c:3:5: note: previous declaration is here
int func(void);
    ^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
    int bla = func(10);
              ~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.
 5
Author: none,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 19:41:22

Si la declaración de la función no tiene parámetros, es decir, está vacía, entonces está tomando un número no especificado de argumentos. Si desea que no tome argumentos, cámbielo a:

int func(void);
 3
Author: P.P.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 12:47:20

Esta es la razón por la que normalmente aconsejo a la gente que compile su código con:

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

Estas banderas imponen un par de cosas:

  • -Wmissing-variable-declarations: Es imposible declarar una función no estática sin obtener primero un prototipo. Esto hace que sea más probable que un prototipo en un archivo de encabezado coincida con la definición real. Alternativamente, obliga a agregar la palabra clave estática a las funciones que no necesitan ser visibles públicamente.
  • -Wstrict-variable-declarations: El prototipo debe listar correctamente los argumentos.
  • -Wold-style-definition: La propia definición de la función también debe listar correctamente los argumentos.

Estos indicadores también se utilizan por defecto en muchos proyectos de Código abierto. Por ejemplo, FreeBSD tiene estas banderas habilitadas cuando compila con WARNS = 6 en su Makefile.

 0
Author: Ed Schouten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-12 21:38:48

Señor, en C++ ( y SOLO en C++ ) se le permite definir múltiples funciones del mismo nombre con diferentes parámetros. Por ejemplo:

int func();
int func(int test);
int func(char testing123);

Debería compilar. Para elegir qué función usar, simplemente pase ese tipo de variable entre paréntesis cuando compile.

Por ejemplo:

int testing123=2;
func(testing123);

Llamará a func(int test).

Considerando que

char test='a';
func(test);

Llamará a func(char).

NO necesita nombres de variables en el encabezado de la función, aunque siempre y cuando el prototipo de función (ya sabes, la línea en la parte superior que tiene solo una función sin código en ella) coincide con los nombres en función real a continuación serás Un OK (por ejemplo, en lugar de int func(int) podrías tener int func(int avariable).

En cuanto a la variable en el prototipo que compila sin un tipo, probablemente por defecto escriba, probablemente int (aunque no estoy seguro de si el tipo por defecto varía según el compilador o no.)

 -12
Author: user1833028,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 11:37:06