Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.4.2  Definición de una función

§1  Sinopsis

La definición de la función se realiza en el código fuente (o en librerías precompiladas), fuera del bloque de cualquier otra función, incluyendo main y, a excepción de la capacidad del C++ de lo que se denomina sobrecarga, solo está permitida en cada programa una definición para cada función [1].

§2  Sintaxis

La definición tiene la sintaxis y las secciones que se indican (la gramática permite casos más complicados)

[<storage-class>] [<tipo-dev>] nombre-func (<tipo-p> <n-param>, ... )
{
    <declaraciones>;
    <sentencias>;
}


1
- <storage-class> especificador opcional de tipo de almacenamiento, extern o static. Por defecto las funciones son externas y accesibles desde cualquier fichero del programa [2], aunque pueden ser restringidas usando el especificador static ( 4.1.8c). Ejemplos:

static int func(char ch, int x) { ... }
extern int func(char ch, int x) { ... }
int func(char ch, int x) { ... }   // equivale a la anterior

Como puede verse, es superflua la declaración de extern ( 4.1.8d) en una función.


2
- <tipo-dev> especificador opcional del valor devuelto por la función, posiblemente void. Por defecto se supone int. Ejemplo, las expresiones que siguen son equivalentes:

int func(char ch, int x) { ... }
func(char ch, int x) { ... }

Nota: en C++Builder, este especificador puede incluir modificadores opcionales: __pascal, __cdecl__export. El valor por defecto depende de la selección de opciones del compilador ( 1.4.3).


3
- nombre-func El identificador (nombre) de la función. Salvo caso de sobrecarga no se permiten identificadores duplicados, la razón es que al no permitirse funciones dentro de funciones, las funciones son siempre de ámbito global al fichero en que se definen (se excluyen de esta consideración las funciones-miembro de clases, ya que para estos efectos, las clases funcionan como subespacios de nombres).


4
- (t n, ...)  Lista de declaración de parámetros entre paréntesis. Aunque el viejo estilo de indicar ausencia de parámetros  func() también es aún válido en C++, se le considera anticuado e inseguro; es preferible poner func(void). La lista indica el tipo y el nombre de cada parámetro, separados por comas.


5
{...} Cuerpo de la función; contiene la parte del programa (declaraciones y sentencias) que se ejecutará cuando la función sea invocada.

Nota: los elementos 1 y 2 pueden intercambiar su orden, es decir, las dos definiciones que siguen son equivalentes:

static int func(char ch, int x) { ... }
int static func(char ch, int x) { ... }


Como puede verse, algunos elementos de la declaración son opcionales; una función mínima, que no devuelve nada y no hace nada,  sería:

void minima (void) {} ;


§3  La diferencia esencial entre definición y declaración es que la definición incluye el cuerpo de la función. Está claro que la declaración (prototipo) y definición deben coincidir. No es necesario que coincidan los nombres de los parámetros en el prototipo y en la definición; de hecho, en C++ no es imprescindible que se incluyan los nombres de los parámetros en el prototipo, sin embargo se recomienda hacerlo, y utilizar nombres lo más descriptivos posible (ver ejemplo ).

Nota: si el prototipo de la función no coincide con la definición, C++ puede detectarlo, si y solo si, la definición está en la misma unidad de compilación que el prototipo. Por contra, si como ocurre en muchas ocasiones, la definición está en una librería o en otro módulo, este tipo de discordancias pueden no ser detectados, con la consiguiente posibilidad de errores impredecibles. Si se crea una librería de funciones y su correspondiente fichero de cabecera con los prototipos, considere incluir el fichero de cabecera a la hora de compilar la librería; de esta forma podrá ser detectada cualquier discrepancia entre los prototipos y las definiciones.

C++ proporciona un enlazado de tipo seguro, de forma que las diferencias entre los argumentos formales y actuales pueden ser denunciados por el enlazador.

§4  Ejemplo:

char* strcpy(char* destino, const char* origen);  // prototipo
...
int main() {            // ==============
  ..
  strcpy(ptr1, ptr2);
}
 
char* strcpy(char* d, const char* s){   // definición
  while (*s != 0) {
    *d = *s;
    s++ ; p++ ;
  }
}

§5  Comentario

Por lo general, sobre todo cuando se utilizan librerías de terceros, las declaraciones de las funciones, clases y macros de la librería se encuentran en ficheros de cabecera del tipo xxxx.h, mientras que las definiciones se encuentran en la librería propiamente dicha. Es decir, en ficheros tipo .lib; .a; .dll, etc.  Para poder utilizar los recursos de la librería, por ejemplo, sus funciones, es necesario incluir la correspondiente directiva #include xxxx.h en el fuente. Sin embargo, esto no es suficiente. Recordemos que además se debe instruir al linker para que enlace la librería correspondiente junto con los módulos de nuestros fuentes [3].  De no hacerlo así se obtienen errores de compilación.  El tipo de error depende del compilador, pero generalmente da pistas suficientes sobre las causas.  Por ejemplo, si pretendemos utilizar en nuestro fuente una función foo() que se encuentra en una librería somelib.dll, y olvidamos incluir el fichero de cabecera correspondiente somelib.h (o el fichero no existe), obtendremos un error del siguiente tenor (compilador GNU gcc):

nn  C:\.....\fichero.cpp `foo' undeclared (first use this function)

nn es el número de línea del fuente C:\.....\fichero.cpp donde se encuentra la primera referencia a la función en cuestión.  Aquí la clave es la palabra "undeclared"; se trata de un error de compilación en el que el compilador indica algo así: "no tengo ni idea de que se trata".  Sin embargo, si incluimos la cabecera, pero olvidamos indicar al enlazador que incluya la librería (donde se encuentran las definiciones), el error mostrado sería el siguiente:

[Linker error] undefined reference to `foo' 

Como vemos, aquí la clave está en la palabra "undefined"; es un error de enlazado, indicativo de que el enlazador no ha encontrado la definición en ningún sitio a pesar de que la función estuviese correctamente declarada. El enlazador viene a decir:  "se de qué se trata pero no encuentro los detalles".

  Inicio.


[1]  Esta regla C++ de "una sola definición", según la cual cualquier cosa puede declararse todas las veces que se desee en un programa, pero solo puede definirse una vez en el mismo ámbito, es válida para cualquier objeto. Tenga en cuenta que los casos de sobrecarga no suponen ninguna violación de esta regla, dado que simplemente se trata de objetos homónimos pero distintos, que el compilador sabe distinguir en base a otras características distintas del nombre.

Una excepción a la regla, que permitiría la existencia de dos funciones del mismo nombre en el mismo programa (en ficheros distintos), sería que una de ellas fuese visible solo en su fichero; bien por ser declarada static ( 4.1.8c), bien por utilizar un espacio de nombres anónimo ( 4.1.11b).

[2]  Esto significa que por defecto las funciones tienen enlazado externo, por lo que un nombre de función tiene el mismo significado (se refiere al mismo objeto) a través de todos los módulos del programa (Enlazado 1.4.4). Por esta razón, los nombres de funciones deben ser únicos.

[3]  Puede verse una descripción pormenorizada del proceso en los capítulos dedicados a las librerías. Capítulo 1.4.4b y siguientes ( 1.4.4b)