ti-enxame.com

Por que usar macros em C?

Possível duplicado:
Quais são as macros C úteis?

A cada poucos meses, fico com vontade de aprender um pouco de C que minha educação em programação de faculdades nunca cobriu. Hoje são macros. Meu entendimento básico de macros é que eles são uma simples pesquisa e substituição que acontece no seu código prior para que ele seja compilado. Estou tendo problemas para entender por que você usaria macros. A maioria dos exemplos básicos que eu estou olhando são algo como 

TEST(a,%d);
#define TEST(a,b) printf(" The value of " #a " = " #b " \n", a)

//which expands to 
printf(" The value of a = %d \n",a);

(exemplo de aqui )

Do meu ponto de vista newbie, parece que definir uma nova função lhe daria os mesmos resultados. Eu posso ver como historicamente macros seria útil para modificar muita fonte rapidamente nos dias antes da busca fácil e substituir, mas algo me diz que estou perdendo algum ponto maior. 

Então, que tipo de coisas úteis as macros podem fazer por você?

47
Alan Storm

Um motivo é até C99, a palavra-chave inline não era padrão na linguagem C. Assim, as macros permitiram que você ativasse pequenas funções. Eles também funcionam como templates, por exemplo. você não precisa especificar tipos na definição de macro, por exemplo:

#define MAX(x,y) ((x) > (y) ? (x) : (y))

Esta macro é complacente com números inteiros, duplos, floats etc.

43
DeusAduro

Não é exatamente procurar e substituir, é a expansão do token. As macros C são o que todo tipo de macro é no mundo da computação: uma maneira de escrever algo curto e simples e transformá-lo automaticamente em algo mais longo e mais complicado. 

Uma razão pela qual as macros são usadas é o desempenho. Eles são uma maneira de eliminar a sobrecarga de chamada de função porque eles sempre são expandidos em linha, diferentemente da palavra-chave "inline" que é um hint freqüentemente ignorado para o compilador, e nem sequer existe (no padrão) antes de C99. Por exemplo, veja a família de macros FD_ usada em conjunto com os fd_sets usados ​​por select e pselect. Esses fd_sets são realmente apenas bitsets, e as macros FD_ estão escondendo operações de bit twiddling. Seria irritante escrever o bit de cada vez, e uma chamada de função seria muito sobrecarga para uma operação tão rápida se não fosse inlined.

Além disso, as macros podem fazer algumas coisas que as funções não podem. Considere a colagem de token. Como o pré-processador é executado antes do compilador, ele pode fazer novos identificadores para o compilador usar. Isso pode fornecer uma maneira abreviada de criar muitas definições semelhantes, por exemplo,.

#define DEF_PAIR_OF(dtype) \
  typedef struct pair_of_##dtype { \
       dtype first; \
       dtype second; \
  } pair_of_##dtype##_t 

 DEF_PAIR_OF(int);
 DEF_PAIR_OF(double);
 DEF_PAIR_OF(MyStruct);
 /* etc */

Outra coisa que pode fazer que uma função não pode é transformar informações de tempo de compilação em informações de tempo de execução:

#ifdef DEBUG
#define REPORT_PTR_VALUE(v) printf("Pointer %s points to %p\n", #v, v)
#else 
#define REPORT_PTR_VALUE(v)
#endif

void someFunction(const int* reallyCoolPointer) {
  REPORT_PTR_VALUE(reallyCoolPointer);
  /* Other code */
}

Não há como uma função poder usar o nome de seu parâmetro em sua saída como a macro pode. Isso também demonstra a compilação de código de depuração para compilações de lançamento.

64
Tyler McHenry

As macros são expandidas no tempo de compilação. Você está correto que eles são frequentemente abusados, mas um exemplo clássico em C seria ter uma macro para escrever mensagens de depuração que (quando o modo de depuração está desativado no momento da compilação) não gera nenhum código, portanto, não causa lentidão.

15
Mike McQuaid

Às vezes você quer fazer o log, mas apenas no modo de depuração. Então você escreve algo como:

#ifdef DEBUG
#define LOG_MSG(x) printf(x);
#else
#define LOG_MSG(X)
#endif

Às vezes você só quer desligar ou ligar alguma coisa com base em um comutador de compilação. Por exemplo, minha empresa faz alguns co-branding de nossos produtos e parceiros solicitam a desativação. Então faremos algo como:

#ifndef SPECIAL_PARTNER_BUILD
    DoSomethingReallyAwesome();
#endif

Em seguida, construa com -DSPECIAL_PARTNER_BUILD quando o parceiro desistir.

Entre muitas outras razões possíveis.

Às vezes você só quer salvar a digitação para as coisas que você faz uma e outra vez. Algumas pessoas levam isso para longe ( cough MFC cough ) e escrevem o que corresponde à sua própria linguagem em Macros para abstrair uma API difícil. Isso faz com que a depuração de um pesadelo frikkin '.

12
i_am_jorf

Macros podem ter muitos usos diferentes além de funcionar como coisas.

É muito útil usar macros para qualquer número mágico ou string relacionado.

#define MY_MAGIC_NUM 57

/*MY_MAGIC_NUM used all through out the code*/
5
lillq

Macros C podem gerar código em tempo de compilação. Isso pode ser (ab) usado para criar efetivamente linguagens específicas de domínio com novas palavras-chave e comportamentos.

Um dos meus exemplos favoritos é o método de Simon Tatham de implementar corrotinas em C através de macros.

4
Dour High Arch

Usamos esse tipo de macro em nosso código:

// convenience macros for implementing field setter/getter functions
#ifndef CREATE_GET_SET
#define CREATE_GET_SET(PREFIX, FIELD_NAME,FIELD_DATATYPE) \
  protected:\
    FIELD_DATATYPE PREFIX ## FIELD_NAME;\
  public:\
  inline FIELD_DATATYPE get ## _ ## FIELD_NAME(void) const\
  { \
    return(PREFIX ## FIELD_NAME); \
  } \
  inline void set ## _ ## FIELD_NAME(FIELD_DATATYPE p) \
  { \
    PREFIX ## FIELD_NAME = p; \
  }
#endif

dentro de uma classe/estrutura, você definiria uma variável:

CREATE_GET_SET(_, id, unsigned int);

Isso definiria sua variável e criaria o getter/setter genérico para o código. Isso apenas gera uma geração de código mais limpa e consistente para get/set. Claro, você pode escrever tudo, mas isso é um monte de código tipo boilerplate. NOTA: esta é apenas uma das duas macros. Eu não postei todos eles. Você não aceitaria dizer "char *" dessa maneira (onde você deseja que o conjunto seja strncpy ou strcpy os dados). Esta foi apenas uma simples demonstração do que você poderia fazer com uma macro e alguns tipos simples.

2
jmq

Eu acho que a maior vantagem vem quando usado como "arquivo de configuração"

#define USE_FAST_AGORHITM
//#define USE_SLOW_ONE

E globais "constantes"

#define MAX_NUMBER_OF_USERS 100000

Existem programas escritos principalmente em macros (Verifique a implementação do AES de Brian Gladman na instância http://www.gladman.me.uk/cryptography_technology/index.php )

1
Luka Rahne

Em cenários de desempenho intenso, você pode usar macros para criar o desdobramento de loop "automático". Os compiladores modernos provavelmente fazem um trabalho melhor com isso, mas isso costumava ser um aplicativo útil para isso.

1
korona

O compilador geralmente conhece detalhes sobre a máquina de destino, portanto, você pode usar a compilação condicional para ter um pedaço de código para processadores big endian e outro para processadores little endian. Isso evita que o código fique inchado com código projetado para um processador diferente.

Um caso semelhante é quando você tem o código Assembly para um sistema específico, mas o código C para outros sistemas.

1
Nosredna

Em macros, o código da função é inserido no fluxo de código do chamador. Isso pode, dependendo de muitas outras coisas, melhorar o desempenho, porque o otimizador pode integrar processualmente o código chamado - otimizar o código chamado para o chamador Mas cuidado, porque em macros os tipos de argumento não são verificados. ] Uma boa referência para funções embutidas (se você também estiver usando C++) e um pouco de macros é. http://www.parashift.com/c++-faq-lite/inline-functions. html # faq-9.5

0
Diego Dias