La pregunta que hace es realmente dos preguntas, no una. La mayoría de las respuestas intentaron cubrir todo el asunto con una manta genérica "esto es K & estilo R" respuesta, mientras que de hecho, solo una pequeña parte tiene algo que ver con lo que se conoce como K & estilo R (a menos que veas todo el lenguaje C como "K & R-estilo" de una manera u otra :)
la primera parte es la sintaxis extraña utilizado en función de la definición
int func(p, p2)
void *p;
int p2; /* <- optional in C89/90, but not in C99 */
{
return 0;
}
Ésta es en realidad una K & Definición de función estilo R Otra respuesta ha cubierto esto bastante bien. Y no hay mucho para eso, en realidad. La sintaxis está en desuso, pero aún totalmente compatible incluso en C99 (excepto para la regla "no implicit imp" en C99, lo que significa que en C99 no puede omitir la declaración de p2
).
La segunda parte tiene poco que ver con K & estilo R. Me refiero al hecho de que la función puede invocarse con argumentos "intercambiados", es decir, que no se realiza ninguna verificación de tipo de parámetro en dicha llamada. Esto tiene muy poco que ver con la definición de estilo R de K & per se, pero tiene todo que ver con que su función no tenga prototipo. Usted ve, en C cuando se declara una función como esta
int foo();
que en realidad declara una función que toma foo
un número indeterminado de parámetros de tipo desconocido. Se le puede llamar como
foo(2, 3);
y como
j = foo(p, -3, "hello world");
ans así sucesivamente (se entiende la idea);
Solo la llamada con argumentos correctos "funcionará" (lo que significa que los demás producen un comportamiento indefinido), pero depende completamente de usted asegurar su corrección. El compilador no está obligado a diagnosticar los incorrectos, incluso si de alguna manera sabe mágicamente los tipos de parámetros correctos y su número total.
En realidad, este comportamiento es una característica del lenguaje C. Una peligrosa, pero una característica sin embargo. Se le permite hacer algo como esto
void foo(int i);
void bar(char *a, double b);
void baz(void);
int main()
{
void (*fn[])() = { foo, bar, baz };
fn[0](5);
fn[1]("abc", 1.0);
fn[2]();
}
es decir, mezclar diferentes tipos de funciones en una matriz "polimórfica" sin ningún tipo de typecasts (tipos de función variadic no se pueden utilizar aquí, sin embargo). Nuevamente, los peligros inherentes de esta técnica son bastante obvios (no recuerdo haberlo usado nunca, pero puedo imaginar dónde puede ser útil), pero eso es C después de todo.
Finalmente, el bit que vincula la segunda parte de la respuesta con la primera. Cuando realiza una definición de función de estilo R K &, no introduce un prototipo para la función. En lo que se refiere a tipo de función, su definición func
declara func
como
int func();
es decir, ninguno de los tipos ni el número total de parámetros son declarados. En tu publicación original dices "... parece especificar cuántos params usa ...".Formalmente hablando, ¡no es así! Después de la de dos parámetros K & R-estilo func
definición todavía se puede llamar como func
func(1, 2, 3, 4, "Hi!");
y no habrá ninguna violación de la restricción en el mismo. (Normalmente, un compilador de calidad le dará una advertencia).
También, a veces se olvida un hecho es que
int f()
{
return 0;
}
es también una definición de función de estilo R K & que no introduzca un prototipo. Para que sea más "moderna" que tendría que poner explícita void
en la lista de parámetros
int f(void)
{
return 0;
}
último, contrariamente a la creencia popular, tanto K & definiciones de funciones de tipo I y declaraciones de funciones no-prototipos son totalmente compatible con C99. El primero ha quedado obsoleto desde C89/90, si no recuerdo mal. C99 requiere que la función se declare antes del primer uso, pero no se requiere que la declaración sea un prototipo. La confusión aparentemente se deriva de la popular confusión terminológica: muchas personas llaman a cualquier declaración de la función "prototipo", mientras que de hecho "declaración de la función" no es lo mismo que "prototipo".
muy bien explicado, esp. la parte "función sin prototipo". –
Pero, ¿cuál es la diferencia ahora entre una función varargs y una no prototipada? – Sebastian
@Sebastian Godelet varargs son innecesariamente estrictas. Por ejemplo, llamar a una función vararg con una int, y abrirla como bytes no es válido. Entonces, definir una función con solo parámetros vararg. – YoYoYonnY