<aside> 🖊️ As coisas que precisei aprender para entender e construir o projeto printf. Fique à vontade para comentar e contribuir!
</aside>
palavras-chave: função variádica, string de formatação, token, parsing
<aside> ⚠️ Sabemos que não é todo mundo que tem o privilégio de poder dedicar 100% do seu tempo integralmente para a 42. Estes guias surgiram com o objetivo de agilizar o processo de descobrir quais os conhecimentos necessários para conseguir resolver o problema proposto pelo projeto, economizando tempo valioso pra codar. Evitei ao máximo dar soluções prontas — porque desenhar, implementar, testar e construir a solução também faz parte do seu aprendizado na 42. Mesmo assim, esta página pode conter alguns spoilers de implementação, por isso, prossiga com precaução!
</aside>
Você provavelmente já usou a função printf()
(que precisa do #include <stdio.h>
) alguma vez, nem que seja para debugar os seus códigos. Essa é aquela função que precisa de pelo menos um parâmetro que seja um string, e que escreve coisas no terminal quando o programa é executado. Essa string inicial pode conter coisas como %i
ou %s
, que no final das contas serão substituídos pelo valor do que for passado nos parâmetros seguintes da função. Algo mais ou menos assim:
#include <stdio.h>
int main(void)
{
int idade = 29;
char *nome = "Rods";
printf("Me chamo %s e tenho %i anos!\\n", nome, idade);
return (0)
}
(Ao ser executado, no terminal deve aparecer isso: Me chamo Rods e tenho 29 anos!
)
Este projeto é tão simplesmente para você criar a sua própria versão da função printf()
, e a partir de então vc poderá adicioná-la na sua libft e a utilizar sempre que precisar!
Entendo que o principal objetivo desse projeto é aprendermos como funcionam funções variádicas (ou seja, funções que recebem uma quantidade variável/indefinida de argumentos de entrada). Para mim, foi também uma oportunidade de aprofundar mais no uso do Makefile, e de revisitar todo o processo de compilação de códigos, que vai desde escrever os códigos fonte em .c
, transformá-los em códigos objeto (arquivos .o
), gerar a biblioteca estática .a
, até gerar o arquivo binário final ****que é o que executamos pelo terminal usando o comando ./
. Coisas que estivemos utilizando desde a Libft, mas que só fizeram sentido mesmo pra mim neste projeto.
Vale notar que o que a função printf
faz, basicamente, é ler uma string de caracteres, e interpretá-la! Isso significa que a função tem uma inteligência a mais, para além de somente ler caracter por caracter, ela é capaz de entender em que pontos dessa string ela precisará fazer uma pausa, ler um outro argumento, escrever esse argumento no terminal de um jeito específico, e então continuar de onde parou a leitura da string original de formatação. A chave para essa análise “inteligente” da string, no caso da printf, é um símbolo: o %
. Não porque um sinal de porcentagem é particularmente mágico ou especial, mas porque no passado, elegeram esse caracter como um caracter especial com uma função especial dentro de uma string de formatação*.* O termo técnico para esse símbolo é token. E o termo técnico para esse processo de interpretar inteligentemente uma sequência de caracteres a partir de tokens, símbolos especiais, é parsing. Neste projeto você escreverá o seu primeiríssimo parser 😊
É um senso comum que a parte mandatória desse projeto é muito simples de ser implementada, já a parte bonus tem um grau bem maior de complexidade. Eu implementei uma partezinha do bonus (a flag #
) apenas, porque precisava entegar o projeto rápido, mas no meu Github dá pra ver que a estrutura básica que criei pra implementar essa flag (o sctruct flags
e a função capture_flags()
) já dá conta de acomodar as outras partes do bonus.
Boa sorte! 🍀
As funções permitidas para fazer esse projeto são as ferramentas que precisamos para trabalhar com funções variádicas. Assim como nas funções (não variádicas) que viemos construindo — em que nós definimos exatamente quantos e quais serão os parâmetros de entrada da função (declaração), e esses parâmetros são inicializados em no momento em que a função é chamada (inicialização), e quando chega ao final da nossa elas são automaticamente liberadas da memória (e se for o caso de usar o malloc, precisamos dar free para liberá-las) —, assim é com os argumentos das funções variádicas. Eles serão todos guardados em uma lista (do tipo va_list), essa lista precisa ser inicializada (va_start) e finalizada (va_end), e para navegar pelos argumentos da função, precisamos sempre informar qual é o tipo do argumento a ser recuperado da lista (int, long int, void *, etc.).
Dá uma lida na documentação dessas funções e também em algum código que implementa essas funções (lá embaixo), e depois, pra aquecer os motores, sugiro seguir esse checklist para se familiarizar com argumentos variáveis:
printf()
)? Como uma função assim é declarada? Declare uma função variádica.va_start()
, va_arg()
, va_copy()
, e va_end()
? Observou como elas dependem de uma variável do tipo va_list
para funcionar? Implemente as funções va_start
, va_arg
e va_end
na função que você declarou no passo anterior.va_*
? Construa uma função variádica de teste que recebe como primeiro argumento de entrada um int, que sinaliza quantos argumentos serão passados depois dele, e que imprime um por um esses argumentos na tela.