You are on page 1of 16

Universidad de Buenos Aires Facultad De Ingenier a

Programando en C a Bajo Nivel


[75.40] Algoritmos y Programacin I o 1er Cuatrimestre 2011 Ctedra: Ing. Pablo Guarna a

Autor: Bernardo Ortega Moncada

Versin 3.0 o

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

Indice
1. Introduccin o 2. Representacin de N meros en Base Binaria o u 2.1. Complementos A1 de un Nmero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . u 3. Sentencias Bit a Bit en C 3.1. Operacin AND (Bit a Bit) (&) . . . . . . . . . . . . o 3.2. Operacin OR (Bit a Bit) (|) . . . . . . . . . . . . . o 3.3. Operacin XOR (bit a bit) ( ) . . . . . . . . . . . o 3.4. Operacin Complemento A1 a la Base ( ) . . . . o 3.5. Desplazamiento de Bits . . . . . . . . . . . . . . . . 3.5.1. Desplazamiento de Bits a la Derecha ( >> ) . 3.5.2. Desplazamiento de Bits a la Izquierda ( < < ) 3.6. Operador Ternario (? :) . . . . . . . . . . . . . . . . 3.6.1. Ejemplo . . . . . . . . . . . . . . . . . . . . . 3.7. Campos de Bits . . . . . . . . . . . . . . . . . . . . . 3.7.1. Limitaciones de Campos de Bits . . . . . . . 3.8. Ventajas y Desventajas de Operar Bit a Bit . . . . . 4. El Preprocesador de C 4.1. Que Es Un Macro? . . . . . . . . . . . . . . 4.2. Ejemplos de Macros . . . . . . . . . . . . . . 4.2.1. Inclusin de Archivos . . . . . . . . . o 4.2.2. Creacin de Archivos de Cabecera . . o 4.2.3. Creacin de Macros como Constantes o 4.2.4. Creacin de Macros como Funcin . . o o 4.3. Otras Directivas del Preprocesador . . . . . . 4.3.1. #undef . . . . . . . . . . . . . . . . . 4.3.2. #if , #else , #elif . . . . . . . . . . . 4.3.3. #error . . . . . . . . . . . . . . . . . . 4.4. Ventajas y Desventajas de Usar Macros . . . 2 2 2 2 3 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 11 11 12 14 14 14 14 15

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

Autor: Bernardo Ortega Moncada

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

1.

Introduccin o

Este apunte esta orientado para que el lector pueda apreciar como uno puede trabajar en el lenguaje de programacin o C con sentencias de Bajo Nivel y utilizar herramientas que pueden ser utiles a la hora de programar sentencias que podr an simplicar lineas operando a bajo nivel. Este apunte es de carcter informativo, no signica que lo vayan a usar en esta a materia, pero quizs en alguna posterior. Esta demas aclarar que para leer este apunte se necesita un conocimiento bsico a a previo del Lenguaje. Todas estas operaciones que se muestran en el apunte, tambin son vlidas en el Lenguaje C++ e a

2.

Representacin de N meros en Base Binaria o u

Como uno ya sabe, los nmeros en la computadora no se expresan de la misma forma que un ser humano escribe. Ya u que la computadora trabaja de forma binaria y solo puede comprender si algo es verdadero o falso, si hay informacin o o no, por lo tanto si uno esta trabajando con nmeros que ocupan 1 Byte, uno sabe que 1 Byte esta compuesto por 8 bits, u lo cual un bit solo puede almacenar 1 o 0. Por lo tanto si nosotros a modo de ejemplo tenemos el nmero, la computadora ve a este nmero de la siguiente manera: u u Representacin Decimal o 0|10 2|10 7|10 25|10 255|10 Representacin Binaria en 8 bits o 00000000|2 00000010|2 00000111|2 00011001|2 11111111|2

= = = = =

Pero tenemos que saber que un nmero representado en binario posee 2 bits muy importantes. El bit mas Signiu cativo (MSB, por sus siglas en ingles) y El Bit Menos Signicativo (LSB, por sus siglas en ingles). Una observacin a tener en cuenta es que el MSB tambin es considerado Bit de Signo, siempre y cuando se use la o e representacin MyBS (Mdulo y Bit de Signo). o o se considera al 1 como el signo negativo se considera al 0 como el signo positivo Por ejemplo, tenemos un numero representado en su forma binaria, podemos apreciar cuales son los bits signicativos: 1 MSB 0 0 1 0 1 0 1 LSB

2.1.

Complementos A1 de un N mero u

El complemento A1 de un numero representado en forma binaria, consiste en negar (o invertir) bit a bit la expresin o del nmero, en lineas generales consiste en cambiar los 0s por 1s y viceversa. Esta herramienta es muy poderosa en u el ambito de la computacin ya que nos permite representar un nmero de forma negativa, ya que como sabemos, la o u computadora no reconoce los nmeros negativos. Veamos el siguiente ejemplo de como calcular el complemento A1 a la u base de un numero binario cualquiera: 01100100 10011011 Complemento A1

3.

Sentencias Bit a Bit en C

Uno puede hacer una serie de operaciones lgicas bit a bit con el lenguaje C, e incluso realizar complementos A1, o o desplazamientos de Bits a la derecha o a la izquierda. Estas operaciones son muy comunes en el lenguaje de bajo nivel denominado Assembler (Ensamblador). A continuacin se muestra una tabla con algunas de las operaciones a bajo nivel o que podemos realizar:

Autor: Bernardo Ortega Moncada

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

Operador & | >> << ?:

Accin o Operacin AND (bit a bit) simula una compuerta AND - 74ls08 TTL o Operacin OR (bit a bit) simula una compuerta OR - 74ls32 TTL o Operacin XOR (bit a bit) simula una compuerta XOR - 74ls86 TTL o Complemento A1 a la base Desplazamiento de bits hacia la derecha Desplazamiento de bits hacia la izquierda Operador Ternario

3.1.

Operacin AND (Bit a Bit) (&) o


A 0 0 1 1 B 0 1 0 1 A&B 0 0 0 1

Si recordamos que la operacin AND (Bit a Bit) esta denida por la siguiente tabla de verdad: o

Como trabajamos con variables que ocupan 1 Byte (8 bits) la operacin AND Bit a Bit para estos casos se comportar o a de la siguiente manera: Supongamos que tenemos una variable a = 23 y otra variable b = 90, su representacin en binario de ambas seria: o a = 00010111 y b = 01011010 por lo tanto: 00010111 & 01011010 00010010 El resultado nos da un nmero en binario cuya expresin es 00010010|2 , que en decimal es conocido como 18|10 . u o Por lo tanto llevando este ejemplo al lenguaje C, se realizar de la siguiente manera: a
Ejemplo1: Operacin Lgica AND Bit a Bit o o

1 #i n c l u d e <s t d i o . h> 2 3 i n t main ( ) 4 { 5 char a = 23; 6 char b = 90; 7 char r e s u l t a d o ; 8 9 printf ( a = % d b = % d ,a , b ); 10 11 resultado = a & b ; 12 13 p r i n t f ( Resultado = % d , r e s u l t a d o ) ; 14 15 return 0; 16 }

Una aplicacin util para este operador, es si queremos averiguar si un bit de cierto nmero es 1 o 0, por ejemplo, se o u tiene el nmero a = 75 y se quiere averiguar si el cuarto bit de dicho nmero es 1 o 0. Para eso tenemos que aplicar el u u operador & al nmero a con un nmero cuya representacin binaria sea 00001000, dicho nmero es el 8. u u o u Veamos como queda la operacin primero: o 01001011 & 00001000 00001000 Entonces el cdigo nos quedar de la siguiente forma: o a Autor: Bernardo Ortega Moncada 3

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

Ejemplo2: Operacin Lgica AND Bit a Bit o o

1 #i n c l u d e <s t d i o . h> 2 3 i n t main ( ) 4 { 5 char a = 75; 6 char b = 8 ; 7 char r e s u l t a d o ; 8 9 printf ( a = % d b = % d ,a , b ); 10 11 i f ( a & b) 12 p r i n t f ( e l c u a r t o b i t de l a v a r i a b l e a e s 1 \ n ) ; 13 else 14 p r i n t f ( e l c u a r t o b i t de l a v a r i a b l e a e s 0 \ n ) ; 15 16 return 0; 17 }

3.2.

Operacin OR (Bit a Bit) (|) o


A 0 0 1 1 B 0 1 0 1 A|B 0 1 1 1

Si recordamos que la operacin OR (Bit a Bit) esta denida por la siguiente tabla de verdad: o

Como trabajamos con variables que ocupan 1 Byte (8 bits) la operacin AND Bit a Bit para estos casos se comportar o a de la siguiente manera: Supongamos que tenemos una variable a = 23 y otra variable b = 90, su representacin en binario de ambas seria: o a = 00010111 y b = 01011010 por lo tanto: 00010111 | 01011010 01011111 El resultado nos da un nmero en binario cuya expresin es 01011111|2 , que en decimal es conocido como 95|10 . u o
Ejemplo: Operacin Lgica OR Bit a Bit o o

1 #i n c l u d e <s t d i o . h> 2 3 i n t main ( ) 4 { 5 char a = 23; 6 char b = 90; 7 char r e s u l t a d o ; 8 9 printf ( a = % d b = % d ,a , b ); 10 11 resultado = a | b ; 12 13 p r i n t f ( Resultado = % d , r e s u l t a d o ) ; 14 15 return 0; 16 }

Autor: Bernardo Ortega Moncada

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

3.3.

Operacin XOR (bit a bit) ( ) o


A 0 0 1 1 B 0 1 0 1 AB 0 1 1 0

Si recordamos que la operacin XOR (Bit a Bit) esta denida por la siguiente tabla de verdad: o

Como trabajamos con variables que ocupan 1 Byte (8 bits) la operacin XOR Bit a Bit para estos casos se comportar o a de la siguiente manera: Supongamos que tenemos una variable a = 23 y otra variable b = 90, su representacin en binario de ambas seria: o a = 00010111 y b = 01011010 por lo tanto: 00010111 01011010 01001101 El resultado nos da un nmero en binario cuya expresin es 01001101|2 , que en decimal es conocido como 77|10 . u o
Ejemplo: Operacin Lgica XOR Bit a Bit o o

1 #i n c l u d e <s t d i o . h> 2 3 i n t main ( ) 4 { 5 char a = 23; 6 char b = 90; 7 char r e s u l t a d o ; 8 9 printf ( a = % d b = % d ,a , b ); 10 11 resultado = a b ; 12 13 p r i n t f ( Resultado = % d , r e s u l t a d o ) ; 14 15 return 0; 16 }

3.4.

Operacin Complemento A1 a la Base ( ) o

Supongamos que tenemos una variable a = 23 su representacin binaria seria: o a = 00010111 por lo tanto: 00010111 11101000

El resultado nos da un nmero en binario cuya expresin es 11101000|2 , que en decimal es conocido como 24|10 . u o Hay que tener en cuenta que el MSB se considera como bit de signo
Ejemplo: Complemento A1 a la base

1 #i n c l u d e <s t d i o . h> 2 3 i n t main ( ) 4 { 5 char a = 23; 6 char r e s u l t a d o ; 7 8 printf ( a = % d ,a ); 9

Autor: Bernardo Ortega Moncada

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

10 11 12 13 14 15 }

r e s u l t a d o = a ; p r i n t f ( Resultado = % d , r e s u l t a d o ) ; return 0;

3.5.

Desplazamiento de Bits

Este tema no es facil de entender al principio, pero para que quede claro, uno puede hacer corrimientos de N Bits, con N N, es decir N = 1, 2, . . .. Esta operacin sirve bsicamente para modicar un nmero a nivel Bit. Los corrimientos o a u de Bits, pueden ser en dos direcciones: Corrimiento hacia la derecha Corrimiento hacia la izquierda 3.5.1. Desplazamiento de Bits a la Derecha ( >> )

Supongamos que tenemos un Byte llamado a = 01101001 105|10 , a este nmero se le puede ser corrimientos de bits u de la siguiente manera: Nmero u 01101001 01101001 01101001 01101001 01101001 Cantidad de Bits de Corrimiento 1 2 3 4 5 Resultado 00110100 52|10 00011010 26|10 00001101 13|10 00000110 6|10 00000011 3|10

Por lo tanto la funcin para aplicarlo en el Lenguaje C, es: o

numero >> cantidad bits de corrimiento;


Ejemplo: Desplazamiento a la Derecha de Bits

1 #i n c l u d e <s t d i o . h> 2 3 i n t main ( ) 4 { 5 char a = 105; 6 char r e s u l t a d o ; 7 8 printf ( a = % d ,a ); 9 10 r e s u l t a d o = a >> 1 ; 11 12 p r i n t f ( R e s u l t a d o = % d , r e s u l t a d o ) / imprime 52 / ; 13 14 r e s u l t a d o = a >> 2 ; 15 16 p r i n t f ( R e s u l t a d o = % d , r e s u l t a d o ) / imprime 26 / ; 17 return 0; 18 }

En pocas palabras, los corrimientos de Bits hacia la derecha, consisten en realizar la divisin Entera del nmero al o u cual se le realiza dicho corrimiento, es decir: numero >> cantidad bits de corrimiento; numero % (cantidad bits de corrimiento + 1);

Autor: Bernardo Ortega Moncada

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

3.5.2.

Desplazamiento de Bits a la Izquierda ( < < )

Supongamos que tenemos un Byte llamado a = 01101001 105|10 , a este nmero se le puede ser corrimientos de u bits de la siguiente manera: 01101001 01101001 01101001 01101001 01101001 1 2 3 4 5 11010010 210|10 10100100 420|10 01001000 840|10 10010000 1680|10 00100000 3360|10

Nota: Pueden apreciar que los valores obtenidos en la base decimal no coinciden en lo absoluto con su representacin o binaria, esto se debe al overow que existe al realizar un desplazamiento de bits, lo cual para realizar esto, hay que tener sumo cuidado!! Por lo tanto la funcin para aplicarlo en el Lenguaje C, es: o numero << cantidad bits de corrimiento;
Ejemplo: Desplazamiento a la Izquierda de Bits

1 2 #i n c l u d e <s t d i o . h> 3 4 i n t main ( ) 5 { 6 char a = 105; 7 char r e s u l t a d o ; 8 9 printf ( a = % d ,a ); 10 11 r e s u l t a d o = a << 1 ; 12 13 p r i n t f ( R e s u l t a d o = % d , r e s u l t a d o ) / imprime 210 / ; 14 15 r e s u l t a d o = a << 2 ; 16 17 p r i n t f ( R e s u l t a d o = % d , r e s u l t a d o ) / imprime 420 / ; 18 return 0; 19 }

En pocas palabras, los corrimientos de Bits hacia la izquierda, consisten en realizar la multiplicacin del nmero al o u cual se le realiza dicho corrimiento, es decir: numero << cantidad bits de corrimiento; numero (cantidad bits de corrimiento + 1);

3.6.

Operador Ternario (? :)

Este operador tambin resulta complejo de comprender al principio, pero para entenderlo, tenemos que verlo desde el e punto de vista de un IF/ELSE. Ya que bsicamente es la expresin abstracta de dicha operacin. El operador ternario a o o funciona con tres expresiones. Supongamos que tenemos tres expresiones (que claramente tienen que ser Expresiones que arrojen resultados booleandos -hay que recordar que en C no existen los booleanos-) E1, E2 y E3. Si queremos realizar operaciones dependiendo del estado de E1, uno dir bueno acudamos a nuestro querido IF, entonces decimos que si E1 a, = TRUE, entonces se ejecute la expresin E2, sino que se ejecute la expresin E3. Por lo tanto el cdigo nos quedar de o o o a la siguiente manera:
Utilizando un IF

1 / c o d i g o a n t e r i o r / 2 i f ( E1 ) 3 E2 4 else 5 E3 6 / c o d i g o p o s t e r i o r /

Ahora veamos como queda esto con el Operador Ternario Autor: Bernardo Ortega Moncada 7

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

Utilizando un Operador Ternario

1 / c o d i g o a n t e r i o r / 2 E1 ? E2 : E3 ; 3 / c o d i g o p o s t e r i o r /

Entonces podemos analizar lo siguiente: E1 ? representa bsicamente if(E1) a E2 como vemos que E2 esta despues de ?, entonces seria la expresin que se ejecutar el caso en que E1 = o a TRUE : E3 Como vemos que E3 esta despus de :, entonces tomemos como el signo : la representacin abstracta e o de un ELSE, que luego la expresin posterior ser la que se ejecutar en caso de que E1 = FALSE. o a a 3.6.1. Ejemplo

Veamos un ejemplo del Operador Ternario. Supongamos que queremos hacer un algoritmo que nos diga si un nmero u es Par o Impar, para eso uno har el clsico algoritmo que todo el mundo conoce. El de aplicar divisin entera por 2 y a a o vericar si el resto da cero o no. Ahora como nosotros sabemos manejar el lenguaje a un bajo nivel, vamos a realizar esta operacin a nivel Bit. Es decir, vamos a vericar que el LSB de un nmero sea 1 o 0; Porque realizo esta alocada manera o u de calcular un nmero par o impar? Bsicamente porque si un nmero cuya representacin binaria tiene que su LSB = u a u o 1, entonces el nmero es impar, sino, si su LSB = 0, entonces el nmero es par. u u Por lo tanto el ejemplo nos quedar de la siguiente manera: a
Algoritmo de un Nmero Par a bajo nivel u

1 #i n c l u d e <s t d i o . h> 2 3 i n t main ( ) 4 { 5 char a ; 6 p r i n t f ( i n g r e s e un numero \n ) ; 7 s c a n f ( % d ,&a ) ; 8 ( a & 1 ) ? p r i n t f ( e l numero e s impar \n ) : p r i n t f ( e l numero e s par \n ) ; 9 return 0; 10 }

3.7.

Campos de Bits

El mtodo que se utiliza en C para operar con campos de bits, est basado en las estructuras (structs) que todos e a conocemos del lenguaje. Una estructura compuesta por bits se denomina campo de bits. La forma general de denicin o de un campo de bits es:
Estructura genrica de campos de bits e

1 typedef 2 { 3 4 5 6 7 } campo

struct s bits <t i p o d e d a t o > nombre1 : <c a n t i d a d d e b i t s >; <t i p o d e d a t o > nombre2 : <c a n t i d a d d e b i t s >; ... <t i p o d e d a t o > nombre3 : <c a n t i d a d d e b i t s >; bits ;

Cada campo de bits puede declararse como char, unsigned char, int, unsigned int, etc y su longitud puede variar entre 1 y MAX NUMEROS DE BITS ACORDE A LA ARQUITECTURA DEL MICROPROCESADOR EN QUE SE ESTE TRABAJANDO DE LA VARIABLE. Es decir, supongamos que trabajamos en una arquitectura de 32 bits, sabemos que un char o unsigned char ocupa 1 Byte (8 bits), por lo tanto para el char, el campo de bits puede variar entre 1 bit y 8 Bits. Ahora, sabemos que un int o unsigned int, ocupa (en esta arquitectura) 32 bits, por lo tanto, este campo de bits puede variar entre 1 bit y 32 bits. Veamos el siguiente ejemplo de campos de bits, que consiste en
Ejemplo1: Construccin de Campos de Bits o

1 struct s bits 2 { 3 unsigned i n t mostrar : 1 ; 4 unsigned i n t r o j o : 2 ; 5 unsigned i n t azul : 2 ;

Autor: Bernardo Ortega Moncada

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

6 unsigned i n t verde : 2 ; 7 unsigned i n t transparencia : 1 ; 8 } campo bits ;

Esta estructura dene cinco variables. Dos de ellas (mostrar y transparencia) son de 1 bit, mientras que el resto son de 2 bits. Se puede utilizar esta estructura u otra similar para codicar una imagen RGB de 16 colores, especicando para cada p su color, si es o no transparente y si debe o no mostrarse. Para acceder a cada variable del campo de bits xel se emplea la misma sintaxis que en cualquier otra estructura de C comn y corriente. Pero hay que tener en cuenta que u un bit slo puede tomar dos valores, 0 y 1 (en el caso de los campos de dos bits, ser cuatro los valores posibles: 0, 1, 2 o an y 3). Ya que si no tenemos esto en cuenta, podemos llegar a tener errores en tiempo de compilacin. o
Ejemplo2: Uso de Campos de Bits

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

#i n c l u d e <s t d i o . h> struct s bits { unsigned unsigned unsigned unsigned unsigned } campo bits ;

int int int int int

mostrar : 1 ; rojo : 2; azul : 2; verde : 2 ; transparencia : 1;

i n t main ( ) { c a m p o b i t s unColor ; / c r e o un c o l o r a l e a t o r i o / unColor . r o j o = 2 ; unColor . a z u l = 1 ; unColor . v e r d e = 3 ; unColor . t r a n s p a r e n c i a = 0 ; / s i e l c o l o r no e s t r a n s p a r e n t e , e n t o n c e s a c t i v o e l campo para m o s t r a r l o en a l g u n a o p e r a c i o i f ( unColor . t r a n s p a r e n c i a == 0 ) { unColor . m o s t r a r = 1 ; } / a q u i v i e n e n o t r a s s e n t e n c i a s / return 0; }

3.7.1.

Limitaciones de Campos de Bits

Las variables de campos de bits tienen ciertas restricciones, veamos algunas de ellas: No se puede aplicar el operador & sobre una de estas variables para averiguar su direccin de memoria (es decir, de o algn elemento del struct). u No se pueden construir arrays de un campo de bits (es decir, de algn elemento del struct). u En algunos entornos, los bits se dispondrn de izquierda a derecha y, en otras, de derecha a izquierda, por lo que la a portabilidad puede verse comprometida!!!.

3.8.

Ventajas y Desventajas de Operar Bit a Bit


Mayor velocidad de procesamiento al momento de ejecutar el programa. Mas ahorro de memoria en caso de querer modicar variables. Son ventajosos al momento de querer programar Microcontroladores (o Microprocesadores). Ocupan poco espacio.

Las ventajas que podemos obtener son:

Autor: Bernardo Ortega Moncada

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

Las desventajas que podemos obtener son: El cdigo se puede tornar menos legible y bastante confuso. o Puede quitarle portabilidad al programa lo cual, no es muy agradable. Seguido de la anterior, son dependientes de la arquitectura en donde se este trabajando. Uno es mas libre a cometer errores y lograr facilmente overow en la memoria de cualquier dispositivo en el que se est programando. e

4.

El Preprocesador de C
El preprocesador de C, es el primer programa invocado por el compilador y procesa directivas como: # include # dene # ifndef # if etc.

El preprocesador utiliza 4 etapas denominadas Fases de traduccin. Aunque alguna implementacin puede elegir hacer o o algunas o todas las fases simultaneamente, debe comportarse como si fuesen ejecutadas paso a paso. 1. Tokenizado lxico: El preprocesador reemplaza la secuencia de trigrafos por los caracteres que representan (en e pocas lineas, consisten en reemplazar s mbolos en los cuales no son aplicados en algunos pa ses, como por ejemplo o el identicador de un macro, etc). 2. Empalmado de l neas: Las l neas de cdigo que continan con secuencias de escape de nueva l o u nea son unidas para formar l neas lgicas (ejemplo reconoce cuando puede o no haber un n de linea \n). o 3. Tokenizacin: Reemplaza los comentarios por espacios en blanco. Divide cada uno de los elementos a preprocesar o por un carcter de separacin. a o 4. Expansin de macros y gestin de directivas: Ejecuta las l o o neas con directivas de preprocesado incluyendo las que incluye otros archivos y las de compilacin condicional. Adems expande las macros. o a Pero en denitiva, el preprocesador es un programa separado que es invocado por el compilador antes de que comience la traduccin real. Un preprocesador de este tipo puede eliminar los comentarios, incluir otros archivos y ejecutar o sustituciones de macros. Ahora bien, la pregunta del milln que quizs surja es la siguiente Que es un Macro?. Para eso vamos a dar la o a denicin de Macro. o

4.1.

Que Es Un Macro?

Un Macro, es una herramienta que nos permite evitar al programador la tediosa repeticin de partes idnticas de o e un programa, los ensambladores y compiladores cuentan con macroprocesadores que permiten denir una abreviatura para representar una parte de un programa y utilizar esa abreviatura cuantas veces sea necesario. Para utilizar una macro, primero hay que declararla. En la declaracin se establece el nombre que se le dar a la macro y el conjunto de o a instrucciones que representar. a El programador escribir el nombre de la macro en cada uno de los lugares donde se requiera la aplicacin de las a o instrucciones por ella representadas. La declaracin se realiza una sola vez, pero la utilizacin o invocacin del macro o o o puede hacerse cuantas veces sea necesario. La utilizacin de macros posibilita la reduccin del tamao del cdigo fuente, o o n o aunque el cdigo objeto tiende a ser mayor que cuando se utilizan funciones. o

Autor: Bernardo Ortega Moncada

10

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

4.2.
4.2.1.

Ejemplos de Macros
Inclusin de Archivos o

Este caso es el mas comn y el que se utiliza con mayor frecuencia (por no decir siempre). Uno siempre incluye u archivos cuya extencin es .h ya que son considerados Archivos de Cabecera o Headers Files, el mas famoso o de todos es el archivo stdio.h y el operador que nos permite incluir este tipo de archivos es: #include lo cual para utilizarlo en el Lenguaje C, tenemos que realizar:
Ejemplo: Inclusin de Archivos por el Preprosesador o

1 2 3 4 5 6 7 8

#i n c l u d e <s t d i o . h> / para l o s a r c h i v o s de c a b e c e r a p r e d e f i n i d o s en C/ #i n c l u d e m i a r c h i v o c a b e c e r a . h / para l o s a r c h i v o s de c a b e c e r a c r e a d o s por uno mismo/ i n t main ( ) { p r i n t f ( Hola Mundo \n ) ; return 0; }

4.2.2.

Creacin de Archivos de Cabecera o

Uno puede crear sus propios archivos de cabecera para poder simplicar el cdigo fuente en el archivo main.c, o bsicamente es el caso anlogo cuando uno creaba UNITS en PASCAL Para eso, utilizamos las siguientes sentencias: a a

#ifndef (verica si el macro no fue creado) #ifdef (verica si el macro fue creado) #dene (crea el macro) #include (si es necesario incluir otros archivos de cabecera) #endif (marca el n de un #ifndef o #ifdef) lo cual para utilizarlo en el Lenguaje C, tenemos que crear un archivo .h :
Ejemplo: Creacin de un header le propio o

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

/ s i no e s t a d e f i n i d o e l macro #i f n d e f MIARCHIVOCABECERA H #d e f i n e MIARCHIVOCABECERA H

MIARCHIVOCABECERA H , l o c r e a y l u e g o l l a m a e l a r c h i v o s t d i o . h /

#i n c l u d e <s t d i o . h> / i n c l u y o un a r c h i v o /

/ s i ya e s t a d e f i n i d o e l macro #i f d e f MIARCHIVOCABECERA H #i n c l u d e <s t d i o . h> #i n c l u d e <s t r i n g . h> #i n c l u d e <m a l l o c . h> #e n d i f / f i n d e l #i f d e f /

MIARCHIVOCABECERA H , i n c l u y e mas a r c h i v o s /

/ aca puedo d e f i n i r t i p o s de d a t o s / / aca puedo d e f i n i r l a s f i r m a s de f u n c i o n e s / p r o c e d i m i e n t o s / #e n d i f / f i n d e l #i f n d e f /

4.2.3.

Creacin de Macros como Constantes o

Estos tipos de Macros son conocidos como Macro Objetos. Para poder denir un Macro Objeto en C, se tiene que tener presente como est compuesto. Para eso, veamos la estructura genrica de un Macro Objeto: a e

Autor: Bernardo Ortega Moncada

11

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

Estructura de un Macro Objeto

1 #d e f i n e <nombre d e l macro>

< l i s t a de t o k e n s a r e e m p l a z a r en e l c o d i g o >

Veamos un ejemplo de como se construye un Macro Objeto:


Ejemplo: Construccin de un Macro Objeto o

1 2 3 4 5 6 7 8 9 10 11

#i n c l u d e <s t d i o . h> #d e f i n e PI 3 . 1 4 1 5 9 #d e f i n e SALUDO Hola Mundo i n t main ( ) { p r i n t f ( % f , PI ) ; / Esto imprime 3 . 1 4 1 5 9 por p a n t a l l a / p r i n t f (SALUDO) ; / Esto imprime Hola Mundo por p a n t a l l a / return 0; }

Podemos apreciar que: <nombre del macro> es PI <lista de tokens a reemplazar en el cdigo> es 3.14159 o En denitiva creamos un Macro Objeto llamado PI, que en cualquier parte del cdigo donde aparezca PI, ser reo a emplazado por 3.14159. Esta ventaja de crear este tipo de Constantes, es que no ocupan lugar en memoria, sino que son reemplazados en tiempo de compilacin, haciendo que el programa sea un poco mas liviano!! o 4.2.4. Creacin de Macros como Funcin o o

Estos tipos de Macros son conocidos como Macro Funciones (valga la redundancia). Para poder denir una Macro Funcin en C, se tiene que tener presente como est compuesto. La estructura genrica de una Macro Funcin no diere o a e o casi en nada a la de un Macro Objeto, slo tiene una diferencia y es que toda sentencia dentro de una Macro Funcin o o tiene que ir entre parntesis ( ) aunque una gran ventaja es que pueden hacerse macros multilinea, es decir, que pueden e hacer macros con mas de una linea de cdigo, siempre y cuando antepongan el s o mbolo \ al nal de cada linea. Veamos un ejemplo de como crear una Macro Funcin, como por ejemplo, una Macro Funcin que calcule si un nmero es mas o o u grande que otro.
Ejemplo: Construccin de una Macro Funcin o o

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

#i n c l u d e <s t d i o . h> #d e f i n e MAX( a , b ) ( ( a > b ) ? a : b ) i n t main ( ) { i n t num1 ; i n t num2 ; printf ( scanf ( printf ( scanf ( i n g r e s e un numero \n ) ; % d ,&num1 ) ; i n g r e s e o t r o numero \n ) ; % d ,&num2 ) ;

p r i n t f ( El maximo e n t r e % d y % d e s : % d \n , num1 , num2 ,MAX( num1 , num2 ) ) ; return 0; }

El unico problema que presenta realizar una Macro Funcin de esta manera, es que evala 2 veces a cualquiera de las o u 2 variables (en este caso a o b), dependiendo la condicin que cumpla, lo cual no es del todo prolijo y hay que tener sumo o cuidado cuando uno quiere hacer una Macro Funcin que modique una variable, ya que podr modicarla dos veces y o a no ser un resultado del todo agradable. Si se esta trabajando con ANSI C, este problema es inevitable, lo cual no hay a manera de salvar este problema. Por lo tanto si se quiere realizar Macro Funciones en ANSI C, hay que tener mucho cuidado y pensarlo dos veces si esa funcin conviene o no hacerla en Macro. o Si se trabaja en otra estandarizacin de C, como por ejemplo C99, uno puede salvar este problema con una sentencia o llamada tipeof(). Autor: Bernardo Ortega Moncada 12

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

Dicha sentencia lo que hace es averiguar el tipo de dato que compone la variable que se le introduzca, y as evita que haya una sobrevaluacin de dicha variable (igual a esto hay que tomarlo con pinzas, ya que le estar o amos quitando portabilidad al programa). Entonces el ejemplo anterior quedar como: a
Solucin al problema o

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

#i n c l u d e <s t d i o . h> #d e f i n e MAX( a , b ) \ ({ typeof ( a ) a = ( a ) ; \ typeof (b) b = (b ) ; \ a > b ? a : b ; }) i n t main ( ) { i n t num1 ; i n t num2 ; printf ( scanf ( printf ( scanf ( i n g r e s e un numero \n ) ; % d ,&num1 ) ; i n g r e s e o t r o numero \n ) ; % d ,&num2 ) ;

p r i n t f ( El maximo e n t r e % d y % d e s : % d \n , num1 , num2 ,MAX( num1 , num2 ) ) ; return 0; }

Veamos tambin como podemos realizar la funcin que calcula si un nmero es Par o No a bajo nivel por medio de e o u Macro Funciones:
Macro Funcin Bajo Nivel para averiguar si un nmero es Par o No o u

1 2 3 4 5 6 7 8 9 10 11 12 13 14

#i n c l u d e <s t d i o . h> #d e f i n e PAR( a ) \ ( ( a & 1 ) ? p r i n t f ( El numero e s Impar ) : p r i n t f ( El numero e s Par ) ) i n t main ( ) { i n t num1 ; p r i n t f ( i n g r e s e un numero \n ) ; s c a n f ( % d ,&num1 ) ; PAR(num1 ) ; return 0; }

Otra herramienta util de una macro funcin es lo que se denomina redenicin de una funcin, es decir, que puedo o o o renombrar a una funcin y que haga exactamente lo mismo o con algunos ligeros cambios. o Supongamos que queremos redenir la funcin printf de C y queremos que se llame IMPRIMIR y que siempre que o se la cite imprima por pantalla Mensaje: <texto que se desea imprimir>. Entonces el cdigo quedar de la siguiente o a forma:
Redenicin de una Funcin o o

1 2 3 4 5 6 7 8 9

#i n c l u d e <s t d i o . h> #d e f i n e IMPRIMIR( s ) \ p r i n t f ( Mensaje : \ %s \\n , s ) ; i n t main ( ) { IMPRIMIR( Hola Mundo ) ; / imprime por p a n t a l l a Mensaje : Hola Mundo/ return 0; }

Autor: Bernardo Ortega Moncada

13

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

4.3.
4.3.1.

Otras Directivas del Preprocesador


#undef

La directiva #undef se usa para quitar una denicin de nombre de macro que se haya denido previamente. Es decir o que el objetivo principal de esta directiva es permitir localizar los nombres de macros slo en las secciones de cdigo que o o se necesiten.
uso de #undef

1 2 3 4 5 6 7 8 9 10 11 12 13 14

#i n c l u d e <s t d i o . h> #d e f i n e IMPRIMIR( s ) \ p r i n t f ( Mensaje : \ %s \\n , s ) ; i n t main ( ) { IMPRIMIR( Hola Mundo ) ; / imprime por p a n t a l l a Mensaje : Hola Mundo/ #u n d e f IMPRIMIR

/ s i queremos ahora u t i l i z a r IMPRIMIR, d i c h a macro f u n c i o n f u e e l i m i n a d a con l o c u a l , no

IMPRIMIR( f u n c i o n a ? ) ; / v e r i f i q u e n que e s t a l i n e a va a p r o d u c i r un e r r o r a l momento de c o return 0; }

4.3.2.

#if , #else , #elif

La directiva #if evala una expresin constante entera. Siempre se debe terminar con #endif para marcar el n de u o esta sentencia. Se pueden as mismo evaluar otro cdigo en caso se cumpla otra condicin, o bien, cuando no se cumple ninguna o o usando #else o #elif (se puede apreciar que es una combinacin de #else e #if respectivamente). o
uso de condicionales en macros

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

#i n c l u d e <s t d i o . h> #d e f i n e LUNES 0 #d e f i n e MARTES 1

#d e f i n e DIA SEMANA LUNES i n t main ( ) { #i f DIA SEMANA == LUNES printf ( lunes ) ; # e l i f DIA SEMANA == MARTES p r i n t f ( martes ) ; #e l s e p r i n t f ( s e r a f i n de semana ? ) ; #e n d i f return 0; }

4.3.3.

#error

La estructura bsica de #error es: a #error <mensaje de error> Esta directiva forza al compilador a parar la compilacin cuando la encuentra, mostrando el mensaje de error o (<mensaje de error> ) que se escribi. Se usa principalmente para depuracin de errores. o o
uso de #error

1 #i n c l u d e <s t d i o . h> 2 3 #d e f i n e DIVIDIR ( a , b ) ( a /b ) 4 #d e f i n e NUM1 5

Autor: Bernardo Ortega Moncada

14

Programando en C a Bajo Nivel

1er Cuatrimestre 2011

5 #d e f i n e NUM2 0 6 7 i n t main ( ) 8 { 9 #i f NUM2 != 0 10 p r i n t f ( % f , DIVIDIR (NUM1,NUM2) ) ; 11 #e l s e 12 #e r r o r d i v i s i o n por c e r o ! 13 #e n d i f 14 return 0; 15 }

Por ejemplo si usamos codeblocks para compilar dicho programa, nos mostrar un mensaje de error al compilar del a estilo:

Figura 1: Mensaje de Error Utilizando #error

4.4.

Ventajas y Desventajas de Usar Macros

La ventaja de usar macros son: Al utilizar Macro Funciones se acelera la ejecucin del programa o Al utilizar tanto Macro Funciones como Macro Objetos, las variables locales o constantes no ocupan espacio en memoria!!! Pero como dice el dicho todo lo bueno siempre tiene algo malo los Macros poseen tres desventajas de gran consideracin. Dichas desventajas son: o El tamao del cdigo aumenta. Ya que se expande ms el cdigo y no hay manera de encapsularlo n o a o El uso de macros queda fuera del normal planteamiento estructurado de creacin de programas. Es decir en tiempo o de diseo no se suelen plantear macros, lo cual puede generar confusiones al momento de implementarlos y llevar n una perdida de tiempo debatiendo si es conveniente o no construirlos Los lenguajes modernos (como C#, Java, etc) no utilizan macros, lo cual si tenemos que exportar el cdigo hecho o a alguno de esto lenguajes, vamos a tener que reconstruirlo y eso llevar una prdida de tiempo. a e

Autor: Bernardo Ortega Moncada

15

You might also like