Ir al contenido principal

C: Macros...

El lenguaje C nos permite definir un tipo especial de "funciones": las macros. Coloco esto entre comillas ya que no pueden considerarse funciones reales.

Una macro tiene parámetros y se las llama igual que a las funciones, pero ese llamado no es traducido por el compilador como un verdadero llamado a una función.

Para terminar esta introducción, hay que decir que las macros resultan más rápidas que las funciones en tiempo de ejecución ya que nos ahorramos todos los detalles que implica llamar a una, constituyendo esto una ventaja pero también tienen sus desventajas como el hecho de no permitir la declaración de variables locales, no admitir recursión y, como veremos, suelen ser peligrosas ya  que debemos extremar cuidados al momento de usarlas.

Definiendo una macro:

Una macro debe definirse antes de la función main de la misma forma que definíamos constantes, usando #define aunque a diferencia de las mismas, luego del nombre de la macro colocaremos entre paréntesis sus parámetros (sin especificar el tipo de dato) y luego de ello definiremos en una sola línea el cuerpo de la misma.

Por lo que la estructura de una macro sería algo como esto:


Veamos un ejemplo de una macro que calcule el cubo de un número:


En caso que necesitáramos que una macro ocupe más de una línea, debemos finalizar todas las líneas en el caracter \ excluyendo la última.

Llamando a una macro:

Como dijimos al comienzo, el llamado a una macro se hace de la misma manera que el llamado a una función. Si debemos devolver un resultado la asignamos a alguna variable o la usamos en alguna estructura.

Veamos cómo llamaríamos dentro de un printf la macro que definimos anteriormente:


Donde x es un entero que declaramos anteriormente. Si x fuera 5 , CUBO(x) nos devolvería el valor 125 que es el que buscábamos pero no todo lo que brilla es oro como veremos más adelante.

¿Qué hace el programa al hacer el llamado a una macro?

Cuando hacemos el llamado a una macro, lo que se hace realmente es reemplazar la llamada por el cuerpo de la macro, simplemente eso.

Osea que en el ejemplo anterior se hace lo siguiente:


Por esta razón, las macros se pueden convertir en armas de doble filo, ya que podríamos obtener resultados muy diferentes a los esperados.

A tener en cuenta:


¿Por qué podemos obtener resultados diferentes a los esperados? Respondamos esta pregunta con el ejemplo anterior, sólo que esta vez donde dice x colocaremos 1+1 que para nosotros es 2, como lo que tendríamos:


Normalmente pensaríamos que esto debe devolver el cubo de 2 que es 8, pero no es así ¿por qué?... por lo que explicamos antes acerca de lo que pasa al llamar una macro:


Por lo que para ahorrarnos estos problemas deberíamos colocar paréntesis en la definición de la macro:


Y siempre tendríamos que tener en cuenta lo que hace la macro para saber si realmente obtendremos el resultado esperado, por lo que son una herramienta algo "peligrosa" al momento de usarlas. Pero si tenemos claros los conceptos podríamos hacer un buen uso de ellas.



Para terminar, queda en manos de cada uno hacer uso de las herramientas que le parezcan adecuadas para cumplir sus objetivos a la hora de programar, pero desde Programacion.NERD no aconsejamos utilizar macros existiendo otras herramientas mejores como las funciones inline que veremos en el próximo post.

Comentarios

Publicar un comentario

Entradas populares de este blog

C: Conversiones de tipo (casting) en C...

El casting o simplemente cast  nos permite hacer una conversión explícita de un tipo de dato a otro, a criterio del programador siempre y cuando estos tipos sean compatibles. Este cast se realiza a través de un operador de conversión de tipos (type casting operator) y es un recurso a tener en cuenta ya que hay situaciones en que nos puede resultar de gran utilidad. Hacer uso de un cast es tan sencillo como poner (tipo de dato)  delante de la expresión o variable a convertir. Veamos un ejemplo: Declaramos una variable de tipo int con un identificador tan creativo como "a" y le realizamos diferentes cast a a para mostrarlo como si fuera un float, un double y un char en un printf. Lo que obtendríamos en pantalla sería lo siguiente: Donde tenemos el valor de nuestro a, a convertido en float y double (mostrándolo con 3 cifras decimales) y a convertido en char. Si vemos este último caso, al hacer la conversión de "a" a char toma a como el código ascii de

C: Ejemplos: Congruencia de Zeller (nivel básico) ...

La Congruencia de Zeller es un algoritmo que se atribuye al matemático alemán Julius Christian Johannes Zeller que vivió en el siglo XIX. Este algoritmo nos permite determinar el día de la semana que le corresponde a una fecha determinada del calendario Gregoriano. La fórmula que nosotros usaremos (con algunas modificaciones respecto de la original para poder usarla en  informática) es la siguiente: Donde h es el día de la semana (entre 0 y 6), J es año/100 (la centuria) y K es año mod 100 (el año de la centuria). Y hay que tener en cuenta que los meses de enero y febrero cuentan como el mes 13 y 14 del año anterior. Ahora que tenemos la fórmula, programemos el algoritmo en C mediante el uso de una función: Analicemos el código paso a paso: Tenemos en cuenta el caso de enero y febrero: Dijimos que estos meses corresponden a los meses 13 y 14 del año anterior por lo que los asignamos como corresponde (mes + 12 , que dará 13 para enero y 14 para febrero) y le rest

Algoritmos: Resolución de problemas y refinamientos en pseudocódigo...

En otras entradas, vimos las partes que debe tener nuestro algoritmo en pseudocódigo y las estructuras que utilizaremos para resolverlo. Ahora llega el turno de implementar todo en conjunto para dar origen a nuestra creación. Pero ¿cómo resolvemos un problema así? Para hacerlo, utilizaremos lo que llamamos refinamientos sucesivos. Este concepto consiste en dividir el problema en subproblemas más pequeños y a estos, a su vez, en otros más pequeños; y así sucesivamente hasta que la solución de los últimos sea trivial, sencillo de resolver. Luego usaremos todas las soluciones obtenidas para armar la solución de nuestro problema mayor. Este principio, tiene base en parte de la técnica divide and conquer (dependiendo de la traducción: "divide y vencerás") que es una de las muchas técnicas de resolución de algoritmos existentes. Como vemos, al dividir el problema en otros más pequeños y más fáciles de resolver, podemos pasar de un problema complicado a uno cuya solución es much