Sintaxis C alternativa (K&R) para declaración de funciones frente a prototipos
¿Qué tiene de útil esta C
sintaxis: usar declaraciones de funciones de estilo 'K&R'?
int func (p, p2)
void* p;
int p2;
{
return 0;
}
Pude escribir esto en Visual Studios 2010beta.
// yes, the arguments are flipped
void f()
{
void* v = 0;
func(5, v);
}
No entiendo. ¿Cuál es el punto de esta sintaxis? Puedo escribir:
int func (p, p2)
int p2;
{
return 0;
}
// and write
int func (p, p2)
{
return 0;
}
Lo único que parece especificar es cuántos parámetros utiliza y el tipo de retorno. Supongo que los parámetros sin tipos son geniales, pero ¿por qué permitirlo y int paranName
después del declarador de función? Es raro.
¿También sigue siendo el estándar C?
La pregunta que usted hace son en realidad dos preguntas, no una. La mayoría de las respuestas hasta ahora han tratado de cubrir todo el asunto con una respuesta genérica de "este es el estilo K&R", mientras que, de hecho, sólo una pequeña parte tiene algo que ver con lo que se conoce como estilo K&R (a menos que vea el todo el lenguaje C como "estilo K&R" de una forma u otra :)
La primera parte es la extraña sintaxis utilizada en la definición de la función.
int func(p, p2)
void *p;
int p2; /* <- optional in C89/90, but not in C99 */
{
return 0;
}
Esta es en realidad una definición de función al estilo K&R. Otras respuestas han cubierto esto bastante bien. Y en realidad no hay mucho que decir. La sintaxis está en desuso, pero aún es totalmente compatible incluso en C18 (excepto la regla "no implicit int" en C99 o posterior, lo que significa que en C99 no se puede omitir la declaración de p2
).
La segunda parte poco tiene que ver con el estilo K&R. Me refiero al hecho de que la función se puede llamar con argumentos "intercambiados", es decir, en dicha llamada no se realiza ninguna verificación del tipo de parámetro. Esto tiene muy poco que ver con la definición de estilo K&R per se, pero tiene mucho que ver con que su función no tenga prototipo. Verás, en C, cuando declaras una función como esta
int foo();
en realidad declara una función foo
que toma un número no especificado de parámetros de tipo desconocido . Puedes llamarlo como
foo(2, 3);
y como
j = foo(p, -3, "hello world");
y así sucesivamente (entiendes la idea);
Sólo la llamada con los argumentos adecuados "funcionará" (lo que significa que las demás producen un comportamiento indefinido), pero depende totalmente de usted garantizar su corrección. El compilador no está obligado a diagnosticar los incorrectos, incluso si de alguna manera mágicamente conoce los tipos de parámetros correctos y su número total.
En realidad, este comportamiento es una característica del lenguaje C. Es peligroso, pero al fin y al cabo es una característica. Te 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, mezcle diferentes tipos de funciones en una matriz "polimórfica" sin encasillamientos (aunque aquí no se pueden usar tipos de funciones variables). Nuevamente, los peligros inherentes de esta técnica son bastante obvios (no recuerdo haberla usado nunca, pero puedo imaginar dónde puede ser útil), pero eso es C después de todo.
Finalmente, la parte que vincula la segunda parte de la respuesta con la primera. Cuando realiza una definición de función al estilo K&R, no introduce un prototipo para la función. En lo que respecta al tipo de función, su func
definición declara func
como
int func();
es decir, no se declaran los tipos ni el número total de parámetros. En su publicación original, dice: "... parece especificar cuántos parámetros utiliza ...". Formalmente hablando, ¡no es así! Después de su definición de estilo K&R de dos parámetros func
, aún puede llamar func
como
func(1, 2, 3, 4, "Hi!");
y no habrá ninguna violación de restricción en él. (Normalmente, un compilador de calidad le dará una advertencia).
Además, un hecho que a veces se pasa por alto es que
int f()
{
return 0;
}
También es una definición de función estilo K&R que no introduce un prototipo. Para hacerlo "moderno", tendrías que ponerlo explícito void
en la lista de parámetros:
int f(void)
{
return 0;
}
Finalmente, contrariamente a la creencia popular, tanto las definiciones de funciones de estilo K&R como las declaraciones de funciones no prototipos son totalmente compatibles con C99 y posteriores (hasta C18, pero no en C23). El primero ha quedado obsoleto desde C89/90, si mal no recuerdo. C99 requiere que la función se declare antes del primer uso, pero no es necesario que la declaración sea un prototipo. La confusión aparentemente surge de la confusión terminológica popular: muchas personas llaman a cualquier declaración de función "un prototipo", mientras que, de hecho, "declaración de función" no es lo mismo que "prototipo".
Tenga en cuenta que C23 cambiará las reglas. La definición de función estilo K&R ya no será el estándar C. Además, una declaración de función como ésta extern int func();
declarará un prototipo de función equivalente a extern int func(void);
, tal como en C++. Y una definición de función como la que int func() { … }
definirá un prototipo para la función.
Esta es una sintaxis de K&R C bastante antigua (anterior a ANSI/ISO C). Hoy en día, ya no deberías usarlo (como ya habrás notado su mayor desventaja: el compilador no verificará los tipos de argumentos por ti). El tipo de argumento en realidad es el predeterminado int
en su ejemplo.
En el momento en que se usaba esta sintaxis, a veces se encontraban funciones como
foo(p, q)
{
return q + p;
}
que en realidad era una definición válida, como los tipos para p
, q
y el tipo de retorno foo
predeterminado para int
.
Esta es simplemente una sintaxis antigua, anterior a la sintaxis "ANSI C" con la que quizás esté más familiarizado. Normalmente se llama " K&R C ".
Los compiladores lo admiten para que sea completo y, por supuesto, para que pueda manejar bases de código antiguas.
Esta es la sintaxis original de K&R antes de que C fuera estandarizado en 1989. C89 introdujo prototipos de funciones, tomados prestados de C++, y dejó obsoleta la sintaxis de K&R. No hay ninguna razón para usarlo (y muchas razones para no hacerlo) en un código nuevo.