Invocación de funciones (programación)

Invocación de funciones (programación)
Información sobre la plantilla
Invocacion de Funciones.jpeg
Concepto:Implica pasarle el control de la ejecución del programa, así como los argumentos ó parámetros que requiere para realizar su tarea

Invocación de funciones (programación). Una invocación ó llamada a una función implica pasarle el control de la ejecución del programa, así como los argumentos ó parámetros que requiere para realizar su tarea, se realiza colocando el nombre de la función y los argumentos actuales en el mismo orden que los parámetros formales correspondientes. La sintaxis del lenguaje permite también la invocación de funciones a través de punteros a funciones e incluso de referencias, aunque esto último sea menos frecuente. Cuando las funciones son miembros de clases la invocación sigue una sintaxis especial. En estos casos incluso existen operadores especiales para invocarlas a través de sus punteros.

Evaluación de argumentos

La gramática C++ permite utilizar expresiones como argumentos en la invocación de funciones. Estas expresiones son evaluadas, y sus posibles efectos laterales tienen efecto, antes que la función sea cargada en la pila. Sin embargo, tales prácticas son en general desaconsejadas, pues dan lugar a código difícil de leer. Ejemplo:

int x foo (int x) { return x+2; }
...
int main() {
 int n = 3;
 cout << "foo -> " << foo(n++) << endl;
 cout << "foo -> " << foo(++n) << endl;
 ...
}

Salida:

foo ->5
foo ->7

El orden de evaluación de los argumentos es indefinido (depende del compilador), por lo que no es recomendable utilizar expresiones que dependan del orden de evaluación de los parámetros. Ejemplo:

int x foo (int x, int y) { return x + y; }
...
int main() {
 int n = 3;
 cout << "foo -> " << foo(n++, n) << endl;
 ...
}

En estas condiciones es imposible predecir si la salida será 7 u 8.

Conversión de argumentos

A continuación de la evaluación, los valores resultantes de las expresiones son convertidos automáticamente a los mismos tipos que los declarados para los parámetros formales. Ejemplo (suponiendo la definición del caso anterior):

cout << foo(x + 3.5);    // -> 8

Cuando no se ha declarado previamente un prototipo de la función, C++ realiza una invocación a la función convirtiendo la totalidad de los argumentos según las reglas de Conversiones aritméticas estándar. En cambio, si existe un prototipo de función en el ámbito, C++ convierte los argumentos al tipo declarado para los parámetros.

Cuando un prototipo incluye puntos suspensivos (...), el compilador convierte todos los argumentos (si los hay) como en cualquier otro caso hasta la elipsis, después conforma todos los demás parámetros (que han sido declarados variables) según las reglas usuales para argumentos de funciones que no tienen prototipo.

Si existe un prototipo, el número de argumentos debe coincidir con los declarados (a menos que existan puntos suspensivos). El tipo de los argumentos también debe coincidir, pero solo hasta el punto en que una asignación pudiera realizar legalmente una conversión (del tipo realmente pasado al tipo esperado). También existe el recurso de hacer una conversión explícita ("cast") para convertir un argumento en un tipo que sea aceptable por el prototipo (Modelado de tipos).

En C++ los parámetros son pasados por valor, lo que significa que existen copias locales de los argumentos formales, estas copias son variables locales de la función invocada.

Conversión de parámetros

Es posible incluir sentencias de asignación en la lista de argumentos de invocación de funciones, aunque sea una práctica desaconsejable, pues da lugar a código difícil de interpretar y en ocasiones sin mucho sentido.

Por ejemplo, en C++ es válido el siguiente código que compila sin problema:

#include <iostream.h>
void fun (char* p, int n) {
 for (int i = 1; i <= n ; i++) cout << p << i << endl;
}
void main() {              // =============
 char* pt1 = "Hola, que tal!! ";
 char* pt2;
 int x;
 fun(pt2 = pt1, x =5);   // desaconsejado !!
}
Salida:
Hola, que tal!! 1
Hola, que tal!! 2
Hola, que tal!! 3
Hola, que tal!! 4
Hola, que tal!! 5

Formas de invocación de funciones

La forma de efectuarse la invocación de funciones y el tratamiento de identificadores globales, son cuestiones que están relacionadas, ya que ciertas formas de llamada presuponen determinado tratamiento de los identificadores.

Estos detalles tienen importancia cuando se quiere mezclar código C++ con el generado por otros lenguajes de programación, bien porque necesitemos llamar una rutina en otro lenguaje desde un programa C++, o porque desde otro lenguaje necesitemos utilizar una rutina escrita en C++. La razón es que todos los lenguajes no se comportan de la misma forma, variando ciertas cuestiones de detalle.

Estos detalles se refieren concretamente a:

  • El tratamiento dado a los identificadores
  • Convención de llamada a utilizar, que compone tres cuestiones: Limpieza de la pila; paso de parámetros, y tratamiento de los identificadores globales.

Los detalles sobre la forma de proceder en cada caso dependen de la plataforma. En la mayoría de compiladores es posible fijar ciertas directrices sobre la forma de proceder en estos casos, tanto a nivel global como a nivel particular de algunos identificadores. Los comentarios que siguen se refieren al compilador Borland C++, aunque salvando algunas cuestiones de detalle, pueden hacerse extensivos al resto de plataformas (MS Visucal C++, GNU g++, etc.)

Tratamiento de identificadores

Una característica que distingue a unos compiladores (lenguajes) de otros, es el tratamiento dado a los identificadores (nombres) de los objetos; lo que se conoce como sistema de codificación de nombres ("name encoding scheme"). De este sistema depende que durante las fases intermedias de la compilación, los identificadores sean guardados tal como los escribe el programador o sufran mutaciones más o menos importantes.

En C++, cuando está activada la opción -u (lo que ocurre por defecto), el compilador guarda todos los identificadores globales en su grafía original (mayúsculas, minúsculas o mixta), añadiendo automáticamente un guión bajo "_" ("Underscore" ASCII 95) delante de cualquier identificador global, ya sea de función (todas lo son), o de variable. Para modificar este comportamiento se puede utilizar la opción -u- como parámetro en la línea de comando del compilador.

En el caso de identificadores que han sido modificados con el identificador Pascal, no se añade ningún guión, pero el identificador es convertido a mayúsculas

Convención de llamada

En informática se han consagrado diversas formas de invocación de funciones, las más frecuentes son las siguientes: Rápida; C; Pascal; Registro y Estándar.

Estas convenciones se diferencian en:

  • La forma que cada una utiliza para la limpieza de la pila (stack).
  • El orden de paso de parámetros (derecha a izquierda o a la inversa).
  • El uso o no de mayúsculas y minúsculas, y ciertos prefijos en los identificadores globales.

La especificación de la forma que se utilizará en el programa, puede hacerse a nivel global o solo a nivel particular de algunas funciones específicas.

Para indicarlo a nivel global se utiliza alguno de los comandos específicos del compilador (son los indicados en cada caso). En estos casos, las palabras clave: _ _pascal, _ _fastcall, o _ _stdcall pueden utilizarse para declarar que una rutina o función utiliza específicamente una convención distinta de la señalada como general.

La forma de indicarlo a nivel particular es mediante el uso de ciertas palabras reservadas para que sea utilizada una forma específica en lugar de la que tenga asignada el compilador por defecto. Estas palabras deben indicarse en la declaración o prototipo, y delante del especificador de invocación de la función. Son las siguientes: _ _cdecl, _ _pascal, _ _fastcall, _ _msfastcall y _ _stdcall.

Invocación rápida

Esta opción se ha incluido en el compilador C++ para compatibilidad con el de Microsoft. Indica al compilador que utilice convención de llamada de MS VC++ para todas las funciones que no tengan explícitamente declarada otra forma. En esta convención, las dos primeras DWORD o argumentos más pequeños pasan a los registros ECX y EDX; todos los demás pasan de derecha a izquierda. La función invocada es responsable de desalojar los argumentos de la pila.


Formas de indicarlo al compilador:

  • Especificador global: comando de compilación -pm
  • Especificador particular: especificador _ _msfastcall

Ejemplo:

int __msfastcall funcDos (x, y);

Invocación C

Esta opción indica al compilador utilizar la secuencia de llamada estándar C para funciones, es decir: generar guiones de subrayado -guiones bajos-; distinguir mayúsculas de minúsculas (no transformar minúsculas en mayúsculas); pasar parámetros de derecha a izquierda.En C++ se utiliza por defecto esta convención de llamada.

En este tipo de invocación, la colocación de parámetros en la pila se realiza de derecha a izquierda, lo que significa que el último argumento de la función es colocado el primero y el primero es colocado el último. Esto hace que estas funciones puedan utilizar un número variable de parámetros en cada invocación. Es decir, no tienen que pasar necesariamente el mismo número de parámetros en todas las invocaciones que se realicen a dicho código. Las funciones que gozan de esta particularidad son denominadas "variadic" en la literatura inglesa. La función que realiza la llamada ("caller") es la encargada de limpiar la pila, lo que provoca que el código de este tipo de ejecutables sean ligeramente mayores que en el resto de convenciones, en las que es la función invocada ("called") la que limpia la pila.

Formas de indicarlo al compilador:

  • Especificador global: comandos de compilación -pc, -p-
  • Especificador particular: especificadores cdecl, _cdecl y _ _cdecl en la declaración de la función o identificador global (las tres formas son equivalentes).

Invocación Pascal

Esta opción indica al compilador utilizar la secuencia de llamada de Pascal para las funciones: no generar guiones de subrayado, forzar identificadores a mayúsculas, limpieza de la pila por la función que realiza la llamada y paso de parámetros de izquierda a derecha.

Generalmente las llamadas de función resultantes son más pequeñas y rápidas que las generadas con la convención C, y también utilizan la pila para el paso de parámetros. En este caso, las funciones deben pasar el número exacto de argumentos y del tipo adecuado. Los identificadores de las funciones declaradas con este modificador están sujetos al planchado de nombres.

En la programación para entornos Windows 3.x se exigía que las llamadas al Sistema utilizaran esta convención.

Formas de indicarlo al compilador:

  • Especificador global: comando de compilación -p
  • Especificador particular: especificadores pascal, _pascal o _ _pascal (las tres formas son equivalentes).

Invocación Registro

Esta opción indica que se deben generar todas las funciones utilizando la convención de paso de parámetros a registro. Esto supone que los tres primeros parámetros (de izquierda a derecha) se colocan en los registros EAX, EDX y ECX. En caso que se trate de objetos que no quepan en los registros, por ejemplo, si son números fraccionarios o estructuras, no se utilizan los registros, sino la pila como es usual.

Las funciones declaradas con los identificadores _cdecl o _pascal no pueden utilizar simultáneamente este especificador, porque ambas utilizan la pila para el paso de parámetros. Los identificadores de estas funciones están sujetos al planchado de nombres, además el compilador les añade una arroba '@' como prefijo.

Formas de indicarlo al compilador:

  • Especificador global: comando de compilación -pr
  • Especificador particular: especificadores _fastcall y _ _fastcall (ambas formas son equivalentes).

Ejemplo:

int _msfastcall funcTres (x, y, z);

Invocación estándar

Esta opción indica al compilador que debe utilizar la secuencia de llamada estándar para funciones: no generar guiones bajos; conservar mayúsculas y minúsculas; la función invocada limpia la pila, y colocación de parámetros de derecha a izquierda.

A diferencia de la invocación C, esta convención exige pasar el número y tipo de argumentos exacto. Estas funciones cumplen con la convención de argumentos de Win32, y están sujetas a planchado de nombres.

Formas de indicarlo al compilador:

  • Especificador global: comando de compilación -ps
  • Especificador particular: especificador _ _stdcall. y _stdcall

Ejemplo:

#define WINAPI __stdcall
#include <windows.h>
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow) {
  MessageBox (NULL, TEXT ("Hola Windows 98"), TEXT ("Saludo"), 0);
  return 0;
}

Dado que en la programación Windows, la función WinMain es equivalente a la main de C/C++, la sentencia anterior constituye el inicio de cualquier aplicación Windows. Su sintaxis señala explícitamente al compilador que debe utilizar para ella la convención de llamada estándar.

Puede Consultar

Fuentes

Artículos consultados

  • www.zator.com/Cpp/E4_4_6.htm Invocación de funciones y conversión de argumentos]
  • www.zator.com/Cpp/E4_4_6a.htm Formas de invocación de funciones]
  • www.sistemas.itlp.edu.mx/tutoriales/pascal/u6_6_2_2.html Invocación de funciones]

Enlaces externos