Variable arguments in C (or C++) 10-09-2016, 11:27 AM
#1
If you've ever wondered how printf can magically take as much as you throw at it (hehe), it isn't some compiler magic happening here. What's really happening is called variable arguments. When a function is called in C (in most cases, Borland, fuck you), it passes the first 4 integer arguments (or pointers) in registers (which vary by system). After that, it pushes them to the stack in reverse order. Once inside the function, you can access them by knowing the stack offset, frame level, and variable offset.
Now, that all sounds pretty tricky, and it really is a pain in the ass to do, so there is a set of helper macros in stdarg.h (new C, don't worry about old stuff, your compiler doesnt support it anymore).
EDIT: I have pastebinned all of the code to make SL happy
Here's what you need to take an infinite number of arguments:
An example for this is a simple printf definition:
the important thing to watch there is the three dots after the first argument. This tells the compiler NOT to use the standard ABI, and to push ALL of the arguments on the stack in reverse order. This way, we don't have to determine the frame order or any of that nonsense.
Alright, so we have a basic starting point, let's talk a little bit about that first argument before going into detail. We only NEED 1 argument, but we should think about what that argument actually is. We can have a near infinite number of args, but its not like passing in an array in Java where you can check the size. We don't get that courtesy, we have no clue how many args were actually provided (or what their types are). So, we should use some form of format specifier (like printf) so we know not only how many arguments but also what type they are.
With that in mind, let's write a printf with the following:
We start off with our base function stuff:
http://pastebin.com/fjqKCKUE
Ok, so, let's explain some of those. I put in some comments to explain relatively what they meant, but I'll break it down nontheless.
This is simply a pointer to the NEXT variable in our stack frame, we need this to get the first variable, and then every subsequent one, since each variable can take a different size, and the stack cannot be misaligned, so this variable takes care of keeping track of that for us.
This is where that mandatory argument comes into play. This argument is used to determine the variable offset in the call frame. A common problem is people thinking that you use the first argument here (fmt in our case). It actually must be the LAST argument in the function definition. This will make sense to you as you dive deeper into call frames and stacks.
think of this one as a sort of free() for the argument list. You don't need to understand how or why this works to work well with variable arguments.
Now, we can start parsing out arguments. To do this, we will use the following macro:
So, let's write our basic code:
http://pastebin.com/RtCtYJWi
Ok, now, if you need to be fed what just happened there, take a look at this article
We are now at the point where we can start pulling out args. Before we do that, let's make a few helper functions. These will make our myprintf code less cluttered and allow us to delegate which prints which type.
http://pastebin.com/vWq8z3JH
Ok, now we are ready. So, let's add in our parsing functions to our code:
http://pastebin.com/5pyArn41
And there you go! I know I skimped on pointers, but there are plenty of tutorials on those, and if you need a more in-depth one, feel free to reply and let me know.
Now, that all sounds pretty tricky, and it really is a pain in the ass to do, so there is a set of helper macros in stdarg.h (new C, don't worry about old stuff, your compiler doesnt support it anymore).
EDIT: I have pastebinned all of the code to make SL happy
Here's what you need to take an infinite number of arguments:
-
A function declaration, preferably in a header file you can include elsewhere
- One or more arguments that MUST be provided
- stdarg.h included in the code side (only)
An example for this is a simple printf definition:
Code:
void myprintf(const char * ...);
void myprintf(const char *fmt, ...)
{
}
the important thing to watch there is the three dots after the first argument. This tells the compiler NOT to use the standard ABI, and to push ALL of the arguments on the stack in reverse order. This way, we don't have to determine the frame order or any of that nonsense.
Alright, so we have a basic starting point, let's talk a little bit about that first argument before going into detail. We only NEED 1 argument, but we should think about what that argument actually is. We can have a near infinite number of args, but its not like passing in an array in Java where you can check the size. We don't get that courtesy, we have no clue how many args were actually provided (or what their types are). So, we should use some form of format specifier (like printf) so we know not only how many arguments but also what type they are.
With that in mind, let's write a printf with the following:
Code:
%i -> integer
%u -> unsigned
%s -> string
We start off with our base function stuff:
http://pastebin.com/fjqKCKUE
Ok, so, let's explain some of those. I put in some comments to explain relatively what they meant, but I'll break it down nontheless.
Code:
va_list args;
Code:
va_start(args, fmt);
Code:
va_end(args);
Now, we can start parsing out arguments. To do this, we will use the following macro:
Code:
type va_arg(list, type);
So, let's write our basic code:
http://pastebin.com/RtCtYJWi
Ok, now, if you need to be fed what just happened there, take a look at this article
We are now at the point where we can start pulling out args. Before we do that, let's make a few helper functions. These will make our myprintf code less cluttered and allow us to delegate which prints which type.
http://pastebin.com/vWq8z3JH
Ok, now we are ready. So, let's add in our parsing functions to our code:
http://pastebin.com/5pyArn41
And there you go! I know I skimped on pointers, but there are plenty of tutorials on those, and if you need a more in-depth one, feel free to reply and let me know.