Linguagem C - Funções Variádicas

Artigo com o intuito de demonstrar o funcionamento de funções com número variável de argumentos. As famosas funções declaradas como func(int, ...).

[ Hits: 14.770 ]

Por: Enzo de Brito Ferber em 20/04/2016 | Blog: http://www.maximasonorizacao.com.br


MACROS Variádicas



Viva o CPP. Não, não viva o C++... CPP é o C Pre-Processor.

Dentre algumas das muitas coisas que tornam a linguagem C a melhor linguagem que existe, o pré-processador está, com absoluta certeza, entre as primeiras. Dando uma olhada nos fontes do Linux, você verá algumas s/gambiarras/maravilhas/ do pré-processador. Não entrarei em detalhes sobre todas as funcionalidades do CPP, já que este não é um artigo sobre ele, mas como este é um artigo sobre funções variádicas, achei que incluir uma parte sobre macros variádicas seria interessante.

A definição de uma macro variádica é exatamente igual a declaração das funções variádicas.

#define MACRO(a, b, ...)      f(a,b, __VA_ARGS__)

E para "acessar" a lista de parâmetros, utilizamos a "palavra reservada" __VA_ARGS__.

Mas, como já sabemos, o CPP não gera saída em linguagem de máquina. Ele é "simplesmente" um tradutor. Todas as macros são substituídas pelo CPP *antes* de qualquer código chegar ao compilador propriamente dito. Sendo um tradutor, não existe uma pilha para ele tirar os argumentos dela.

Portanto, não há endereços de variáveis como nas funções que vimos, e também não tem jeito de acessar cada argumento separadamente (jeito tem, mas é mais feio que nem o Deadpool - ver referências).

Você pode ver a saída do CPP digitando:

gcc -E arquivo.c

Aviso: os includes normalmente são bem grandes. Pra brincar e ver como o CPP funciona, recomendo não colocar nenhuma include. Apesar de ficarem no início do arquivo, é melhor não encher o terminal. Use apenas defines e veja a saída.

É bem legal. Continuando...

Lembra da nossa função debug()? Podemos reescrevê-la de maneira mais simples se tudo que ela fizer for o que definimos anteriormente (só chamar fprintf() com a lista de argumentos, sem fazer nenhum tipo de verificação de nível de log etc):

#ifndef PRODUCTION
  #define debug(fmt, ...)   fprintf(stderr, fmt, __VA_ARGS__)
#else
  #define debug(fmt, ...)
#endif

Bem melhor não é? Mas temos um problema. Se chamarmos debug() com apenas *um* argumento, o pré-processador gerará código quebrado. Pense:

debug("Variavel big_chunk foi alocada");

Se torna:

fprintf(strerr, "Variavel big_chunk foi alocada", );

Olha aquela virgulinha malvada ali no final... Isso não é bom.

Mas, o CPP é o cara, e nos fornece um meio muito bom de evitar problemas desse tipo, utilizando o operador de concatenação ##. E então nossa macro se torna:

#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)

Agora ela será traduzida, corretamente, para:

fprintf(strerr, "Variavel big_chunk foi alocada" );

E esse nome grandão, __VA_ARGS__? Podemos trocar também. É só colocar um nome (identificador) para ele, antes das reticências:

#define debug(fmt, args...) fprintf(stderr, fmt, ## args)

Isso é útil se você estiver trabalhando em alguma macro mais complicada, como algumas das belezuras encontradas no Linux.

/* debug_macro.c
 * 	Exemplo de MACRO com argumentos variáveis.
 *
 * 	Em desenvolvimento:
 * 	$ gcc -o debug_macro debug_macro.c
 *
 * 	Para entregar:
 * 	$ gcc -o debug_macro debug_macro.c -DPRODUCTION
 *
 * 	Para ver as expansões:
 * 	$ gcc -E debug_macro.c
 *
 * 	Comente o include e retire o ## da macro debug e tente compilar.
 * 	Veja também a saída o preprocessador usando o parâmetro -E e 
 * 	procure pela expansão da primeira chamada a debug.
 *
 * 	(C) 2016 - Enzo Ferber, <enzoferber@gmail.com>
 */
#include <stdio.h>

#ifndef PRODUCTION
 #define debug(fmt, args...)	fprintf(stderr, fmt, ## args)
#else
 #define debug(fmt, args...)
#endif

int main(void)
{
	debug("Teste sem argumentos
");
	debug("Verdade inteira: %d
", 42);
	debug("A resposta sobre o %s: %.1f
", "O universo e tudo mais", 42.000);
	return 0;
}

Página anterior     Próxima página

Páginas do artigo
   1. Introdução
   2. stdarg.h
   3. Exemplos
   4. Expandindo horizontes
   5. MACROS Variádicas
   6. Conclusão e referências
Outros artigos deste autor

Linguagem C - Listas Duplamente Encadeadas

Linguagem C - Árvores Binárias

Leitura recomendada

Mapear objetos em C

A duplicação do buffer de saída na chamada de sistema fork() do Linux

GNA: um Coprocessador para Aceleração Neural

Android NDK: Desmistificando o acesso a códigos nativos em C

Algum humor e C++ Design Patterns (parte 2)

  
Comentários
[1] Comentário enviado por sacioz em 21/04/2016 - 10:15h

Gostei , com certeza vou dar um controldê na pag.inicial
Obrigado...:-))

[2] Comentário enviado por removido em 21/04/2016 - 18:14h

Muito bom. Vou guardar como referência.

----------------------------------------------------------------------------------------------------------------
# apt-get purge systemd (não é prá digitar isso!)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden

[3] Comentário enviado por EnzoFerber em 30/04/2016 - 12:49h

Muito obrigado, @sacioz e @listeiro_037.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts