¿Qué es una macro? ¿Diferencia entre macro y función?

16

No entiendo bien el concepto macro. ¿Qué es una macro? No entiendo cómo es diferente de la función? Tanto la función como la macro contienen un bloque de código. Entonces, ¿cómo difieren macro y función?

Edoardo Sorgentone
fuente
15
¿Te refieres a algún lenguaje de programación en particular?
mkrieger1
15
Tienes que ser más específico; macro se refiere a tres conceptos muy diferentes, aproximadamente macros de texto / preprocesador de estilo C, macros Lisp y macros de aplicación.
chrylis -on strike-

Respuestas:

7

Nota

Me gustaría agregar la siguiente aclaración después de observar el patrón de votación polarizante en esta respuesta.

La respuesta no fue escrita teniendo en cuenta la precisión técnica y la generalización general. Fue un intento humilde de explicar en un lenguaje simple, la diferencia entre macros y funciones para un novato en programación, sin tratar de ser completo o preciso (de hecho, está lejos de ser preciso). El lenguaje de programación C estaba en mi mente cuando redactaba la respuesta, pero pido disculpas por no mencionarlo claramente y causar confusión (potencial).

Realmente considero la respuesta compartida por Jörg W Mittag . Fue perspicaz leerlo (qué menos sé) y lo voté poco después de que fuera publicado. Acabo de comenzar con el intercambio de pila de ingeniería de software, y la experiencia y las discusiones hasta ahora han sido realmente perspicaces.

Dejaré esta respuesta aquí, ya que puede ser útil para otros novatos en desarrollo de software, tratando de entender un concepto sin atascarse en precisiones técnicas.


Tanto la macro como la función representan una unidad de código autónoma. Ambos son herramientas que ayudan en el diseño modular de un programa. Desde el punto de vista del programador que está escribiendo el código fuente, parecen bastante similares. Sin embargo, difieren en cómo se manejan durante el ciclo de vida de ejecución del programa.

Una macro se define una vez y se usa en muchos lugares de un programa. Macro se expande en línea durante la etapa de preprocesamiento. Por lo tanto, técnicamente no permanece como una entidad separada una vez que se compila el código fuente. Las declaraciones en la definición de macro se convierten en parte de las instrucciones del programa, al igual que otras declaraciones.

El motivo detrás de escribir una macro es hacer que la escritura y la administración del código fuente sean más fáciles para el programador. Las macros generalmente se desean para tareas más simples en las que escribir una función completa sería una penalización por sobrecarga / tiempo de ejecución. Ejemplos de situaciones en las que una macro es preferible a la función son:

  • Usar valores constantes (como valores matemáticos o científicos) o algún parámetro específico del programa.

  • Impresión de mensajes de registro o manejo de aserciones.

  • Realización de cálculos simples o verificaciones de condición.

Cuando se usa macro, es fácil hacer cambios / correcciones en un lugar que están disponibles instantáneamente en todas partes donde se usa la macro en el programa. Se requiere una simple compilación del programa para que los cambios surtan efecto.

El código de función, por otro lado, se compila como una unidad separada dentro del programa y se carga en la memoria durante la ejecución del programa solo si es necesario. El código de función retiene su identidad independiente del resto del programa. El código cargado se reutiliza si la función se llama más de una vez. Cuando se encuentra la llamada de función en el programa en ejecución, el subsistema de tiempo de ejecución pasa el control y se conserva el contexto del programa en ejecución (dirección de instrucción de retorno).

Sin embargo, hay una pequeña penalización de rendimiento que se debe encontrar al llamar a una función (cambio de contexto, preservar la dirección de retorno de las instrucciones principales del programa, pasar parámetros y manejar valores de retorno, etc.). Por lo tanto, el uso de la función solo se desea para bloques de código complejos (contra macros que manejan casos más simples).

Con experiencia, un programador toma una decisión juiciosa sobre si un código encajará bien como macro o como función en la arquitectura general del programa.

Nimesh Neema
fuente
9
¿Qué pasa con la función en línea?
Nathan Cooper
1
En el lenguaje C & co, una macro también puede ser una llamada a la función. Entonces hacer una distinción entre los dos no tiene mucho sentido.
Sombrero Chicken
55
Y las macros de estilo C son todo menos autónomas: no introducen un alcance léxico y tienen acceso ilimitado a los nombres locales en el alcance en el punto de sustitución.
Inútil
@Sombrero Chicken: ¿Quizás podrías mostrar un ejemplo de eso? Puede tener macros que CONTIENEN llamadas a funciones, pero (AFAIK) la macro no es una llamada a funciones.
jamesqf
3
Hay muchas cosas sobre esta respuesta que son engañosas. Los ejemplos que menciona no son en absoluto situaciones en las que las macros sean "preferibles", sino todo lo contrario. Las macros no deberían usarse para esas cosas a menos que haya una razón extremadamente buena para hacerlo. El rendimiento generalmente no es una buena razón para usar macros en absoluto. Por las razones mencionadas en otros comentarios, justificar el uso de macros de esta manera puede resultar en un código propenso a errores con todo tipo de consecuencias involuntarias, particularmente en una base de código más grande.
Ben Cottrell
65

Desafortunadamente, hay múltiples usos diferentes del término "macro" en la programación.

En la familia de idiomas Lisp, e idiomas inspirados por ellos, así como en muchos lenguajes modernos funcionales o inspirados en funciones como Scala y Haskell, así como en algunos lenguajes imperativos como Boo, una macro es un código que se ejecuta en tiempo de compilación (o al menos antes del tiempo de ejecución para implementaciones sin un compilador) y puede transformar el Árbol de sintaxis abstracta (o lo que sea el equivalente en el lenguaje particular, por ejemplo, en Lisp, serían las S-Expresiones) en algo más durante la compilación. Por ejemplo, en muchas implementaciones de Scheme, fores una macro que se expande en múltiples llamadas al cuerpo. En lenguajes de tipo estático, las macros a menudo son de tipo seguro, es decir, no pueden producir código que no esté bien escrito.

En la familia de lenguajes C, las macros se parecen más a la sustitución textual. Lo que también significa que pueden producir código que no está bien escrito, o incluso que no es sintácticamente legal.

En los macroensambladores, las "macros" se refieren a "instrucciones virtuales", es decir, instrucciones que la CPU no admite de forma nativa pero que son útiles, por lo que el ensamblador le permite usar esas instrucciones y las ampliará en múltiples instrucciones que la CPU entiende .

En las secuencias de comandos de aplicaciones, una "macro" se refiere a una serie de acciones que el usuario puede "grabar" y "reproducir".

Todos estos son, en cierto sentido, tipos de código ejecutable, lo que significa que, en cierto sentido, pueden verse como funciones. Sin embargo, en el caso de las macros Lisp, por ejemplo, su entrada y salida son fragmentos de programa. En el caso de C, su entrada y salida son tokens. Los tres primeros también tienen la distinción muy importante de que se ejecutan en tiempo de compilación . De hecho, las macros del preprocesador C, como su nombre lo indica, se ejecutan antes de que el código llegue al compilador .

Jörg W Mittag
fuente
1
"En lenguajes de tipo estático, las macros suelen ser de tipo seguro, es decir, no pueden producir código que no esté bien escrito", ¿de verdad? ¿A qué idiomas te refieres? AFAICS, esto en general solo es posible garantizarlo en un lenguaje de tipo dependiente. En Haskell, las macros TH solo son sintácticamente seguras, pero el verificador de tipo se ejecuta después. (Entonces, en cuanto al tipo, ofrecen incluso menos garantías que las plantillas C ++).
Leftaroundabout
1
Aparte de las secuencias de comandos de la aplicación, no creo que los tres ejemplos que da sean tan diferentes. Todos realizan una sustitución de algún tipo en el programa, en lugar de saltar a un código compartido cuando se ejecuta.
IMSoP
Tal vez pueda extender la mención de las macros de C con el concepto general de lenguajes macro como M4, ya que para C es básicamente que la especificación define un lenguaje macro auxiliar CPP y estandariza su uso con C.
JoL
1
El tema general parece ser que las macros generan código para ser interpretado como parte del código fuente de un programa más grande, en lugar de ejecutarse cuando se ejecuta el programa.
jpmc26
1
@ jpmc26 Yo diría que el tema general es la sustitución donde haces algo pequeño y se expande a algo grande. El lenguaje macro M4 no está especialmente diseñado para el código. Puedes usarlo para generar cualquier texto. También hay macros de teclado, como esta respuesta mencionada. Presiona 1 o 2 teclas y se expanden a una mayor cantidad de pulsaciones de teclas. las macros de vim son así también. Guarde una secuencia grande de comandos de modo normal en una sola tecla y llame a esa secuencia ejecutando el comando de modo normal que la ejecuta.
JoL
2

En la familia de lenguaje C, una definición de macro , un comando de preprocesador, especifica una plantilla de código parametrizada que se sustituye en la llamada de macro sin compilarse en la definición. Esto significa que todas las variables libres deben estar vinculadas en el contexto de la llamada macro. Los argumentos de parámetro con un efecto secundario como i++podrían repetirse, mediante el uso plural del parámetro. La sustitución textual del argumento 1 + 2de algún parámetro xocurre antes de la compilación y puede causar un comportamiento inesperado x * 3( 7io 9). Un error en el cuerpo de macro de la definición de macro se mostrará solo al compilar en la llamada de macro.

La definición de la función especifica código con variables libres vinculadas al contexto del cuerpo de la función; No es la llamada a la función .

Sin embargo, la macro aparentemente negativa proporciona acceso a la llamada, el número de línea y el archivo fuente, el argumento como cadena .

Joop Eggen
fuente
2

En términos un poco más abstractos, una macro es sintaxis como una función es a datos. Una función (en abstracto) encapsula alguna transformación en los datos. Toma sus argumentos como datos evaluados, realiza algunas operaciones sobre ellos y devuelve un resultado que también son solo datos.

Una macro en contraste toma una sintaxis no evaluada y opera sobre eso. Para lenguajes tipo C, la sintaxis se presenta a nivel de token. Para lenguajes con macros similares a LISP, obtienen la sintaxis representada como AST. La macro debe devolver un nuevo fragmento de sintaxis.

Ashton Wiersdorf
fuente
0

Una macro generalmente se refiere a algo que se expande en su lugar , reemplazando la macro "llamada" durante la compilación o preprocesamiento con instrucciones individuales en el idioma de destino. En tiempo de ejecución, generalmente no habrá indicación de dónde comienza y dónde termina la macro.

Esto es distinto de una subrutina , que es un fragmento de código reutilizable que se encuentra por separado en la memoria, al que se pasa el control en tiempo de ejecución . Las "funciones", "procedimientos" y "métodos" en la mayoría de los lenguajes de programación entran en esta categoría.

Como se comenta en la respuesta de Jörg W Mittag , los detalles exactos varían entre los lenguajes: en algunos, como C, una macro realiza la sustitución de texto en el código fuente; en algunos, como Lisp, realiza la manipulación de una forma intermedia como un árbol de sintaxis abstracta. También hay algunas áreas grises: algunos idiomas tienen notación para "funciones en línea", que se definen como una función, pero se expanden en el programa compilado como una macro.

La mayoría de los lenguajes alientan a los programadores a razonar sobre cada subrutina de forma aislada, definiendo contratos de tipo para entradas y salidas y ocultando otra información sobre el código de llamada. A menudo hay un impacto en el rendimiento de llamar a una subrutina en la que las macros no incurren, y las macros pueden manipular el programa de una manera que una subrutina no puede. Por lo tanto, las macros pueden considerarse de "nivel inferior" que las subrutinas, porque funcionan de manera menos abstracta.

IMSoP
fuente
0

La macro se ejecuta durante la compilación y la función se ejecuta en tiempo de ejecución.

Ejemplo:

#include <stdio.h>

#define macro_sum(x,y) (x+y)

int func_sum(x,y) {
    return x+y;
}

int main(void) {
    printf("%d\n", macro_sum(2,3));
    printf("%d\n", func_sum(2,3));
    return 0;
}

Entonces, durante la compilación, el código se cambia a:

#include <stdio.h>

int func_sum(x,y) {
    return x+y;
}

int main(void) {
    printf("%d\n", (2+3));
    printf("%d\n", func_sum(2,3));
    return 0;
}
david72
fuente