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 ofscanf
would beint *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 theva_*
family of functions). The variableap
in most architectures is a pointer to the function's stack frame. It points to the next variable afterfmt
in this case.Your example
In the case of
scanf
in your example, it will point to a list of pointers (because all arguments toscanf
must be pointers). So you should put that into your pointers like this:There are some problems with this.
When you finish manipulating the arguments, you must pass
fmt
andap
tovfscanf
, which will then parsefmt
and expectn
elements (the amount of elements in the format string). The problem is thatap
now will only give usn - x
elements (x
being the number of elements you "poped" in your own function). A little example:In this simple example you can already see the problem.
vfscanf
will callva_arg
for each element it finds in the format string, which isn
, but onlyn - 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 ofva_copy
is:And something to know about it (from the manual):
The solution:
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, becausevfscanf
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 manipulateva_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:
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:
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 ofva_list
and then pass compatible set of arguments tofscanf()
(notvfscanf()
) directly.On a given platform you may use some tricky hack:
va_list
is basically a pointer to some data (typicallychar *
),va_arg
is basically pointer arithmetic and castSo, you can allocate an array of two pointers to int, set the values and call vfscanf with it. Something like:
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.
It is not possible directly but you can do as below.
I think this will do same as you want.