Declaración de funciones
Una declaración de función introduce un identificador que designa una función y, opcionalmente, especifica los tipos de parámetros de función (el prototipo). Las declaraciones de función (a diferencia de las definiciones) pueden aparecer tanto en el alcance del bloque como en el del archivo.
Contenido |
[editar] Sintaxis
En la gramática de la declaración de una función, la secuencia de especificaciones de tipo, posiblemente modificada por el declarante, designa el tipo de retorno (que puede ser de cualquier tipo que no sea un arreglo o un tipo de función), y el "declarante" tiene una de dos formas:
declarador-nopunt ( lista-parametros )
|
(1) | ||||||||
declarador-nopunt ( lista-identificadores(opcional) )
|
(2) | ||||||||
donde
declarador-nopun | - | cualquier declarador excepto el de puntero sin paréntesis. El identificador contenido en este declarador es el identificador que se convierte en el designador de la función. |
lista-parametros | - | o bien el termino clave void o una lista separada por comas de parámetros, que puede terminar con un parámetro de elipsis |
lista-identificadores | - | lista de identificadores separados por comas (sólo si este declarador se utiliza como parte de la definición de función de estilo antiguo), debe omitirse para las declaraciones de estilo antiguo que no sean definiciones. |
int max(int a, int b); // Declaracion int n = max(12.01, 3.14); // Esta bien, es una conversión de double a int
int max(); int n = max(true, (char)'a'); // Llama a max con dos argumentos int (después de promocionarse) int n = max(12.01f, 3.14); // Llama a max con dos argumentos double (después de promocionarse) int max(a, b) int a, b; { return a>b?a:b; } // La definición espera ints; la segunda llamada es indefinida
[editar] Explicación
El tipo de retorno de la función, determinado por el especificador de tipo en los especificadores-y-calificadores y posiblemente modificado por el declarador como es habitual en las declarations, debe ser un tipo de objeto completo que no sea un arreglo o el tipo void.
void f(char *s); // El tipo de retorno es void int sum(int a, int b); // El tipo de retorno de sum es int int (*foo(const void *p))[3]; // El tipo de retorno es un puntero a un arreglo de 3 int
El tipo de retorno de una función no puede ser cualificado cvr: cualquier tipo de retorno cualificado se ajusta a su versión no cualificada con el fin de construir el tipo de función: double const foo(void) { return 0.; } // Declara a la funcion de tipo double(void) double (*foop)(void) = foo; // Esta bien, foop es un puntero a double(void) double const (*foopc)(void) = foop; // Esta bien, foopc es otro puntero a double(void) |
(desde C17) |
Los declaradores de función pueden combinarse con otros declaradores siempre y cuando puedan compartir sus especificadores de tipo y calificadores.
int f(void), *fip(), (*pfi)(), *ap[3]; // Declara dos funciones y dos objetos inline int g(int), n; // Error: el calificador inline es sólo para funciones typedef int array_t[3]; array_t a, h(); // Error: el tipo arreglo no puede ser un tipo de retorno para una función
Si una declaración de función aparece fuera de cualquier función, el identificador que introduce tiene alcance de archivo y enlace externo, a menos que se utilice static
o se vea una declaración estática anterior. Si la declaración se produce dentro de otra función, el identificador tiene un alcance de bloque (y también un enlace interno o externo).
int main(void) { int f(int); // Enlace externo, alcance del archivo f(1); // La definición debe estar disponible en algún lugar del programa }
No es necesario nombrar los parámetros de una declaración que no forman parte de una definición de función:
int f(int, int); // Declaracion // int f(int, int) { return 7; } // Error, los parámetros deben ser nombrados en las definiciones
Cada parámetro de una lista-parametros es una declaración que introduce una única variable, con las siguientes propiedades adicionales:
- el identificador en el declarador es opcional (excepto si esta declaración de función forma parte de una definición de función)
int f(int, double); // Esta bien int g(int a, double b); // Tambien esta bien int f(int, double) { return 1; } // Error: la definición debe nombrar los parámetros
- el único especificador de clase de almacenamiento permitido para los parámetros es
register
, y se ignora en las declaraciones de función que no son definiciones
int f(static int x); // Error int f(int [static 10]); // Esta bien (el índice del arreglo estático no es un especificador de clase de almacenamiento)
- cualquier parámetro del tipo de arreglo se ajusta al tipo de puntero correspondiente, que puede calificarse si hay calificadores entre los corchetes del declarador de arreglo. (desde C99)
int f(int[]); // Declara int f(int*) int g(const int[10]); // Declara int g(const int*) int h(int[const volatile]); // Declara int h(int * const volatile) int x(int[*]); // Declara int x(int*)
- cualquier parámetro del tipo de función se ajusta al tipo de puntero correspondiente
int f(char g(double)); // Declara int f(char (*g)(double)) int h(int(void)); // Declara int h(int (*)(void))
- la lista de parámetros puede terminar con
, ...
, véase funciones variadicas para más detalles.
int f(int, ...);
- no pueden tener el tipo void}. (pero puede tener un puntero a void). La lista de parámetros especiales que consiste enteramente en la palabra clave void se utiliza para declarar funciones que no toman ningún parámetro.
int f(void); // Esta bien int g(void x); // Error
- cualquier identificador que aparezca en una lista de parámetros que pueda ser tratado como un nombre de typedef o como un nombre de parámetro es tratado como un nombre de typedef: int f(size_t, uintptr_t) es analizado como un nuevo declarador de estilo para una función que toma dos parámetros sin nombre de tipo size_t y uintptr_t, no un declarador de estilo antiguo que comienza la definición de una función tomando dos parámetros llamados "size_t" y "uintptr_t".
- los parámetros pueden tener tipo incompleto y puede usar la notación VLA [*] (desde C99) (excepto que en una definición de función, los tipos de parámetros después del ajuste de arreglo a puntero y funcion a puntero deben estar completos)
Véase el operador de llamada de función para otros detalles sobre la mecánica de una llamada de función y return para el retorno de las funciones.
[editar] Observaciones
A diferencia de C++, los declaradores f() y f(void) tienen diferentes significados: el declarador f(void) es un nuevo estilo (prototipo) de declarador que declara una función que no toma ningún parámetro. El declarador f() es un declarador de estilo antiguo (K&R) que declara una función que toma un número no especificado de parámetros (a menos que se utilice en una definición de función de estilo antiguo)
int f(void); // declaracion que no toma parametros int g(); // declaracion que toma un numero desconocido de parametros int main(void) { f(1); // Error de compilación g(2); // comportamiento indefinido } int f(void) { return 1; ) // definicion real int g(a,b,c,d) int a,b,c,d; { return 2; } // definicion real
A diferencia de una definición de función, la lista de parámetros puede ser heredada de un typedef
typedef int p(int q, int r); // p es una funcion de tipo int(int, int) p f; // Declara int f(int, int)
En C89, los especificadores-y-calificadores era opcionales, y si se omiten, el tipo de retorno de la función por defecto es int (posiblemente modificado por el declarador). *f() { // función que retorna int* return NULL; } |
(hasta C99) |
[editar] Referencias
- C17 standard (ISO/IEC 9899:2018):
- 6.7.6.3 Function declarators (including prototypes) (p: 96-98)
- Standard C11 (ISO/IEC 9899:2011):
- 6.7.6.3 Function declarators (including prototypes) (p: 133-136)
- Standard C99 (ISO/IEC 9899:1999):
- 6.7.5.3 Function declarators (including prototypes) (p: 118-121)
- Standard C89/C90 (ISO/IEC 9899:1990):
- 3.5.4.3 Function declarators (including prototypes)
[editar] Véase también
Documentación de C++ para Declaración de funciones
|