Palavras-chave: C, C++, varargs, argumentos variáveis
Para escrever funções que aceitam quantidade variável de parâmetros, usa-se o mecanismo de stdargs do C. Para isso coloca-se “…” como último parâmetro na declaração da função e depois usa-se as funções de stdarg.h para acessar os valores passados quando a funcão é chamada.
-
É obrigatório a inclusão de pelo menos um parâmetro normal antes da parte variável. (ex.:
char *concat(const char *s, ...)
) -
Qualquer tipo de valor pode ser passado como parâmetro, mas é impossível determinar em runtime o tipo dos valores que uma função recebeu. Porém é possível criar formas de o código que chama a funcão comunicar os tipos dos valores passados. Por exemplo, a função printf() determina os tipos dos parâmetros recebidos pela string de formatação (%s para char*, %i para int etc); a função
concat()
no exemplo espera que todos os parâmetros recebidos sejam strings e assim por diante. -
O número de parâmetros recebido também não pode ser determinado explicitamente. É necessário usar artifícios adicionais como passar uma variável extra com o número de valores sendo passados, usar o valor NULL como indicador de último parâmetro ou derivar a quantidade de valores a partir da string de formatação, como no caso do printf() e similares.
Exemplo:
#include <stdarg.h> #include <stdlib.h> #include <string.h> char *concat(const char *s, ...) { va_list args; char *tmp; char *res; size_t len= strlen(s); // pega um handle ao início da lista de parâmetros va_start(args, s); // calcula o tamanho total de todas as strings // pega o próximo parâmetro da lista, até chegar no NULL while ((tmp= va_arg(args, char*))) { len+= strlen(tmp); } va_end(args); res= malloc(len+1); if (!res) return NULL; // cria a string concatenada strcpy(res, s); va_start(args, s); // pega o próximo parâmetro da lista, até chegar no NULL while ((tmp= va_arg(args, char*))) { strcat(res, tmp); } va_end(args); return res; } int main() { char *s= concat("hello", " ", "world", "!!!!", NULL); puts(s); free(s); return 0; }
Se você quer usar macros com número de parâmetros variável (por exemplo, uma macro que chama uma função com stdargs), veja o post sobre o assunto.
Kojima: s/#include /#include /
Errr, maldito wordpress comendo os > e < dos comentários. Corrigindo:
Kojima: s/#include <stdargs>/#include <stdarg>/
Ops, obrigado pelo toque.
Morreu novamente ?
/me Tentando manter esse ótimo site ativo …. :)
Alfredo
Belo post, acho que complementa o anterior… agora usar strlen/strcat não é considerado ‘herético’? Heheheheh…
Vai uma dica de gcc:
pragma GCC poison strlen strcpy sprintf
Abraços
Adenilson
Adenilson: não vejo qual seria o problema com o strlen(). E se a gente já sabe que o tamanho do buffer é suficiente (como no código acima), também não vejo problema com strcat() e strcpy().
sprintf() até entendo, já que é difícil ter certeza que a string resultante realmente vá caber.