How does the particular C function work? - c

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.

Related

How do Variable length arguments work in C?

I am trying to understand how variable length arguments work in C.
Basically when a variable length argument function(ex: printf(const char *format, ...);) is called, where the arguments are copied (stack/register?)? and how the called function gets the information about the arguments passed by calling function?
I highly appreciate any form of help.
Thanks in advance.
The use of variable arguments list is a standard feature of 'C' language, and as such must be enforced on any machine for which exist a C compiler.
When we say any machine we mean that independently from the way used for parameters passing, registers, stack or both, we must have the feature.
In effect what is really needed to implement the functionality is the deterministic nature of the process. It is not relevant if parameters are passed in stack, register, both, or other MCU custom ways, what is important is that the way it is done is well defined and always the same.
If this property is respected we are sure that we can always walk the parameters list, and access each of them.
Actually the method used to pass parameters for each machine or system, is specified in the ABI (Application Binary Interface, see https://en.wikipedia.org/wiki/Application_binary_interface), following the rules, in reverse, you can always backtrack parameters.
Anyway on some system, the vast majority, the simple reverse engineering of the ABI isn't sufficient to recover parameters, i.e. parameter sizes different from standard CPU register/stack size, in this case you need more info about the parameter you are looking for: the operand size.
Let review the variable parameter handling in C. First you declare a function having a single parameter of type integer, holding the count of parameters passed as variable arguments, and the 3 dots for variable part:
int foo(int cnt, ...);
To access variable arguments normally you use the definitions in <stdarg.h> header in the following way:
int foo(int cnt, ...)
{
va_list ap; //pointer used to iterate through parameters
int i, val;
va_start(ap, cnt); //Initialize pointer to the last known parameter
for (i=0; i<cnt; i++)
{
val = va_arg(ap, int); //Retrieve next parameter using pointer and size
printf("%d ", val); // Print parameter, an integer
}
va_end(ap); //Release pointer. Normally do_nothing
putchar('\n');
}
On a stack based machine (i.e. x86-32bits) where the parameters are pushed sequentially the code above works more or less as the following:
int foo(int cnt, ...)
{
char *ap; //pointer used to iterate through parameters
int i, val;
ap = &cnt; //Initialize pointer to the last known parameter
for (i=0; i<cnt; i++)
{
/*
* We are going to update pointer to next parameter on the stack.
* Please note that here we simply add int size to pointer because
* normally the stack word size is the same of natural integer for
* that machine, but if we are using different type we **must**
* adjust pointer to the correct stack bound by rounding to the
* larger multiply size.
*/
ap = (ap + sizeof(int));
val = *((int *)ap); //Retrieve next parameter using pointer and size
printf("%d ", val); // Print parameter, an integer
}
putchar('\n');
}
Please note that if we access types different from int e/o having size different from native stack word size, the pointer must be adjusted to always increase of a multiple of stack word size.
Now consider a machine that use registers to pass parameters, for simplicity we consider that no operand could be larger than a register size, and that the allocation is made using the registers sequentially (also note the pseudo assembler instruction mov val, rx that loads the variable val with contents of register rx):
int foo(int cnt, ...)
{
int ap; //pointer used to iterate through parameters
int i, val;
/*
* Initialize pointer to the last known parameter, in our
* case the first in the list (see after why)
*/
ap = 1;
for (i=0; i<cnt; i++)
{
/*
* Retrieve next parameter
* The code below obviously isn't real code, but should give the idea.
*/
ap++; //Next parameter
switch(ap)
{
case 1:
__asm mov val, r1; //Get value from register
break;
case 2:
__asm mov val, r2;
break;
case 3:
__asm mov val, r3;
break;
.....
case n:
__asm mov val, rn;
break;
}
printf("%d ", val); // Print parameter, an integer
}
putchar('\n');
}
Hope the concept is clear enough now.
Traditionally, the arguments were "always" push on the stack, regardless of other register passing optimisations, and then va_list was basically just a pointer into the stack to identify the next argument to va_arg. However, register passing is so favoured on new processors and compiler optimisation settings, that even varargs are put as registers.
With this, va_list becomes a small data structure (or a pointer to that data structure) which captures all those register arguments, /and/ has a pointer into the stack, if the number of arguments are too many. The va_arg macro first steps through the captured registers, then steps through the stack entries, so va_list also has a "current index".
Note that at least in the gcc implementation va_list is a hybrid object: When declared in the body it is an instance of the structure, but when passed as an argument, it magically becomes a pointer, like a C++ reference even though C doesn't have the concept of references.
In some platforms va_list also allocates some dynamic memory, which is why you should always call va_end.
where the arguments are copied (stack/register?)?
It varies. On x64 normal conventions are used: the first few arguments (depending on type) probably go into registers, and other arguments go onto the stack. The C standard requires that the compiler support at least 127 arguments to a function, so it's inevitable that some of them are going to go on the stack.
how the called function gets the information about the arguments passed by calling function?
By using the initial arguments, such as the printf format string. The varargs support facilities in C doesn't allow the function to inspect the number and types of arguments, only to get them one at a time (and if they're improperly casted, or if more arguments are accessed than were passed, the result is undefined behavior).
Most implementations push the arguments on the stack, using register won't work well on register-starved architectures or if there's more arguments than registers generally.
And the called function doesn't know anything at all about the arguments, their count or their types. That's why e.g. printf and related functions use format specifiers. The called function will then interpret the next part of the stack according to that format specifier (using the va_arg "function").
If the type fetched by va_arg doesn't match the actual type of the argument, you will have undefined behavior.
As extracted from ABI document, The method to store all the arguments is provided by the ABI document of an architecture.
Reference Link: https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf (page number 56).
The Register Save Area:
The prologue of a function taking a variable argument list and known to call the
macro va_start is expected to save the argument registers to the register save area. Each argument register has a fixed offset in the register save area.
C h\s the standard mechanisms to access those parameters. Macros are defined in the stdarg.h
http://www.cse.unt.edu/~donr/courses/4410/NOTES/stdarg/
here you have a very simple implementation of the sniprintf
int ts_formatstring(char *buf, size_t maxlen, const char *fmt, va_list va)
{
char *start_buf = buf;
maxlen--;
while(*fmt && maxlen)
{
/* Character needs formating? */
if (*fmt == '%')
{
switch (*(++fmt))
{
case 'c':
*buf++ = va_arg(va, int);
maxlen--;
break;
case 'd':
case 'i':
{
signed int val = va_arg(va, signed int);
if (val < 0)
{
val *= -1;
*buf++ = '-';
maxlen--;
}
maxlen = ts_itoa(&buf, val, 10, maxlen);
}
break;
case 's':
{
char * arg = va_arg(va, char *);
while (*arg && maxlen)
{
*buf++ = *arg++;
maxlen--;
}
}
break;
case 'u':
maxlen = ts_itoa(&buf, va_arg(va, unsigned int), 10, maxlen);
break;
case 'x':
case 'X':
maxlen = ts_itoa(&buf, va_arg(va, int), 16, maxlen);
break;
case '%':
*buf++ = '%';
maxlen--;
break;
}
fmt++;
}
/* Else just copy */
else
{
*buf++ = *fmt++;
maxlen--;
}
}
*buf = 0;
return (int)(buf - start_buf);
}
int sniprintf(char *buf, size_t maxlen, const char *fmt, ...)
{
int length;
va_list va;
va_start(va, fmt);
length = ts_formatstring(buf, maxlen, fmt, va);
va_end(va);
return length;
}
It is from the atollic studio tiny printf.
All the mechanisms (including the passing the list of the parameters to another functions are shown here.

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.

How to automatically add type casts to printf style functions in c source code?

I'm porting a large c project from Windows to Unix and the source contains many thousand calls for a logprint function which is declared like this:
VOID logprint(DWORD level, LPCSTR format, ...);
Now here are my two problems:
1.) Used format type specifiers are not portable
The code uses %lu for ULONG variables. On Windows this is fine because ULONG is a typedef for unsigned long. However when porting the code I cannot reproduce this typedef because ULONG must always be exactly 32-bit according to [MS-DTYP] (NB: With Microsoft's c compilers unsigned long is always 32-bit).
So I've created a windows types header file wtypes.h which defines the basic Windows data types with the help of stdint.h and limits.h.
Of course now this results in invalid reads because of the %lu specifier if the systems unsigned long is 64-bit and my ULONG is 32-bit. So I also have to add a (unsigned long) cast to all ULONG logprint arguments.
And ULONG is just one example of course ...
2.) Invalid format type specifiers used
In addition that code uses lots of invalid format specifiers. E.g. %d for DWORD arguments.
Of course it is easy to solve:
identify all logprint calls
identify the type of each argument
verify that the correct format specifier is used
add the correct type casts to the arguments
Example:
Replace:
ULONG ulMin, ulMax;
...
logprint(FATAL, "specified interval is invalid %ld..%u out of range",
ulMin, ulMax);
with:
logprint(FATAL, "specified interval is invalid %lu..%lu",
(unsigned long) ulMin, (unsigned long) ulMax);
But it would take me at least two weeks and my brain will be garbled after that.
So my actual question:
Are there any automated tools for making these kind of changes?
As a minimum requirement that tool would have to be able to identify the type of the arguments and prefix them with a type cast. Once the typecasts are there I can easily write a python script which fixes the format specifiers.
Is the source of the logprint accessible? If it is, the best way seems to change it directly. It must contain type casting code for va_arg such as:
ul = va_arg(argp, ULONG);
then just change ULONG as you needed.
If it is not, just make your own wrapper function such as logprint64 doing the similar task but casting the types for the arguments as needed. Substituting logprint64 for logprint will take less than a hour, I guess.
Or, you may rewrite logprint. According to your post and reply, the logprint seems be in the following form:
#include <stdio.h>
#include <stdarg.h>
enum ErrCode { FATAL, MILD };
typedef unsigned short ULONG;
#define MAX 100
char Buf[MAX];
void logprint(enum ErrCode code, char *fmt, ...)
{
va_list aptr;
va_start(aptr, fmt);
vsprintf(Buf, fmt, aptr);
va_end(aptr);
}
int main()
{
ULONG ulMin = 97, ulMax = 99;
logprint(FATAL,"interval is invalid %c..%c", ulMin, ulMax);
printf("%s\n", Buf);
return(0);
}
You can replace it with the following definition simulating vsprintf:
void logprint(enum ErrCode code, const char *fmt, ...)
{ // add your types as needed
ULONG h;
unsigned long u;
long d;
int i;
const char *p;
char *buf;
va_list argp;
va_start(argp, fmt);
for (p = fmt, buf = Buf; *p != '\0'; p++) {
if (*p != '%') {
buf += sprintf(buf, "%c", *p); continue;
}
switch (*++p) { // change the type casting as needed
case 'l':
switch (*++p) {
case 'u':
u = (unsigned long) va_arg(argp, ULONG);
buf += sprintf(buf, "%lu", u); continue;
case 'd':
d = va_arg(argp, long);
buf += sprintf(buf, "%ld", d); continue;
}
case 'c':
u = va_arg(argp, unsigned long);
buf += sprintf(buf, "%lu", u); continue;
case 'd':
i = va_arg(argp, int);
buf += sprintf(buf, "%d", i); continue;
}
}
va_end(argp);
}
Hope this helps.

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 and va_arg : variabilizing the type to be printed

I am trying to recode a printf function so I need to use va_arg since printf is a variadic function.
The thing is, when I want to print out the arguments of my_printf, they can be %i (int) or %s (char *) if we take these two basic possibilities.
So when I go through my va_list ap, I tried to put a variable in the va_arg function so I can change the type to be written, like this:
char *str = NULL;
str = ft_set_my_types(flags[cur_arg]);
ft_putstr(va_arg(ap, str));
with my function ft_set_my_types being something like:
char *ft_set_my_types(char *flags)
{
char **tab = NULL;
tab = ft_alloc_mem2(2, 15); /* this function malloc a tab */
char a = 65;
/* the example here is only with two types of variables */
/* I am sending flags %s in my example which correspond to "char *" */
tab['s'] = "char *";
tab['d'] = "int ";
size_t len = ft_strlen(flags) - 1;
while (a < 123)
{
if (a == flags[len])
break ;
else
a++;
}
return (tab[a]);
}
The "char *flags" being everything between the "%" and the flag (included) of my_printf.
The problem is: when I compile it does not understand my tab['s'] as char * but as "char *" so I get this error:
ft_print.c:63:25: error: unknown type name 'str'
ft_putstr(va_arg(ap, str));
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.2/include/stdarg.h:35:50: note:
expanded from macro 'va_arg'
#define va_arg(ap, type) __builtin_va_arg(ap, type)
1 error generated.
Could you help me to figure out how I can insert my variable *str in this va_arg function?
C is statically typed. All data types are determined at compile time. You cannot obtain the data type for a declaration by calling a function.
Instead, to handle variadic arguments whose type is not known until run time, you must branch based on the selected data type, via a switch statement or an if/else if/else nest. In each alternative, use the va_arg macro to obtain the argument, with the appropriate static type handled by that alternative. You may be able to perform the type-specific processing in helper functions, if you like.
For example,
/* ... */
switch (field_code) {
case 'i':
do_something_with_int(va_arg(ap, int));
break;
case 's':
do_something_with_string(va_arg(ap, char *));
break;
default:
handle_error();
break;
}
/* ... */
Note that the second argument to va_arg is the type itself, not a string representation of it.

Resources