Standard way to manipulate variadic arguments? - c

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.

Related

How do I print function parameters in C?

I know this question has been asked many times but sorry, I couldn't find the answer. Below is a function with parameters (how many parameters is unknown). How do I get all parameters and then print them?
int func(int a, int b, ...) {
// print the parameters
}
The short answer is "you don't." C doesn't give you any mechanism to know when the arguments end.
If you want to use varargs, you will need to give yourself a mechanism that will tell you how many arguments there are, and how big each one is. To take the most well-known example, printf() requires its first argument to be a formatting string, that tells it about the varargs and their sizes.
If you know that all your arguments are going to be the same size (say, ints), you can design your routine so the first argument is the number of arguments, something like:
void
my_func (int n_args, ...)
{
va_list ap;
int i;
va_start(ap, n_args);
for (i = 0 ; i < n_args ; i++) {
process(va_arg(ap, int));
}
va_end(ap);
}

How to replace values in va_list?

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.

Variadic function works in Win32 but not in Win64

I have been using the following function for quite some time:
void AddRow(int iNumOfColumns,...)
{
int* pValuePerColumn = (int*)&iNumOfColumns+1;
for (int i=0; i<iNumOfColumns; i++)
{
// Do something with pValuePerColumn[i]
}
}
Now it turns out that it crashes on Win64 for one of our customers.
I do not have a 64-bit platform at hand, but I am assuming that the reason is:
When the function is invoked, the arguments are pushed into the stack as 64-bit values.
Under this assumption, I believe that replacing int* with size_t* should resolve the problem.
My questions are:
Is my analysis correct?
Is my solution correct?
Is there a more "conventional" way for solving this?
Derefencing a pointer to one past the last element of the array, or a non array object, is undefined behavior:
int* pValuePerColumn = (int*)&iNumOfColumns+1;
...
pValuePerColumn[i]
Changing the type to size_t is irrelevant for this problem.
The only correct way of using variable arguments are macros provided in stdarg.h.
You should use varargs to access extra parameters in portable way. Look for va_list docs. Probably your code should look next
void AddRow(int iNumOfColumns,...)
{
va_list ap;
va_start(ap, iNumOfColumns);
for (int i=0; i<iNumOfColumns; i++)
{
int col = va_arg(ap, int);
// Do something with col
}
va_end(ap);
}
And as I remember on Win64 first four integer args are passed via registers, not via the stack, so tricks with pointers won't work.

Variadic function in C99 to deallocate several arrays?

Currently, I have a very simple function to deallocate array of doubles in my program:
void deallocate(double** array)
{
free(*array);
}
I would like this function to be variadic in order to take several arrays, and free them one after another. I've never written a variadic function, and as there may exist tricks with pointers I would like to know how to do that.
Don't do this with a variadic function, this concept should be retired. In particular it makes no sense at all for something that is to receive arguments of all the same type, void*.
Just have a simple function, first that receives an array of pointers
void free_arrays(void* a[]) {
for (size_t i = 0; a[i]; ++i) free(a[i]);
}
Then you can wrap that with a macro like that
#define FREE_ARRAYS(...) free_arrays((void*[]){ __VA_ARGS__, 0 })
This supposes that none of your pointers is already 0, since the processing would stop at that point.
If you'd have a need to have that working even if some of the pointers are 0, you'd have to pass the number of elements as a first parameter to your function. This is a bit tedious but can be determined in the macro, too.
void free_arrays0(size_t n, void* a[]) {
for (size_t i = 0; i < n; ++i) free(a[i]);
}
#define FREE_ARRAYS0(...) \
free_arrays( \
sizeof((void*[]){ __VA_ARGS__})/sizeof(void*), \
(void*[]){ __VA_ARGS__} \
)
You can do it like this:
void deallocate(double *p, ...)
{
va_list ap;
va_start(ap, p);
do {
free(p);
p = va_arg(ap, double *);
} while (p);
va_end(ap);
}
Call as deallocate(p1, p2, p3, (double *)NULL). You need the NULL (or some other value) as a sentinel to signal the end of the argument list; none of the other pointers should be NULL or the loop will stop prematurely.
I'm not saying that this is a good idea, though: varargs functions have their use cases, but they're error-prone with pointers because some implicit conversions don't take place (because the compiler doesn't know the type of the arguments beyond the first).

Do C functions support an arbitrary number of arguments?

PHP has a func_get_args() for getting all function arguments, and JavaScript has the functions object.
I've written a very simple max() in C
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
I'm pretty sure in most languages you can supply any number of arguments to their max() (or equivalent) built in. Can you do this in C?
I thought this question may have been what I wanted, but I don't think it is.
Please keep in mind I'm still learning too. :)
Many thanks.
You could write a variable-arguments function that takes the number of arguments, for example
#include <stdio.h>
#include <stdarg.h>
int sum(int numArgs, ...)
{
va_list args;
va_start(args, numArgs);
int ret = 0;
for(unsigned int i = 0; i < numArgs; ++i)
{
ret += va_arg(args, int);
}
va_end(args);
return ret;
}
int main()
{
printf("%d\n", sum(4, 1,3,3,7)); /* prints 14 */
}
The function assumes that each variable argument is an integer (see va_arg call).
Yes, C has the concept of variadic functions, which is similar to the way printf() allows a variable number of arguments.
A maximum function would look something like this:
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
static int myMax (int quant, ...) {
va_list vlst;
int i;
int num;
int max = INT_MIN;
va_start (vlst, quant);
for (i = 0; i < quant; i++) {
if (i == 0) {
max = va_arg (vlst, int);
} else {
num = va_arg (vlst, int);
if (num > max) {
max = num;
}
}
}
va_end (vlst);
return max;
}
int main (void) {
printf ("Maximum is %d\n", myMax (5, 97, 5, 22, 5, 6));
printf ("Maximum is %d\n", myMax (0));
return 0;
}
This outputs:
Maximum is 97
Maximum is -2147483648
Note the use of the quant variable. There are generally two ways to indicate the end of your arguments, either a count up front (the 5) or a sentinel value at the back.
An example of the latter would be a list of pointers, passing NULL as the last. Since this max function needs to be able to handle the entire range of integers, a sentinel solution is not viable.
The printf function uses the former approach but slightly differently. It doesn't have a specific count, rather it uses the % fields in the format string to figure out the other arguments.
In fact, this are two questions. First of all C99 only requires that a C implementation may handle at least:
127 parameters in one function
definition
127 arguments in one function call
Now, to your real question, yes there are so-called variadic functions and macros in C99. The syntax for the declaration is with ... in the argument list. The implementation of variadic functions goes with macros from the stdarg.h header file.
here is a link to site that shows an example of using varargs in c Writing a ``varargs'' Function
You can use the va_args function to retrieve the optional arguments you pass to a function. And using this you can pass 0-n optional parameters. So you can support more then 2 arguments if you choose
Another alternative is to pass in an array, like main(). for example:
int myfunc(type* argarray, int argcount);
Yes, you can declare a variadic function in C. The most commonly used one is probably printf, which has a declaration that looks like the following
int printf(const char *format, ...);
The ... is how it declares that it accepts a variable number of arguments.
To access those argument it can uses va_start, va_arg and the like which are typically macros defined in stdarg.h. See here
It is probably also worth noting that you can often "confuse" such a function. For example the following call to printf will print whatever happens to be on the top of the stack when it is called. In reality this is probably the saved stack base pointer.
printf("%d");
C can have functions receive an arbitrary number of parameters.
You already know one: printf()
printf("Hello World\n");
printf("%s\n", "Hello World");
printf("%d + %d is %d\n", 2, 2, 2+2);
There is no max function which accepts an arbitrary number of parameters, but it's a good exercise for you to write your own.
Use <stdarg.h> and the va_list, va_start, va_arg, and va_end identifiers defined in that header.
http://www.kernel.org/doc/man-pages/online/pages/man3/stdarg.3.html

Resources