ft_printf
by htsang
written for version 9.2
Your first lego house!
Why? Because the lego house looks complicated with a lot details, yet it does not feel intimidating. You don't feel the same way?
Probably you don't see what the pieces for building the house are.
Let's forget about you and me being a programmer for a moment. Let's tackle this project as if we were a builder who tries to build a house. Meaning: We'll draft and plan before building.
You will need to use a struct. Structs surely look terrifying, but they are just arrays with named items. For getting to know the, play around them a little bit! Then, for checking whether you are ready: When should you use #Prerequisites
va_...
-macros.va_start()
?#Bonus part
struct.item
, and when struct->item
?
The bonus is about handling combinations of conditions. In the mandatory part, since you have 9 formats to handle, you could handle it easily with one layer of if-else statements. In the bonus, you have to handle an extra layer of conditions: flags. Make notes on what you are supposed to print in different combinations of flags and format! Do not start writing any code until you know what are you expected to do! Building a house needs planning, why not in coding? Plan ahead! Try structuring your code into two parts:#During
va_arg
can take in its second parameter?write()
can fail – did you catch it? If you are using your Libft functions that use write()
, did they return the return value of write()
?write()
and the original printf() in the same program and observing their printing order.#Bonus part
cs%
) and number formats (diuxXp
) respectively?
If you end up with a lot of files, make sure even more that your (Example:#Cleaning Up
git ls-files
.#Bonus part
%
is a format like everything else. That means flags should work with it, too.ft_printf(“%5%”)
should print 4 spaces and then a percent sign.%#X
prints a 0X
(uppercase) instead of a 0x
(lowercase).Makefile
does not relink.
You have defined some helper functions that depend on your libft.
Then you need to compile Libft first, helper functions, and the core logic functions at last. It is also a good idea to only put files that need to compile together in a folder.)
If we put every function inside a single However, that makes it tough to maintain your code – What if in the next project you need to add a floating point format to your Instead, you could put functions in different files and can search for them easily (as long as you name them properly(!)). This way, you wouldn't even have to open the file to know where you'd have to look. Good. Now you have multiple files, then the problem is about how you split your functions into different files sensibly. One thing to avoid are circular You can find a lot of ways to resolve it by just changing the your For example, the following is an excerpt of a folder structure for ft_printf. Each group of functions is stored under a folder with its corresponding header file, which conveys what dependencies that group of functions needs. And so all the functions will just include the header file from that folder. The Makefile also makes explicit how each group of files depend on the other: #Aftercare
#1. Explicit files dependencies
.c
file, we do not need to think about file dependencies because there is only 1 file and everything will work, and you also don’t necessarily need a Makefile.ft_printf()
? You'd have to find the functions inside this humongous .c
file you'd need to change. Not good.#include
s. If moduleA.h includes moduleB.h, and moduleB.h includes moduleA, #include
would expand infinitely. Thankfully, the C compiler will figure that out, and not compile your code.#include
s, but it might be a sign that you are unclear about which files your file depends on. The easiest way is to group functions with the same dependencies under the same folder, but keep in mind that the group should also make sense conceptually so that it will be easy for you to look for the functions you want to change inside it..
|- libft/
|---- libft.h
|- printers/ # functions that are related to do printing
|---- printers.h
|- parsers/ # functions that collect information from the input string
|---- parsers.h
|- ft_printf.h
|- ft_printf.c
${MAIN_OBJS}: ${PARSERS_OBJS} ${PRINTERS_OBJS}
${PARSERS_OBJS}: ${LIBFT_OBJS}
In Take in the memory address (in other words, a pointer) of a struct as an argument, and change its value inside the function. Use static variables inside functions. Now the variable is attached to the function, and whenever you need to read it, you can just call this function.#2. Passing values around layers of functions
ft_printf()
, you are required to return how many characters have you written to STDOUT
, we call that len
in the following. The intuitive way is to have most functions return len
, so pass the len
value around and eventually out. But what if you need to pass around multiple states? These are some ways you can try:void example(t_struct *struct)
int *example2(int input)
{
static int state = NULL;
// Change the static variable depending on the input of the function
return (&state);
}
Crazy things about printf: #Pointers
%n
.
TL;DR: Don't ever use %n
until you really understand it.