Muy a menudo necesitamos especificar valores u opciones a nuestros programas cuando los ejecutamos desde la línea de comandos.
Por ejemplo, si hacemos un programa que copie ficheros, del tipo del "copy" de MS-DOS, necesitaremos especificar el nombre del archivo de origen y el de destino.
Hasta ahora siempre hemos usado la función "main" sin parámetros, sin embargo, como veremos ahora, se pueden pasar argumentos a nuestros programas a través de los parámetros de la función main.
Para tener acceso a los argumentos de la línea de comandos hay que declararlos en la función "main", la manera de hacerlo puede ser una de las siguientes:
int main(int argc, char *argv[]); int main(int argc, char **argv);
Que como sabemos son equivalentes.
El primer parámetro, "argc" (argument counter), es el número de argumentos que se han especificado en la línea de comandos. El segundo, "argv", (argument values) es un array de cadenas que contiene los argumentos especificados en la línea de comandos.
Por ejemplo, si nuestro programa se llama "programa", y lo ejecutamos con la siguiente línea de comandos:
programa arg1 arg2 arg3 arg4
argc valdrá 5, ya que el nombre del programa también se cuenta como un argumento.
argv[] contendrá la siguiente lista: "C:\programasc\programa", "arg1", "arg2", "arg3" y "arg4".
Ejemplo:
#include <iostream> using namespace std; int main(int argc, char **argv) { for(int i = 0; i < argc; i++) cout << argv[i] << " "; cout << endl; }
Cuando escribimos el nombre de una función dentro de un programa decimos que "llamamos" a esa función. Esto quiere decir que lo que hace el programa es "saltar" a la función, ejecutarla y retornar al punto en que fue llamada.
Esto es cierto para las funciones que hemos usado hasta ahora, pero hay un tipo especial de funciones que trabajan de otro modo. En lugar de existir una única copia de la función dentro del código, cuando se declara una función como "inline" lo que se hace es insertar su código en el lugar en que se realiza la llamada, en lugar de invocar a la función.
Sintaxis:
inline <tipo> <nombre_de_funcion>(<lista_de_parámetros>);
Esto tiene la ventaja de que la ejecución es más rápida, pero por contra, el programa generado es más grande. Se debe evitar el uso de funciones "inline" cuando éstas son de gran tamaño, aunque con funciones pequeñas es recomendable, ya que se suelen producir programas más rápidos. Su uso es frecuente cuando las funciones tienen código en ensamblador, ya que en estos casos la optimización es mucho mayor.
En algunos casos, si la función es demasiado larga, el compilador puede decidir no insertar la función, sino simplemente llamarla. El uso de "inline" no es por lo tanto una obligación para el compilador, sino simplemente una recomendación.
Aparentemente, una función "inline" se comportará como cualquier otra función. De hecho, es incluso posible obtener un puntero a una función declara inline.
Nota: "inline" es exclusivo de C++, y no está disponible en C.
Ejemplos:
#include <iostream> using namespace std; inline int mayor(int a, int b) { if(a > b) return a; else return b; } int main() { cout << "El mayor de 12,32 es " << mayor(12,32) << endl; cout << "El mayor de 6,21 es " << mayor(6,21) << endl; cout << "El mayor de 14,34 es " << mayor(14,34) << endl; cin.get(); return 0; }
Tanto en C como en C++ se pueden declarar punteros a funciones.
Sintaxis:
<tipo> (*<identificador>)(<lista_de_parámetros>);
De esta forma se declara un puntero a una función que devuelve un valor de tipo <tipo> y acepta la lista de parámetros especificada. Es muy importante usar los paréntesis para agrupar el identificador, ya que de otro modo estaríamos declarando una función que devuelve un puntero al tipo especificado y que admite la lista de parámetros indicada.
No tiene sentido declarar variables de tipo función, es decir, la sintaxis indicada, prescindiendo del '*' lo que realmente declara es un prototipo, y no es posible asignarle un valor a un prototipo, como se puede hacer con los punteros, sino que únicamente podremos definir la función.
Ejemplos:
int (*pfuncion1)(); (1) void (*pfuncion2)(int); (2) float *(*pfuncion3)(char*, int); (3) void (*pfuncion4)(void (*)(int)); (4) int (*pfuncion5[10])(int); (5)
El ejemplo 1 declara un puntero, "pfuncion1" a una función que devuelve un "int" y no acepta parámetros.
El ejemplo 2 declara un puntero, "pfuncion2" a una función que no devuelve valor y que acepta un parámetro de tipo "int".
El ejemplo 3 a una función que devuelve un puntero a "float" y admite dos parámetros: un puntero a "char" y un "int".
El 4, declara una función "pfuncion4" que no devuelve valor y acepta un parámetro. Ese parámetro debe ser un puntero a una función que tampoco devuelve valor y admite como parámetro un "int".
El 5 declara un array de punteros a función, cada una de ellas devuelve un "int" y admite como parámetro un "int".
Este otro ejemplo:
int *(pfuncionx)();
Equivale a:
int *pfuncionx();
Que, claramente, es una declaración de un prototipo de una función que devuelve un puntero a "int" y no admite parámetros.
La utilidad de los punteros a funciones se manifiesta sobre todo cuando se personalizan ciertas funciones de librerías. Podemos por ejemplo, diseñar una función de librería que admita como parámetro una función, que debe crear el usuario (en este caso otro programador), para que la función de librería complete su funcionamiento.
Este es el caso de la función "qsort", declarada en "stdlib".Si nos fijamos en su prototipo:
void qsort(void *base, size_t nmemb, size_t tamanyo, int (*comparar)(const void *, const void *));
Vemos que el cuarto parámetro es un puntero a una función "comparar" que devuelve un "int" y admite dos parámetros de tipo puntero genérico.
Esto permite a la librería "stdlib" definir una función para ordenar arrays independientemente de su tipo, ya que para comparar elementos del array se usa una función definida por el usuario, y "qsort" puede invocarla después.
Una vez declarado uno de estos punteros, se comporta como una variable cualquiera, podemos por lo tanto, usarlo como parámetro en funciones, o asignarle valores, por supuesto, del mismo tipo.
int funcion(); ... int (*pf1)(); // Puntero a función sin argumentos // que devuelve un int. pf1 = funcion; // Asignamos al puntero pf1 la // función "funcion" ... int funcion() { return 1; }
La asignación es tan simple como asignar el nombre de la función.
Nota: Aunque muchos compiladores lo admiten, no es
recomendable aplicar el operador de dirección (&) al nombre de la
función
pf1 = &funcion;.
La forma propuesta en el ejemplo es la
recomendable.
Para invocar a la función usando el puntero, sólo hay que usar el identificador del puntero como si se tratase de una función. En realidad, el puntero se comporta exactamente igual que un "alias" de la función a la que apunta.
int x = pf1();
De este modo, llamamos a la función "funcion" previamente asignada a *pf1.
Ejemplo completo:
#include <iostream> using namespace std; int Muestra1(); int Muestra2(); int Muestra3(); int Muestra4(); int main() { int (*pf1)(); // Puntero a función sin argumentos que devuelve un int. int num; do { cout << "Introduce un número entre 1 y 4, " << "0 para salir: "; cin >> num; if(num >= 1 && num <=4) { switch(num) { case 1: pf1 = Muestra1; break; case 2: pf1 = Muestra2; break; case 3: pf1 = Muestra3; break; case 4: pf1 = Muestra4; break; } pf1(); } } while(num != 0); return 0; } int Muestra1() { cout << "Muestra 1" << endl; return 1; } int Muestra2() { cout << "Muestra 2" << endl; return 2; } int Muestra3() { cout << "Muestra 3" << endl; return 3; } int Muestra4() { cout << "Muestra 4" << endl; return 4; }
Otro ejemplo:
#include <iostream> using namespace std; int Fun1(int); int Fun2(int); int Fun3(int); int Fun4(int); int main() { int (*pf1[4])(int); // Array de punteros a función con un // argumento int que devuelven un int. int num; int valores; pf1[0] = Fun1; pf1[1] = Fun2; pf1[2] = Fun3; pf1[3] = Fun4; do { cout << "Introduce un número entre 1 y 4, " << "0 para salir: "; cin >> num; if(num >= 1 && num <=4) { cout << "Introduce un número entre 1 y 10: "; cin >> valores; if(valores > 0 && valores < 11) pf1[num-1](valores); } } while(num != 0); return 0; } int Fun1(int v) { while(v--) cout << "1" << endl; return 1; } int Fun2(int v) { while(v--) cout << "Muestra 2" << endl; return 2; } int Fun3(int v) { while(v--) cout << "Muestra 3" << endl; return 3; } int Fun4(int v) { while(v--) cout << "Muestra 4" << endl; return 4; }
inline.
© Septiembre de 2000 Salvador Pozo, salvador@conclase.net