• No se han encontrado resultados

ENSAMBLADOR Y C++ 155 a funci´ on, ´ el busca una funci´ on donde emparejen los tipos de los argumentos

Punto flotante

7.2. ENSAMBLADOR Y C++ 155 a funci´ on, ´ el busca una funci´ on donde emparejen los tipos de los argumentos

pasados a la funci´on.5 Si se empareja entonces crea un CALL a la funci´on correcta usando las reglas de manipulaci´on del compilador.

Ya que cada compilador usa sus reglas de manipulaci´on de nombres, los archivos objeto de C++ generados por diferentes compiladores no se pueden unir, el c´odigo de C++ compilado con compiladores diferentes no es posible unirlo. Este hecho es importante cuando se considera usar una biblioteca de C++ precompilada. Si uno desea escribir una funci´on en ensamblador que ser´a usada con c´odigo C++, debe conocer las reglas de manipulaci´on de nombres del compilador (o usar la t´ecnica explicada abajo).

El estudiante astuto puede preguntar si el c´odigo de la Figura 7.10 tra- bajar´a como se espera. Ya que C++ manipula los nombres de todas las funciones, entonces la funci´on printf ser´a manipulada y el compilador no producir´a unCALL a la etiqueta printf. Esto es un v´alido. Si el prototipo para printffue colocado simplemente al principio del archivo, esto podr´ıa pasar. El prototipo es:

int printf ( const char ∗, ...);

DJGPP podr´ıa manipular esto como printf FPCce(laFes porfunci´on,P por apuntador,Cporconstante,cporchar yepor elipsis). Esto podr´ıa no llamar la funci´onprintfde la biblioteca de C. Claro est´a, debe haber alguna forma para el c´odigo de C++ llamar c´odigo de C. Esto es muy importante ya que hay una gran cantidad de c´odigo de C ´util. Adem´as permitirle a uno llamar c´odigo de C heredado, C++ tambi´en le permite a uno llamar c´odigo de ensamblador usando las convenciones normales de llamado.

C++ extiende la palabra clave externpara permitir especificar que la funci´on o variable global que modifica usa las convenciones de llamado de C. En la terminolog´ıa de C++, las variables y funciones globales usan el encadenamiento de C. Por ejemplo para declarar queprintftenga el enca- denamiento de C, use el prototipo:

extern ”C”int printf ( const char∗, ... );

Esto le dice al compilador que no use las reglas de manipulaci´on de nombres en esta funci´on, y en su lugar usar las reglas de C. Sin embargo, haciendo esto, la funci´onprintfno se puede sobrecargar. Esto suministra una manera f´acil de interfazar C++ y ensamblador, define la funci´on para que use el encadenamiento de C y la convenci´on de llamado de C.

Por conveniencia C++ tambi´en permite el encadenamiento de un bloque de funciones y variables globales a ser definidas. El bloque se enmarca por las t´ıpicas llaves:

5El emparejamiento no tiene que ser exacto, el compilador considerar´a las conversiones

de tipos de los argumentos. Las reglas para este proceso est´an m´as all´a del alcance de este libro. Consulte un libro de C++ para los detalles.

void f ( int & x ) // el & denota una referencia { x++;}

int main()

{

int y = 5;

f (y ); // se pasa la referencia a y, obeserve que // no hay & ac´a

printf (” %d\n”, y); // imprime 6! return 0;

}

Figura 7.11: Ejemplo de referencia

extern”C”{

/∗ encadenamiento tipo C a variables globales y prototipos de funciones ∗/ }

Si uno examina los archivos de encabezado de ANSI C que vienen con los compiladores de C/C++ de hoy d´ıa, en ellos se encontr´a al principio de cada archivo de cabecera:

#ifdef cplusplus

extern”C”{ #endif

Y una construcci´on parecida cerca del final conteniendo el fin de la llave. Los compiladores de C++ definen el macro cplusplus (con dos guiones bajos adelante). La porci´on de c´odigo anterior encierra todo el archivo de cabecera en un bloqueextern Csi el archivo de cabecera es compilado como C++, pero no hace nada si es compilado como C (ya que el compilador de C dar´ıa un error de sintaxis paraextern C). Esta misma t´ecnica puede ser usada por cualquier programador para crear un archivo de cabecera para las rutinas de ensamblador que pueden ser usadas con C o C++.

7.2.2. Referencias

Las referencias son otra caracter´ıstica nueva C++. Ellas le permiten a uno pasar par´ametros a funciones sin usar apuntadores expl´ıcitamente. Por ejemplo, considere el c´odigo de la Figura 7.11. Los par´ametros por referencia son muy simples, ellos realmente son tan solo apuntadores. El compilador solo le oculta esto al programador (tal como los compiladores de Pascal implementan los par´ametros varcomo apuntadores). Cuando el compilador genera el ensamblador para el llamado a la funci´on en la l´ınea 7, pasa la

7.2. ENSAMBLADOR Y C++ 157 direcci´on de y. Si uno escribi´o la funci´on f en ensamblador, ella se deber´ıa comportar como si el prototipo fuera6:

void f ( int ∗ xp);

Las referencias son s´olo una conveniencia que es especialmente ´util para la sobrecarga de operadores. Esta es otra caracter´ıstica de C++ que le per- mite a uno definir el significado de los operadores comunes en estructuras o tipos de clases. Por ejemplo, un uso com´un es definir el operador m´as (+) para unir objetos tipo cadena. As´ı, siayb fueran cadenas, a + bdeber´ıa retornar la uni´on de las cadenasay b. C++ llama una funci´on para hacer esto (de hecho, esta expresi´on podr´ıa ser reescrita en la notaci´on de funci´on comooperator +(a,b)). Por eficiencia uno podr´ıa querer pasar la direcci´on de los objetos cadena en lugar de pasarlos por valor. Sin referencias esto se podr´ıa hacer como operator +(&a,&b), pero esto requerir´ıa escribir en la sintaxis del operador como &a + &b. Esto ser´ıa muy complicado y confuso. Sin embargo, usando referencias, uno puede escribir a + b que se ve muy natural.

7.2.3. Funciones inline

Lasfunciones inline son otra caracter´ıstica de C++7. Las funciones in- line son hechas para reemplazar, los macros basados en el procesador que toman par´ametros que son muy propensos a tener errores. Recuerde de C, que escribir un macro que eleva al cuadrado un n´umero podr´ıa verse como:

#defineSQR(x) ((x)∗(x))

Debido a que el preprocesador no entiende C y hace simples sustituciones, se requieren los par´entesis para calcular la respuesta correcta en la mayor´ıa de los casos. Sin embargo, a´un esta versi´on no dar´a la respuesta correcta para SQR(X++).

Los macros se usan porque ellos eliminan el trabajo extra de hacer un lla- mad a funci´on para una funci´on elemental. Como se demostr´o en el cap´ıtulo de subprogramas realizar un llamado a funci´on involucra varios pasos. Por cada funci´on elemental, el tiempo que se toma en llamar la funci´on puede ser mayor que el tiempo necesario en realizar las operaciones en la funci´on. La funci´on inline es una manera mucho m´as amigable de escribir c´odigo que se vea como una funci´on normal, pero que no hace el bloque de c´odigo para CALL. En lugar de ello los llamados a funciones inline son reemplazados por el c´odigo que realiza la funci´on. C++ le permite a una funci´on ser inline

6

Claro est´a ella podr´ıa esperar declarar la funci´on con encadenamiento C para evitar la manipulaci´on de nombres discutida en la secci´on 7.2.1

7

Los compiladores de C a menudo soportan esta caracter´ıstica como una extensi´on de ANSI C

inline int inline f ( int x ) { return x∗x; } int f ( int x ) { return x∗x; } int main() { int y, x = 5; y = f(x ); y = inline f (x ); return 0; }

Figura 7.12: ejemplo inline

colocando la palabra inline clave al frente de la definici´on de la funci´on. Por ejemplo considere las funciones declaradas en la Figura 7.12. El llamado a la funci´on f en la l´ınea 10 hace un llamado normal a una funci´on (en ensamblador, asumiendo quexest´a en la direcci´onebp-8eyest´a enebp-4:

1 push dword [ebp-8]

2 call _f

3 pop ecx

4 mov [ebp-4], eax

Sin embargo, el llamado a la funci´on inline f de la l´ınea 11 podr´ıa verse como:

1 mov eax, [ebp-8] 2 imul eax, eax 3 mov [ebp-4], eax

En este caso hay dos ventajas de las funciones inline. Primero la funci´on en l´ınea es r´apida. No se colocan los par´ametros en la pila, no se crea el marco de la pila ni se destruye, no se hace el salto. Segundo el llamado de las funciones en l´ınea usa menos c´odigo. Este ´ultimo punto es verdad para este ejemplo pero no siempre es cierto.

La principal desventaja de las funciones inline es que el c´odigo en l´ınea no se encadena y as´ı el c´odigo de una funci´on en l´ınea debe estar disponible

7.2. ENSAMBLADOR Y C++ 159

class Simple{ public:

Simple (); // Construcctor por omisi´on

˜Simple(); // destructor

int get data () const; // funciones miembro void set data ( int );

private:

int data; // datos miembro };

Simple :: Simple()

{ data = 0; }

Simple ::˜ Simple()

{ /∗ cuerpo vac´ıo ∗/ } int Simple :: get data () const { return data; }

void Simple :: set data ( int x )

{ data = x; }

Figura 7.13: Una clase simple de C++

en todos los archivos que la usen. El ejemplo anterior prueba esto. El llama- do de una funci´on normal solo requiere el conocimiento de los par´ametros, el tipo de valor de retorno, la convenci´on de llamado y el nombre de la eti- queta para la funci´on. Toda esta informaci´on est´a disponible en el prototipo de la funci´on. Sin embargo al usar funciones inline se requiere conocer todo el c´odigo de la funci´on. Esto significa que si cualquier parte de una funci´on en l´ınea se cambia, todos los archivos fuentes que usen la funci´on deben ser recompilados. Recuerde que para las funciones no inline, si el prototipo cambia, a menudo los archivos que usan la funci´on no necesitan ser recompi- lados. Por todas estas razones, el c´odigo de las funciones en l´ınea se colocan normalmente en los archivos de cabecera. Esta pr´actica es contraria a la regla estricta de C las instrucciones de c´odigo ejecutable nunca se colocan en los archivos de cabecera.

void set data ( Simple ∗ object , int x )

{

object−>data = x;

}

Figura 7.14: Versi´on de C de Simple::set data() 1 _set_data__6Simplei: ; nombre manipulador

2 push ebp

3 mov ebp, esp 4

5 mov eax, [ebp + 8] ; eax = apuntador al objeto (this) 6 mov edx, [ebp + 12] ; edx = par´ametro entero

7 mov [eax], edx ; data tiene un desplazamiento de 0 8

9 leave

10 ret

Figura 7.15: Salida del compilador para Simple::set data( int )

7.2.4. Clases

Una clase en C++ describe un tipo de objeto. Un objeto tiene como miembros a datos y funciones8. En otras palabras, es una estructura con datos y funciones asociadas a ella. Considere la clase definida en la Figu- ra 7.13. Una variable de tipoSimplese podr´ıa ver tal como unaestructura normal en C con un solo int como miembro. Las funciones no son alma-

En efecto, C++ usa la pa- labra clavethispara acce- der al apuntador de un ob- jeto que est´a dentro de la funci´on miembro.

cenadas en la memoria asignada a la estructura. Sin embargo las funciones miembro son diferentes de las otras funciones . Ellas son pasadas como un par´ametro oculto. Este par´ametro es un apuntador al objeto al cual la fun- ci´on pertenece.

Por ejemplo considere el m´etodo set data de la claseSimple de la Fi- gura 7.13. Si se escribi´o en C se ver´ıa como una funci´on que fue pasada expl´ıcitamente a un apuntador al objeto como muestra la Figura 7.14. La opci´on -S en el compilador DJGPP (y en los compiladores gcc y Borland tambi´en) le dice al compilador que genere un archivo que contenga el len- guaje ensamblador equivalente del c´odigo producido. Para DJGPP y gcc el archivo ensamblador finaliza con una extensi´on .s y desafortunadamen- te usa la sintaxis del lenguaje ensamblador AT&T que es diferente de la

7.2. ENSAMBLADOR Y C++ 161