How to replace values in va_list? - c

I want to do some exercise about va_list. This is my code.
int myscanf( char* fmt, ... ) {
va_list ap;
va_start ( ap, fmt );
vfscanf ( stdin, fmt, ap );
va_end ( ap );
}
int main() {
int a, b;
myscanf( "%d %d", &a, &b );
}
As I shown above, I have written a scanf() and it is work.
Now I want to redirect the value of arguments in myscanf().
For example, I can redirect fmt to the space which is allocated in myscanf()
int myscanf( char* fmt, ... ) {
char newFmt[10] = "%d %d";
va_list ap;
va_start ( ap, fmt );
vfscanf ( stdin, newFmt, ap );
va_end ( ap );
}
However, I feel confused when I try to change the value of others arguments.
I can fetch these variable argument by va_arg(), but I can't modify them because va_arg() is a macro.
int myscanf( char* fmt, ... ) {
va_list ap;
va_start ( ap, fmt );
int* arg1 = (int)va_arg(ap, int*); // get the value of &a in main()
int newA; // I want to read a value by scanf() and store it to &newA
// ??? = &newA // <- how to do?
vfscanf ( stdin, fmt, ap );
va_end ( ap );
}
Any suggestion?
-----------edit-----------
Thanks for replies,
But something should be clarified.
The "value" in this case is "address". Therefore, my purpose is changing the target address so that the vfscanf() will read and write the value to the another address space.
For example,
int gA, gB, gC, gD;
int myscanf( char* fmt, ... ) {
va_list ap;
va_start ( ap, fmt );
// do something for making the following vfscanf() to write value into gC and gD directly
vfscanf ( stdin, fmt, ap );
// don't assign *gA to *gC and *gB to *gD after performing vfscanf()
va_end ( ap );
}
int main() {
myscanf( "%d %d", &gA, &gB );
}
As I change fmt to newFmt, we want to change the value (in this case is address) in va_list directly.
And the parsing problem is solved because that I can allocate a space dynamically while I parse a "%..." from format string. These addresses of spaces will replace inputs repeatedly if the question above is solved.

Variadic Functions
The arguments to scanf will always be pointers, not values as in your example. The correct way of getting an argument of scanf would be int *arg1 = va_arg(ap, int*); - and you don't need to cast.
If you want to manipulate the way scanf behaves, you have to know first how variadic functions work (you can get it by reading the manual of any of the va_* family of functions). The variable ap in most architectures is a pointer to the function's stack frame. It points to the next variable after fmt in this case.
Your example
In the case of scanf in your example, it will point to a list of pointers (because all arguments to scanf must be pointers). So you should put that into your pointers like this:
int *a = va_arg(ap, int*);
/* Then you can modify it like this: */
*a = 666;
There are some problems with this.
When you finish manipulating the arguments, you must pass fmt and ap to vfscanf, which will then parse fmt and expect n elements (the amount of elements in the format string). The problem is that ap now will only give us n - x elements (x being the number of elements you "poped" in your own function). A little example:
myscanf("%d %d", &a, &b);
/* n = 2 */
...
int *a = va_arg(ap, int *);
/* x = 1 */
...
vfscanf(stdin, fmt, ap);
/* n = 2 cause fmt is still the same, however
* x = 1, so the number of elements "popable" from the stack is only
* n - x = 2 - 1 = 1.
*/
In this simple example you can already see the problem. vfscanf will call va_arg for each element it finds in the format string, which is n, but only n - x are popable. This means undefined behavior - vfscanf will be writing somewhere it shouldn't and most probably will crash your program.
Hack Around
To overcome that, I propose a little work with va_copy. The signature of va_copy is:
void va_copy(va_list dest, va_list src);
And something to know about it (from the manual):
Each invocation of va_copy() must be matched by a corresponding invocation of va_end() in the same function. Some systems that do not supply va_copy() have __va_copy instead, since that was the name used in the draft proposal.
The solution:
#include <stdio.h>
#include <stdarg.h>
int myscanf(char *fmt, ...)
{
va_list ap, hack;
/* start our reference point as usual */
va_start(ap, fmt);
/* make a copy of it */
va_copy(hack, ap);
/* get the addresses for the variables we wanna hack */
int *a = va_arg(hack, int*);
int *b = va_arg(hack, int*);
/* pass vfscanf the _original_ ap reference */
vfscanf(stdin, fmt, ap);
va_end(ap);
va_end(hack);
/* hack the elements */
*a = 666;
*b = 999;
}
int main(void)
{
int a, b;
printf("Type two values: ");
myscanf("%d %d", &a, &b);
printf("Values: %d %d\n", a, b);
return 0;
}
Conclusion and Warnings
There are a couple of things you should note. First, if you put the hacking of the elements before calling vfscanf, the values you set will be lost, because vfscanf will overwrite those locations.
Next, you should also note that this is a very specific use case. I knew beforehand that I was going to pass two integers as arguments, so I designed myscanf with that in mind. But this means you need a parsing pass to find out which arguments are of which type - if you don't do it, you'll enter undefined behavior again. Writing that kind of parser is very straight-forward and shouldn't be a problem.
After your edit
After what you said in your clarification edit, I can only propose a little wrapper function around vfscanf(), because you can't directly manipulate va_list variables. You can't write directly to the stack (in theory, you can't, but if you did some inline-assembly you could, but that's gonna be an ugly hack and very non-portable).
The reason it's gonna be extremely ugly and non-portable is that the inline assembly will have to take into account how the architecture treats argument passing. Writing inline-assembly by itself is already very ugly... Check out this for the official GCC manual on it's inline assembly.
Back to your problem:
Stack Overflow: How do I fill a va_list
That answer explains a whole lot, so I won't say it here again. The final conclusion of the answer is **no, you don't do it". What you _can do however, is a wrapper. Like this:
#include <stdio.h>
#include <stdarg.h>
int a, b, c, d;
void ms_wrapper(char *newfmt, ...)
{
va_list ap;
va_start(ap, newfmt);
vfscanf(stdin, newfmt, ap);
va_end(ap);
}
int myscanf(char *fmt, ...)
{
/* manipulate fmt.... */
char *newfmt = "%d %d";
/* come up with a way of building the argument list */
/* call the wrapper */
ms_wrapper(newfmt, &c, &d);
}
int main(void)
{
a = 111;
b = 222;
c = 000;
d = 000;
printf("Values a b: %d %d\n", a, b);
printf("Values c d: %d %d\n\n", c, c);
printf("Type two values: ");
myscanf("%d %d", &a, &b);
printf("\nValues a b: %d %d\n", a, b);
printf("Values c d: %d %d\n", c, d);
return 0;
}
Beware that you can only build argument lists for variadic functions in your compile-time. You can't have a dynamically changing list of parameters. In other words, you'll have to hard-code each case you'd ever wanna handle. If the user enters something different, your program will behave very oddly and most probably crash.

The only way is to pass updated arguments directly, since va_list can not be modified. In your case you should parse format string to have an idea about actual content of va_list and then pass compatible set of arguments to fscanf() (not vfscanf()) directly.

It is not possible directly but you can do as below.
int myscanf( char* fmt, ... ) {
va_list ap;
va_start ( ap, fmt );
int newA;
scanf("%d",&new);
vfscanf ( stdin, fmt, ap );
va_end ( ap );
}
I think this will do same as you want.

On a given platform you may use some tricky hack:
va_list is basically a pointer to some data (typically char *),
va_arg is basically pointer arithmetic and cast
So, you can allocate an array of two pointers to int, set the values and call vfscanf with it. Something like:
int *hack[2];
hack[0] = &gC;
hack[1] = &gD;
vscanf(stdin, fmt, (va_list)hack);
BEWARE this is highly non portable, very tricky and error prone. There is a lot of problem with such, even if it basically works on many platforms.

Related

Functions with infinitely many (unnamed) arguments - how to properly access these arguments?

I have a tough question for you, that I have been playing around with today, but for which I could not yet come up with an elegant solution.
We know, that functions of the form void func() take infinitely many arguments. Now I have put together a very simple minimal, working code snippet:
#include <stdio.h>
int func()
{
printf("%d, %d, %d, %d\n");
return 0;
}
int func() {
sucks(0 /* Offset */, 1, 2, 3, 4);
}
Alright, we can now call func() with as many arguments as we want. The questions that I am experimenting with atm is: how can we properly access the content of those arguments? The printf() function prints out something like this ... just to verify to ourselves that the arguments are actually there:
anonymous#melina:/tmp$ ./a.out
1 2 3 4
So now the question: The above snippet is a bit hackish. Is there a proper way to access these arguments? Or do you have to actually mess around with the stack pointer and inline assembly? I have, as a first try, thought about getting the stack pointer at the beginning of the function, e.g.,
uint64_t sp;
asm( "mov %%rsp, %0" : "=rm" ( sp ));
... and somehow use it, to guess where (in memory) those arguments actually are. However ... I have had no success so far.
Is there a proper way to access these arguments?
Yes. Specify a parameter list in the function definition with types and identifiers of the function.
You can do it "old style" (don't do it, it should not be used in new code):
int func(a, b, c, d)
int a;
int b;
int c;
int d;
{
printf("%d %d %d %d\n", a, b, c, d);
}
Or normal:
int func(int a, int b, int c, int d) {
printf("%d %d %d %d\n", a, b, c, d);
}
or using stdarg.h :
int func(int a, ...) {
va_list ap;
va_start(va, a);
int b = va_arg(va, int);
int c = va_arg(va, int);
int d = va_arg(va, int);
printf("%d %d %d %d\n", a, b, c, d);
va_end(ap);
}
or using stdarg.h from the second arg:
int func(int a, int b, ...) {
va_list ap;
va_start(va, b);
int c = va_arg(va, int);
int d = va_arg(va, int);
printf("%d %d %d %d\n", a, b, c, d);
va_end(ap);
}
or using stdarg.h from the third arg:
int func(int a, int b, int c, ...) {
va_list ap;
va_start(va, c);
int d = va_arg(va, int);
printf("%d %d %d %d\n", a, b, c, d);
va_end(ap);
}
stdarg.h needs at least one, first argument in the parameter list to be declared. So there is no way to handle function with unspecified number and type of arguments, but there is a way (stdarg.h) to handle function with at least 1 argument followed by unspecified number and type of arguments.
... functions of the form void func() take infinitely many arguments
This is not true. I don't think there is a C standard way to pass infinite number of arguments to a function.
Function of the form func() take unspecified number and type of arguments. They take a finite number of arguments, but the number is unspecified in the transaction unit where such function declaration is encountered. If the function definition takes 5 arguments, they take 5 arguments, otherwise undefined behavior. The number of arguments is not specified, if the function definition takes elipsis (, ...) in which case no information about the number or types of the parameters after the comma is supplied. (C11 6.7.6.3p9).
Or do you have to actually mess around with the stack pointer and inline assembly?
Yes, you have to "mess around" with implementation defined behavior.
From GCC docs 18.10:
... The ISO implementation of va_start takes an additional second argument. The user is supposed to write the last named argument of the function here.
However, va_start should not use this argument.
You need to specify the second argument to va_start. Maybe more on such topic can be found in this thread.
printf("%d %d %d %d\n");
This is undefined behavior. Once your program has undefined behavior, nasal demons start spawning in your room and are fighting for independence. It may have well defined behavior on your platform or implementation, but this is wrong, it's no longer a C language. If there is a "correct" behavior according to your mental model of the language, that model is simply wrong; from this thread. From the language point of view, nasal demons fight for independence and the program is invalid.
[...] functions of the form void func() take infinitely many arguments.
Nope.
Functions used without a prototype must be called according to their definition.
void foo(); // not a prototype
int main(void) {
// foo() must be called according to its definition
foo(12, 4, "bar", -42); // error (undetected by compiler)
foo("---", 12, 4, "bar", -42); // no error
}
void foo(const char *msg, int n, ...) { /* ... */ }
Note that pre-C89 function definitions had a different way of defining arguments. Desire for backwards compatability still allows for this syntax. I believe the same restrictions apply as above
void bar() /* pre-C89 syntax */
const char *msg;
int n;
{ /* ... */ }

How to write to variables passed to the Variadic function in C

I'm totally new to C and I'm wondering is it possible to create a variadic function and pass pointers of variables into it and write data into the variables?
One obvious example of what I'm looking for is the scanf function which takes the input from the stdin and writes it into the variables.
Here is a sample of want I want to do:
void fun(int num, ...){
// insert 2 in a and "abc" in b
}
int main(void){
int a;
char *b;
fun(2, &a, &b);
}
update I can alter my constructor to get variables pattern instead of the number of them, so here is the code after modification:
void fun(char *fmt, ...){
// insert 2 in a and "abc" in b
}
int main(void){
int a;
char *b;
fun("dc", &a, &b);
}
Start with the example code shown in the stdarg man page (man 3 stdarg). Slightly modified for readability, and adding a trivial main():
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
void foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt) {
switch (*(fmt++)) {
case 's':
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd':
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c':
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
}
va_end(ap);
}
int main(void)
{
char *s1 = "First";
char *s2 = "Second";
int d = 42;
char c = '?';
foo("sdcs", s1, d, c, s2);
return EXIT_SUCCESS;
}
If you compile and run the above, it will output
string First
int 42
char ?
string Second
As liliscent and Jonathan Leffler commented to the question, the key point is that we need a way to describe the type of each variadic argument. (In C, type information is essentially discarded at compile time, so if we want to support multiple types for one argument, we must also pass its type explicitly: the type (of a variadic function argument) simply does not exist at run time anymore.)
Above, the first parameter, fmt, is a string where each character describes one variadic argument (by describing its type). Thus, there are expected to be the same number of variadic arguments as there are s, d, or c characters in the fmt string.
The printf family of functions and the scanf family of functions both use % to indicate a variadic argument, followed by the formatting details and type specification of that argument. Because of the quite complex formatting they support, the code implementing those is much more complicated than the above example, but the logic is very much the same.
In an update to the question, OP asked if the function can change the value of the variadic arguments -- or rather, the values pointed to by the variadic arguments, similar to how scanf() family of functions work.
Because parameters are passed by value, and va_arg() yields the value of the parameter, and not a reference to the parameter, any modifications we make to the value itself locally (to s, d, or c in the foo() function example above) will not be visible to the caller. However, if we pass pointers to the values -- just like scanf() functions do --, we can modify the values the pointers point to.
Consider a slightly modified version of the above foo() function, zero():
void zero(char *fmt, ...)
{
va_list ap;
int *d;
char *c, **s;
va_start(ap, fmt);
while (*fmt) {
switch (*(fmt++)) {
case 's':
s = va_arg(ap, char **);
if (s)
*s = NULL;
break;
case 'd':
d = va_arg(ap, int *);
if (d)
*d = 0;
break;
case 'c':
/* pointers are fully promoted */
c = va_arg(ap, char *);
if (c)
*c = 0;
break;
}
}
va_end(ap);
}
Note the differences to foo(), especially in the va_arg() expressions. (I would also suggest renaming d, c, and s to dptr, cptr, and sptr, respectively, to help remind us humans reading the code that they are no longer the values themselves, but pointers to the values we wish to modify. I omitted this change to keep the function as similar to foo() as possible, to keep it easy to compare the two functions.)
With this, we can do for example
int d = 5;
char *p = "z";
zero("ds", &d, &p);
and d will be cleared to zero, and p to be NULL.
We are not limited to a single va_arg() within each case, either. We can, for example, modify the above to take two parameters per formatting letter, with the first being a pointer to the parameter, and the second the value:
void let(char *fmt, ...)
{
va_list ap;
int *dptr, d;
char *cptr, c, **sptr, *s;
va_start(ap, fmt);
while (*fmt) {
switch (*(fmt++)) {
case 's':
sptr = va_arg(ap, char **);
s = va_arg(ap, char *);
if (sptr)
*sptr = s;
break;
case 'd':
dptr = va_arg(ap, int *);
d = va_arg(ap, int);
if (dptr)
*dptr = d;
break;
case 'c':
cptr = va_arg(ap, char *);
/* a 'char' type variadic argument
is promoted to 'int' in C: */
c = (char) va_arg(ap, int);
if (cptr)
*cptr = c;
break;
}
}
va_end(ap);
}
This last function you can use via e.g.
int a;
char *b;
let("ds", &a, 2, &b, "abc");
which has the same effect as a = 2; b = "abc";. Note that we do not modify the data b points to; we just set b to point to a literal string abc.
In C11 and later, there is a _Generic keyword (see e.g. this answer here), that can be used in conjunction with preprocessor macros, to choose between expressions depending on the type(s) of the argument(s).
Because it does not exist in earlier versions of the standards, we now have to use for example sin(), sinf(), and sinl() to return the sine of their argument, depending on whether the argument (and desired result) is a double, float, or a long double. In C11, we can define
#define Sin(x) _Generic((x), \
long double: sinl, \
float: sinf, \
default: sin)(x)
so that we can just call Sin(x), with the compiler choosing the proper function variant: Sin(1.0f) is equivalent to sinf(1.0f), and Sin(1.0) is equivalent to sin(1.0), for example.
(Above, the _Generic() expression evaluates to one of sinl, sinf, or sin; the final (x) makes the macro evaluate to a function call with the macro parameter x as the function parameter.)
This is not a contradiction to the earlier section of this answer. Even when using the _Generic keyword, the types are checked at compile time. It is basically just syntactic sugar on top of macro parameter type comparison checking, that helps writing type-specific code; in other words, a kind of a switch..case statement that acts on preprocessor macro parameter types, calling exactly one function in each case.
Furthermore, _Generic does not really work with variadic functions. In particular, you cannot do the selection based on any variadic arguments to those functions.
However, the macros used can easily look like variadic functions. If you want to explore such generics a bit further, see e.g. this "answer" I wrote some time ago.

Print one more parameter with va_args

I have a function which implements a custom variant of printf.
Does anyone have a clue how I can print out one more parameter with va_list? My format is like "%d some text %s". I need a, to be printed as first parameter.
void func (int a, char *fmt, ...) {
va_list ap;
va_start (ap, fmt);
// vprintf(stdout, fmt, a, ap) //Can't do like this :(
vfprintf(fmt, a, ap); //Or like this :(
va_end(ap);
}
If a is always printed the same way, it shouldn't be part of the format passed in. You should instead print it separately.
In the example you gave, change your format string to "some text %s". Then your function can do this:
void func (int a, char *fmt, ...) {
va_list ap;
// first print the function specific fields
printf("%d ", a);
// then the user's format
va_start (ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
As noted in comments:
Redesign the interface
You would do best to redesign the interface so that the format string only applies to the ... (va_list) arguments; the fixed argument gets treated separately.
Standard reasons for wanting extra arguments are logging things like __FILE__ and __LINE__ (presumably not __func__ if you don't have C99 __VA_ARGS__ support — see below). You would probably do best to remove the option of caller-controlled formatting of those fixed arguments, leaving the fmt to process just the ... arguments.
If you can't redesign the interface
The fact that the %d is part of the format and the int value is not part of the va_list makes life tricky.
If you cannot, or decline to, change the rules for calling the function, you'll need to parse the format, for example separating it into the part up to (but not including) the second %, and use that format with the standalone argument; the remainder of the format is then passed to vprintf(). If you're on a POSIX system and in a multi-threaded program, you might want to use flockfile() and funlockfile() on stdout to ensure that they're treated as a single unit of output despite the multi-threading.
// Using C99 features
void func(int a, char *fmt, ...)
{
char *p1 = strchr(fmt, '%');
if (p1 == 0)
{
// No conversion specifications. Not puts(); it adds a newline
fputs(fmt, stdout);
return;
}
char *p2 = strchr(p1 + 1, '%');
if (p2 == 0)
{
// The user invoked func(1, "iteration %d:\n");?
printf(fmt, a);
return;
}
int buflen = p2 - fmt + 1;
char buffer[buflen];
memmove(buffer, fmt, buflen);
buffer[buflen-1] = '\0';
// flockfile(stdout); // Multi-threading
printf(buffer, a);
va_list ap;
va_start(ap, fmt);
vprintf(p2, ap);
va_end(ap);
// funlockfile(stdout); // Multi-threading
}
The code cheats; it uses a C99 VLA (variable length array) and also interleaved declarations and statements. In C90, therefore, you'd probably end up using malloc(), or perhaps a fixed size buffer (running risks of overflows), or a hybrid (use a fixed size buffer if the prefix of the format string is small enough to fit, or allocate (and free) if not).
You need to decide on the appropriate error handling strategies. Note that this isn't bullet-proof code. Calling func(a, "%% yield is %d; data is [%s]\n", data); throws another spanner in the works. Also, if you're on POSIX and someone passes "it is %2$s and %1$d things happened", you're in deep trouble.
Yes, I'm on POSIX system. But it is quite old, I am even not able to use approaches with __VA_ARGS__ macros as it appears in C99+ standards.
It's curious that you're on such an old system. However, that simply means you probably won't write code with the n$ notations in it, so it is one less thing to worry about. The multi-threading issue is less likely to be a problem either. And you can't use the other C99 features that were shown in my sample code.
You could look at the FFI (foreign function interface) library; I'm not at all sure it will help, though.
Based on previous answer to parse format, I've found a simple solution (if %d is always first):
void func (int a, char *fmt, ...) {
char *new_format = malloc(10*sizeof(char));
snprintf(new_format, 10, "%d", a);
strcat(new_format, (fmt+2));
va_list ap;
va_start (ap, fmt);
vprintf(stdout, new_format, ap);
va_end(ap);
}
Special Thanks to Jonathan Leffler

How does the particular C function work?

I am trying to learn C and am very confused already.
In the OOP languages i have used there exists the ability to perform method overloading, where the same function could have different parameter types and call whichever was the most appropriate.
Now in C i know that this is not the case so i cant figure out the following problem, How printf() works.
For example:
char chVar = 'A';
int intVar = 123;
float flVar = 99.999;
printf("%c - %i - %f \n",chVar, intVar, flVar);
printf("%i - %f - %c \n",intVar, flVar, chVar);
printf("%f - %c - %i \n",flVar, chVar, intVar);
Now as C does'nt support function overloading, How does printf manage to take any number of arguments, of any type, and then work correctly with them?
I have tried to find the printf() working by downloading the glibc source package but can quite seem to find it, though i'll keep looking.
Could anyone here explain how C performs the above task?
C supports a type of function signature called "varargs" meaning "variable (number of) arguments". Such a function must have at least one required argument. In the case of printf, the format string is a required argument.
Generally, on a stack-based machine, when you call any C function, the arguments are pushed onto the stack from right-to-left. In this way, the first argument to the function is that found on the "top" of the stack, just after the return address.
There are C macros defined which allow you to retrieve the variable arguments.
The key points are:
There is no type-safety for the variable arguments. In the case of printf(), if the format string is wrong, the code will read invalid results from memory, possibly crashing.
The variable arguments are read through a pointer which is incremented through the memory containing those arguments.
The argument pointer must be initialized with va_start, incremented with va_arg, and released with va_end.
I have posted a ton of code you may find interesting on the related question:
Best Way to Store a va_list for Later Use in C/C++
Here's a skeleton of a printf() which only formats integers ("%d"):
int printf( const char * fmt, ... )
{
int d; /* Used to store any int arguments. */
va_list args; /* Used as a pointer to the next variable argument. */
va_start( args, fmt ); /* Initialize the pointer to arguments. */
while (*fmt)
{
if ('%' == *fmt)
{
fmt ++;
switch (*fmt)
{
case 'd': /* Format string says 'd'. */
/* ASSUME there is an integer at the args pointer. */
d = va_arg( args, int);
/* Print the integer stored in d... */
break;
}
}
else
/* Not a format character, copy it to output. */
fmt++;
}
va_end( args );
}
Internally, printf will (at least usually) use some macros from stdarg.h. The general idea is (a greatly expanded version of) something like this:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
int my_vfprintf(FILE *file, char const *fmt, va_list arg) {
int int_temp;
char char_temp;
char *string_temp;
char ch;
int length = 0;
char buffer[512];
while ( ch = *fmt++) {
if ( '%' == ch ) {
switch (ch = *fmt++) {
/* %% - print out a single % */
case '%':
fputc('%', file);
length++;
break;
/* %c: print out a character */
case 'c':
char_temp = va_arg(arg, int);
fputc(char_temp, file);
length++;
break;
/* %s: print out a string */
case 's':
string_temp = va_arg(arg, char *);
fputs(string_temp, file);
length += strlen(string_temp);
break;
/* %d: print out an int */
case 'd':
int_temp = va_arg(arg, int);
itoa(int_temp, buffer, 10);
fputs(buffer, file);
length += strlen(buffer);
break;
/* %x: print out an int in hex */
case 'x':
int_temp = va_arg(arg, int);
itoa(int_temp, buffer, 16);
fputs(buffer, file);
length += strlen(buffer);
break;
}
}
else {
putc(ch, file);
length++;
}
}
return length;
}
int my_printf(char const *fmt, ...) {
va_list arg;
int length;
va_start(arg, fmt);
length = my_vfprintf(stdout, fmt, arg);
va_end(arg);
return length;
}
int my_fprintf(FILE *file, char const *fmt, ...) {
va_list arg;
int length;
va_start(arg, fmt);
length = my_vfprintf(file, fmt, arg);
va_end(arg);
return length;
}
#ifdef TEST
int main() {
my_printf("%s", "Some string");
return 0;
}
#endif
Fleshing it out does involve quite a bit of work -- dealing with field width, precision, more conversions, etc. This is enough, however, to at least give a flavor of how you retrieve varying arguments of varying types inside your function.
(Don't forget that, if you're using gcc (and g++?), you can pass -Wformat in the compiler options to get the compiler to check that the types of the arguments match the formatting. I hope other compilers have similar options.)
Could anyone here explain how C performs the above task?
Blind faith. It assumes that you have ensured that the types of the arguments match perfectly with the corresponding letters in your format string. When printf is called, all the arguments are represented in binary, unceremoniously concatenated together, and passed effectively as a single big argument to printf. If they don't match, you'll have problems. As printf iterates through the format string, every time it see %d it will take 4 bytes from the arguments (assuming 32-bit, it would be 8 bytes for 64-bit ints of course) and it will interpret them as an integer.
Now maybe you actually passed a double (typically taking up twice as much memory as an int), in which case printf will just take 32 of those bits and represented them as an integer. Then the next format field (maybe a %d) will take the rest of the double.
So basically, if the types don't match perfectly you'll get badly garbled data. And if you're unlucky you will have undefined behaviour.

Standard way to manipulate variadic arguments?

This is a weird question, but is there a standard way to manipulate the contents of a va_list before passing it to another function? For instance, suppose I have two functions, sum and vsum:
int vsum(int n, va_list ap) {
int total = 0;
for (int i = 0; i < n; ++i) {
total += va_arg(n, int);
return total;
}
int sum(int n, ...) {
va_list ap;
va_start(ap, n);
int total = vsum(n, ap);
va_end(ap);
return total;
}
If I call sum as sum(4, 1, 2, 3, 4), I expect to get the result 10. Now let's suppose that instead of calling vsum directly, sum calls an intermediate function, vsum_stub which does the following:
int vsum_stub(int n, va_list ap) {
va_list temp_ap;
va_copy(temp_ap, ap);
for (int i = 0; i < n; ++i) {
int *arg = &va_arg(ap, int);
*arg += 2;
}
va_end(temp_ap);
return vsum(n, ap);
}
Now when I call sum(4, 1, 2, 3, 4), I should get back the result 20, since vsum_stub increments all of the values in the va_list by 2. This doesn't compile of course since you can't take the address of the result of va_arg. Is there another way to do this though? I'm working in C99.
Background:
I'm working on a library that does some pointer translation so that data may be stored on the heap in a more efficient format. Programs are compiled with a custom transformation which converts calls to library functions like printf to my own stub functions (e.g., hc_printf). hc_printf needs to translate any pointer arguments (strings intended for %s) before passing the arguments to the real printf function.
Edit: Here's a code example. Let's say we have a string foo. foo is dynamically allocated with a modified version of malloc which returns a fake pointer. The compiler modifies the program so that it can deal with fake pointers. So this works:
char *foo = fake_malloc(4);
fake_strcpy(foo, "foo");
I want to write a fake_vprintf function like this (in pseudocode):
int fake_vprintf(const char *format, va_list args) {
for each pointer argument p in args
translate p to q, a real pointer to contiguous memory
replace p with q in args
}
return vprintf(format, args);
}
The program would call fake_vprintf just like the original vprintf using the fake pointer. fake_vprintf translates the fake pointer to a real pointer that the real vprintf can use.
Aha, as I understand, your problem is creating a new va_list argument to pass on to the standard vprintf functions. Which in turn, will require you to modify each member of the list. However, since there is no element wise fetch/edit/insert operation for such a list you are stuck.
I don't really see any way of doing this. Of course, you can create a vprintf apply the transformations in situ, one argument at a time. My suggestion will be: Reimplement all such standard library functions -- at any rate you are writing wrappers. This involves some work, but you are already doing a part of it with hc_printf etc, so why not go the whole distance (and guess what save on a function call!).
You probably can't use va_list in a platform-agnostic way. You'll have to look at how your environment defines a va_list in stdarg.h, and then write your own tools to work with it.
For example, if a va_list is just a (char *), you can do all sorts of things with it.
// add 1000 to the integer stored on the stack and advance va_list
*(int *)va_list += 1000;
va_list += sizeof(int);
You're telling the compiler that you want it to consider va_list a pointer to an int (via the int * cast), then take the value (*) and add 1000 to it (+= 1000). Now advance the va_list pointer to the next argument on the stack.

Resources