O que é uma macro? Diferença entre macro e função?


16

Eu não entendo bem o conceito macro. O que é uma macro? Eu não entendo como é diferente da função? Tanto a função quanto a macro contêm um bloco de código. Então, como a macro e a função diferem?


15
Você está se referindo a alguma linguagem de programação específica?
mkrieger1

15
Você precisa ser mais específico; macro refere-se a três conceitos muito diferentes, macros de texto / pré-processador no estilo C, macros Lisp e macros de aplicativo.
chrylis -on strike-

Respostas:


7

Nota

Gostaria de acrescentar o seguinte esclarecimento após observar o padrão de votação de polarização nesta resposta.

A resposta não foi escrita tendo em mente a precisão técnica e a ampla generalização. Foi uma tentativa humilde de explicar em linguagem simples, a diferença entre macros e funções para um iniciante em programação, sem tentar ser completo ou preciso (na verdade, está longe de ser preciso). A linguagem de programação C estava em minha mente ao redigir a resposta, mas minhas desculpas por não mencioná-la claramente e causar qualquer confusão (potencial).

Realmente considero a resposta compartilhada por Jörg W Mittag . Foi uma leitura perspicaz (quanto menos eu sei) e votei novamente logo após a publicação. Acabei de começar no Software Engineering Stack Exchange, e a experiência e as discussões até agora foram realmente esclarecedoras.

Deixarei essa resposta aqui, pois ela pode ser útil para outros iniciantes em desenvolvimento de software, tentando entender um conceito sem ficar atolado em precisões técnicas.


Tanto a macro quanto a função representam a unidade de código independente. Ambos são ferramentas que auxiliam no design modular de um programa. Do ponto de vista do programador que está escrevendo o código fonte, eles parecem bastante semelhantes. No entanto, eles diferem na maneira como são tratados durante o ciclo de vida de execução do programa.

Uma macro é definida uma vez e usada em vários locais de um programa. A macro é expandida em linha durante o estágio de pré-processamento. Portanto, tecnicamente, não permanece uma entidade separada depois que o código-fonte é compilado. As instruções na definição de macro tornam-se parte das instruções do programa, assim como outras instruções.

O motivo por trás da gravação de uma macro é facilitar a escrita e o gerenciamento do código-fonte para o programador. Geralmente, as macros são desejadas para tarefas mais simples, nas quais escrever uma função completa seria uma penalidade de sobrecarga de desempenho / tempo de execução. Exemplos de situações em que uma macro é preferível à função são:

  • Usando valores constantes (como valores matemáticos ou científicos) ou algum parâmetro específico do programa.

  • Imprimir mensagens de log ou manipular asserções.

  • Executando cálculos simples ou verificações de condição.

Ao usar a macro, é fácil fazer alterações / correções em um local que estão instantaneamente disponíveis em qualquer lugar em que a macro é usada no programa. É necessária uma recompilação simples do programa para que as alterações tenham efeito.

O código de função, por outro lado, é compilado como uma unidade separada dentro do programa e é carregado na memória durante a execução do programa somente se necessário. O código de função mantém sua identidade independente do restante do programa. O código carregado é reutilizado se a função for chamada mais de uma vez. Quando a chamada de função é encontrada no programa em execução, o controle é passado a ele pelo subsistema de tempo de execução e o contexto do programa em execução (endereço da instrução de retorno) é preservado.

No entanto, há uma pequena penalidade no desempenho que precisa ser encontrada ao chamar uma função (alternância de contexto, preservação do endereço de retorno das principais instruções do programa, passagem de parâmetros e manipulação de valores de retorno etc.). Portanto, o uso da função é desejado apenas para blocos complexos de código (contra macros que manipulam casos mais simples).

Com a experiência, um programador toma uma decisão criteriosa como se um pedaço de código seria um bom ajuste como macro ou função na arquitetura geral do programa.


9
E a função embutida?
Nathan Cooper

1
Na linguagem C & co, uma macro também pode ser uma chamada de função. Portanto, fazer uma distinção entre os dois não faz muito sentido.
Sombrero Chicken

5
E as macros no estilo C são tudo menos independentes - elas não introduzem um escopo lexical e têm acesso ilimitado a nomes locais no escopo no momento da substituição.
Useless

@ Chicken Sombrero: Talvez você possa mostrar um exemplo disso? Você pode ter macros chamadas de função CONTAIN, mas (AFAIK) a macro não é uma chamada de função.
Jamesqf

3
Há muitas coisas nessa resposta que são enganosas. Os exemplos que você menciona não são absolutamente de forma alguma as situações em que as macros são "preferíveis" - muito pelo contrário. As macros não devem ser usadas para essas coisas, a menos que haja uma razão extremamente boa para isso. O desempenho geralmente não é uma boa justificativa para o uso de macros. Por razões mencionadas em outros comentários, justificar o uso de macros dessa maneira provavelmente resultará em código propenso a erros com todo tipo de conseqüências não intencionais, particularmente em uma base de código maior.
Ben Cottrell

65

Infelizmente, existem vários usos diferentes do termo "macro" na programação.

Na família de idiomas Lisp e idiomas inspirados por eles, assim como em muitas linguagens funcionais ou inspiradas em funções modernas como Scala e Haskell, além de algumas linguagens imperativas como Boo, uma macro é um pedaço de código que é executado em tempo de compilação (ou pelo menos antes do tempo de execução para implementações sem um compilador) e pode transformar a Árvore de Sintaxe Abstrata (ou qualquer que seja o equivalente na linguagem específica, por exemplo, no Lisp, seriam as Expressões-S) em outra coisa durante a compilação. Por exemplo, em muitas implementações de esquema, foré uma macro que se expande em várias chamadas para o corpo. Nas linguagens de tipo estático, as macros geralmente são seguras, ou seja, não podem produzir código que não seja bem-digitado.

Na família de idiomas C, as macros são mais como substituição de texto. O que também significa que eles podem produzir código que não é bem digitado ou que não é sintaticamente legal.

Nos montadores de macros, "macros" se referem a "instruções virtuais", isto é, instruções que a CPU não suporta nativamente, mas que são úteis; portanto, o montador permite que você use essas instruções e as expande em várias instruções que a CPU entende. .

No script de aplicativo, uma "macro" refere-se a uma série de ações que o usuário pode "gravar" e "reproduzir".

Todos esses são, em certo sentido, tipos de código executável, o que significa que, em certo sentido, podem ser vistos como funções. No entanto, no caso das macros Lisp, por exemplo, suas entradas e saídas são fragmentos de programa. No caso de C, suas entradas e saídas são tokens. Os três primeiros também têm a distinção muito importante de que eles são executados em tempo de compilação . De fato, as macros do pré-processador C, como o nome indica, são realmente executadas antes que o código chegue ao compilador .


1
“Em linguagens de tipo estático, as macros geralmente são seguras, ou seja, não podem produzir código que não é bem-digitado” - sério? Para quais idiomas você está se referindo? AFAICS, isso geralmente só é possível garantir em um idioma de tipo dependente. No Haskell, as macros TH são apenas sintaticamente seguras, mas o verificador de tipos é executado posteriormente. (Portanto, em termos de tipo, eles oferecem ainda menos garantias do que os modelos C ++).
leftaroundabout

1
Além de scripts de aplicativos, não acho que os três exemplos que você fornece sejam tão diferentes. Todos realizam uma substituição de algum tipo no programa, em vez de pular para um pedaço de código compartilhado quando executado.
IMSoP 24/06/19

Talvez você pode estender a menção de macros de C com o conceito geral de linguagens de macro como M4, uma vez que para C é basicamente que a especificação define um CPP auxiliar linguagem de macro e padroniza sua utilização com C.
JOL

1
O tema geral parece ser que as macros geram código a ser interpretado como parte do código-fonte de um programa maior, em vez de serem executados quando o programa é executado.
jpmc26

1
@ jpmc26 Eu diria que o tema geral é a substituição, onde você faz uma coisa pequena e se expande para uma coisa grande. A linguagem de macro M4 não se destina particularmente ao código. Você pode usá-lo para gerar qualquer texto. Também há macros de teclado, como esta resposta mencionada. Você pressiona 1 ou 2 teclas e elas se expandem para uma quantidade maior de pressionamentos de tecla. As macros do vim também são assim. Salve uma grande sequência de comandos no modo normal com uma única tecla e chame essa sequência executando o comando no modo normal que a executa.
JOL

2

Na família da linguagem C, uma definição de macro , um comando de pré-processador, especifica um modelo de código parametrizado que é substituído na chamada de macro sem ser compilado na definição. Isso significa que todas as variáveis ​​livres devem ser vinculadas no contexto da chamada de macro. Argumentos de parâmetro com um efeito colateral semelhante i++poderiam ser repetidos pelo uso plural do parâmetro. A substituição textual do argumento 1 + 2de algum parâmetro xocorre antes da compilação e pode causar comportamento inesperado x * 3( 7io 9). Um erro no corpo da macro da definição de macro será exibido apenas na compilação na chamada de macro.

A definição da função especifica código com variáveis ​​livres vinculadas ao contexto do corpo da função; não é a chamada de função .

No entanto, a macro aparentemente negativa fornece acesso à chamada, ao número da linha e ao arquivo de origem, ao argumento como string .


2

Em termos um pouco mais abstratos, uma macro é a sintaxe, como uma função é os dados. Uma função (no resumo) encapsula alguma transformação nos dados. Ele pega seus argumentos como dados avaliados, realiza algumas operações neles e retorna um resultado que também é apenas dados.

Uma macro, por outro lado, utiliza uma sintaxe não avaliada e opera com base nisso. Para idiomas do tipo C, a sintaxe entra no nível do token. Para idiomas com macros do tipo LISP, eles obtêm a sintaxe representada como um AST. A macro deve retornar um novo pedaço de sintaxe.


0

Uma macro geralmente se refere a algo expandido , substituindo a "chamada" da macro durante a compilação ou pré-processamento por instruções individuais no idioma de destino. Em tempo de execução, geralmente não há indicação de onde a macro começa e termina.

Isso é diferente de uma sub - rotina , que é um pedaço de código reutilizável, localizado separadamente na memória, para o qual o controle é passado no tempo de execução . As "funções", "procedimentos" e "métodos" na maioria das linguagens de programação se enquadram nessa categoria.

Como a resposta de Jörg W Mittag discute, os detalhes exatos variam entre os idiomas: em alguns, como C, uma macro executa substituição de texto no código-fonte; em alguns, como o Lisp, ele executa a manipulação de uma forma intermediária como uma Árvore de Sintaxe Abstrata. Existem também algumas áreas cinzentas: algumas linguagens têm notação para "funções embutidas", definidas como uma função, mas expandidas no programa compilado como uma macro.

A maioria dos idiomas incentiva os programadores a raciocinar sobre cada sub-rotina isoladamente, definindo contratos de tipo para entradas e saídas e ocultando outras informações sobre o código de chamada. Geralmente, há um impacto no desempenho ao chamar uma sub-rotina na qual as macros não incorrem e as macros podem ser capazes de manipular o programa de maneiras que uma sub-rotina não pode. Portanto, as macros podem ser consideradas "nível inferior" que as sub-rotinas, porque operam em uma base menos abstrata.


0

A macro é executada durante a compilação e a função é executada em tempo de execução.

Exemplo:

#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;
}

Portanto, durante a compilação, o código é realmente alterado para:

#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;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.