Background: I am currently trying to "extend" standard C formatting with support for handling a certain struct, similar to how Objective-C extends C formatting to allow support for NSString with the "%#" sequence.
The one problem I'm struggling with is that vsprintf seems to be behaving differently on OS X versus Linux (I've tested with Ubuntu 10.10 and 12.04). On OS X, it is behaving how I thought it should, where after calling vsprintf, calling va_arg returns the ms pointer (as if the vsprintf function called va_arg to get the 5). On Linux, however, the va_list does not change from vsprintf, and calling va_arg returns 5.
I would really like to figure out a way to implement this functionality so that it behaves consistently across platforms. Is it wrong to assume that you can expect vsprintf to consistently change the pointer inside va_list so that the next time you call va_arg it returns the next not-yet-used argument?
I have simplified my code as much as possible to demonstrates the issue. On OS X, this code prints the correct address of the pointer returned from malloc. On Linux, the value of ms in foo becomes 5, so it prints 5.
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
static void foo(void *, ...);
typedef struct {
char *value;
} mystruct;
int main(int argc, char *argv[]) {
mystruct *ms = malloc(sizeof(mystruct));
foo(NULL, "%d %#", 5, ms);
}
void foo(void *dummy, ...) {
va_list args;
va_start(args, dummy);
char buffer[512];
int buffer_ptr = 0;
int i = 0;
char *format = va_arg(args, char *);
buffer[0] = '\0';
for (i = 0; i < strlen(format); i++) {
if (i <= strlen(format) - 1 && (format[i] == '%' && format[i+1] == '#')) {
vsprintf(buffer, buffer, args);
/* can expect the next argument to be a mystruct pointer */
mystruct *ms = va_arg(args, mystruct *);
buffer[buffer_ptr+1] = '\0';
fprintf(stderr, "%p", ms); /* SHOULD NOT PRINT 5 */
/* concatenate here */
} else {
buffer[buffer_ptr++] = format[i];
buffer[buffer_ptr] = '\0';
}
}
va_end(args);
}
You need to use va_copy if you're going to use an argument list more than once -- failure to do so is undefined behavior. Your code should look something like this:
va_list args;
va_start(args, dummy);
...
char *format = va_arg(args, char *);
...
va_list argsCopy;
va_copy(argsCopy, args);
vsprintf(..., argsCopy);
va_end(argsCopy);
...
mystruct *ms = va_arg(args, mystruct *);
...
va_end(args);
The problem is that it's up to the implementation how to implement a va_list -- it might contain all the info and state for extracting arguments directly, or it might contain a pointer to something that holds the state indirectly. So passing it to vsprintf might make a copy of all the relevant state or it might not.
What you want for what you are trying to do is a vspintf-like function that takes a va_list * rather than a va_list, so you can ensure you have the proper state after it returns. Unfortunately, the standard does not provide any such function.
Related
If the format string passed to vsprintf() (and variants thereof) contains no %-references, is it guaranteed that the va_list argument is not accessed?
Put another way, is:
#include <stdarg.h>
#include <stdio.h>
int main ( void ) {
char str[16];
va_list ap; /* never initialized */
(void)vsnprintf(str, sizeof(str), "", ap);
return 0;
}
a standard-conforming program? or is there undefined behavior there?
The example above is obviously silly, but imagine a function which can be called by both a variadic function and a fixed-args function, grossly simplified into something like:
void somefuncVA ( const char * fmt, va_list ap ) {
char str[16];
int n;
n = vsnprintf(str, sizeof(str), fmt, ap);
/* potentially do something with str */
}
void vfoo ( const char * fmt, ... ) {
va_list ap;
va_start(fmt, ap);
somefuncVA(fmt, ap);
}
void foo ( void ) {
va_list ap; /* no way to initialize this */
somefuncVA("", ap);
}
int vsprintf(char * restrict s, const char * restrict format, va_list arg);
If the format string passed to vsprintf() ... contains no %-references, is it guaranteed that the va_list argument is not accessed.
No.
The vsprintf function is equivalent to sprintf, with the variable argument list
replaced by arg, which shall have been initialized by the va_start macro .....
C11dr §7.21.6.13
Since the below code does not adhere to the spec, the result is undefined behavior (UB). No guarantees. #Eugene Sh.
va_list ap;
// vv-- ap not initialized
(void)vsnprintf(str, sizeof(str), "", ap);
Is vsprintf() guaranteed not to access va_list if format string makes no % references?
With a properly passed va_list arg, vsprintf() acts like sprintf(). Code like the following is OK. It is permissible to pass extra arguments. Via vsprintf(), they (the extra arguments) are not accessed, yet va_list arg may be accessed.
sprintf(buf, "format without percent", 1.2345, 456)`
If you don't have varargs passed to your function - your function isn't defined with ... as the last parameter - there's simply never any need for any use of va_list or va_start() in that function. If you want to pass an empty set of variable arguments, simply call the varargs function directly without any variable arguments - e.g., printf("\n");.
For example, instead of
void foo ( void ) {
va_list ap; /* no way to initialize this */
somefuncVA("", ap);
}
you can just write
void foo ( void ) {
vfoo("");
}
I'm trying to assign data type to world but unable to figure it out.
#include <stdarg.h>
#include <stdio.h>
#define TRACE(arg) TraceDebug arg ;\
void TraceDebug(const char* format, ...);
void TraceDebug(const char* format, ...)
{
char buffer[256];
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
int main(void)
{
int a =55;
TRACE((Hello,a));
return 0;
}
Below is the error statement in detail.
main.c: In function 'main':
main.c:28:12: error: 'Hello' undeclared (first use in this function)
TRACE((Hello,a));
^
main.c:13:32: note: in definition of macro 'TRACE'
#define TRACE(arg) TraceDebug arg ;\
^
main.c:28:12: note: each undeclared identifier is reported only once for each function it appears in
TRACE((Hello,a));
^
main.c:13:32: note: in definition of macro 'TRACE'
#define TRACE(arg) TraceDebug arg ;\
^
Is there anyway possible to declare Hello as a variable, after declaring I need to get the address of the variable.
In simple I want to change the below code into a variadic function arguments
for example #define QU(arg1,arg2) as #define QU(arg1,...) since variadic macro is not supported am using variadic functions.
#define TRACE(arg1) QU arg1
#define QU(arg1,arg2) {static const char arg1; \
printf("%p\n",(void*)&arg1);\
printf("%d\n",arg2);}\
int main(void)
{
int aaa =333;
int bbb =444;
TRACE((Hello,aaa));
TRACE((Hello2,bbb));
return 0;
}
1) (title) How to declare the data type for variable arguments?
2) (1st question) I'm trying to assign data type to world but unable to figure it out.
1) The data type for the variadic argument (represented by the ellipses: ... ) is always the type of the variable preceding the ellipses . For this prototype:
int variadicFunc(int a, const char *b, ...);
^^^^^^^^^^ ^^^
type assumes the type const char *
2) From content of your question only, the answer could be to be use a typedef statement:
typedef char World; // a new type 'World' is created
But there are clarifications in the comments:
if i change the string to variable i can reduce the memory size,... (you)
You want to have a variable argument list to pass variables existing in your program that you want to place on a Trace list for debugging
purposes. (is that close?)... (me)
(is that close?) yes, that's the thing am trying to do... Are you always going to pass the same type to this function? Ahh, type will
be like TRACE(("Hello", a,"world")); (you)
It appears you want to enter a variable number of either string literals, or string variables as function arguments, then for those items to be placed into variables, then the addresses of those variables to be stored in a file, for the purpose of saving space.
The following code illustrates how you can pass a variable number of strings (in different forms) into a function, and have the address and content retained into a struct. From this, you should be able to adapt from what I have done here, to something more useful to your needs. Note, I have reserved the first string argument to be used a file location to store addresses.
#define MAX_LEN 200
typedef struct {
unsigned int addr;
char str[MAX_LEN];
} DATA;
int variadicFunc(int argCount, const char *str, ...);
int main(void)
{
char a[] = {"this is a string"};
char b[] = {"another string"};
char c[] = {"yet another string"};
// count non-variable v1 v2 v3 v4
variadicFunc(4, ".\\storage.txt", a, b, "var string", c);
// ^count of variable argument list
return 0;
}
int variadicFunc(int argCount, const char *str, ...)
{
va_list arg;
int i;
char sAddr[10];
DATA *d = calloc(argCount, sizeof(*d));
va_start(arg, str);
FILE *fp = fopen(str, "w");//using first string as filename to populate
if(fp)
{
for(i=0;i<argCount;i++)
{
// retain addresses and content for each string
strcpy(d[i].str, va_arg(arg, const char *));
d[i].addr = (unsigned int)&d[i].str[i];
sprintf(sAddr, "%X\n", d[i].addr);
fputs(sAddr, fp);
}
fclose(fp);
}
return 0;
}
I have the following code:
int __dmasprintf (char **s, const char *format, ...) {
char buf[512];
va_list arg;
int ret;
va_start(arg,format);
ret = vsprintf(buf, format, arg);
va_end(arg);
*s = strdup(buf);
if (*s == NULL) return -1;
return 0;
}
I want to add an argument to the va_list arg before calling vsprintf() because my format contains 1 extra argument at the end.
How to add an argument (for example char * myarg) to the the va_list arg?
Or is it possible to pass vsprintf() a customized list?
You can't.
You either need to make two vsprintf (surely vsnprintf?) calls, or replace your function with a variadic macro, like
#define __dmasprintf(S, FMT, ...) ( \
(*S = do_dmasprintf(FMT, __VA_ARGS__, my_arg)) == NULL ? -1 : 0)
char *do__dmasprintf (const char *format, ...) {
char buf[512];
va_list arg;
int ret;
va_start(arg,format);
ret = vsnprintf(buf, sizeof(buf), format, arg);
va_end(arg);
char *s = strdup(buf);
return s;
}
Notes:
I replaced vsprintf with vsnprintf. There's no reason to use the former here (or pretty much anywhere else)
you ignore ret. Should you?
I kept the macro argument layout similar to the original, but since __VA_ARGS__ must be one-or-more arguments (it can't be empty), that means at least one argument is required after FMT. Just remove the FMT argument entirely if you want to allow zero arguments after it.
There is unfortunately no direct way to do that. There is a reason for that : the stdarg macros take the address in the stack of the last known parameter, and then directly iterate the stack.
If you can use macros, #Useless provided a nice solution - beware, macros can have side effects when you pass variables pre- or post-fixed with ++ or --.
If you want to avoid macros, you will have to write your own variant of vsprintf. No problem for it, just find a source for C stdlib (GNU libc could be a nice start point) and be brave ... hope you can use macros !
I would like to design a function which takes a variable number of arguments, one of these arguments being itself a va_list; but something gets wrong in my code and I do not understand what...
WARNING — My question is not about designing a code doing what I wish to do (I have found a way to bypass the problem), but only about understanding what I did wrong...
To explain my question, let us start with a simple example: namely, a function ffprintf which acts like fprintf, but writing its contents onto several strings, strings whose number is indicated by the first argument of ffprintf, and whose identities are given by the very next arguments (the number of these arguments may vary from one call to another, so you have to use a variable argument list). Such a function would be used like this:
FILE *stream0, *stream1, *stream2;
int a, b;
ffprintf (3, stream0, stream1, stream2, "%d divided by %d worths %f", a, b, (double)a / b);
And its code would be:
void ffprintf (int z, ...)
{va_list vlist, auxvlist;
FILE **streams = malloc (z * sizeof(FILE *));
va_start (vlist, z);
for (int i = 0; i < z; ++i)
{streams[i] = va_arg (vlist, FILE *); // Getting the next stream argument
}
char const *format = va_arg (vlist, char const *); // Getting the format argument
for (int i = 0; i < z; ++i)
{va_copy (auxvlist, vlist); // You have to work on a copy "auxvlist" of "vlist", for otherwise "vlist" would be altered by the next line
vfprintf (streams[i], format, auxvlist);
va_end (auxvlist);
}
va_end (vlist);
free (streams);
}
That works fine. Now, there is also the standard function vfprintf, whose prototype is vfprintf (FILE *stream, char const* format, va_list vlist);, and which you use like this to create another function having a variable argument list:
void fprintf_variant (FILE *stream, char const* format, ...)
{
va_list vlist;
va_start (vlist, format);
vfprintf (stream, format, vlist);
va_end (vlist);
}
That works fine too. Now, my goal is to combine both ideas to create a function which I would call vffprintf, which you would use like this:
FILE *stream0, *stream1, *stream2;
void fprintf_onto_streams012 (char const *format, ...)
{va_list vlist;
va_start (vlist, format);
vffprintf (3, stream0, stream1, stream2, format, vlist);
va_end (vlist);
}
I have designed the following code:
void vffprintf (int z, ...)
{va_list vlist, auxvlist, auxauxvlist;
va_start (vlist, z);
FILE **streams = malloc (z * sizeof(FILE *));
for (int i = 0; i < z; ++i)
{streams[i] = va_arg (vlist, FILE *);
}
char const *format = va_arg (vlist, char const *);
va_copy (auxvlist, va_arg (vlist, va_list)); // Here I get the next argument of "vlist", knowing that this argument is of "va_list" type
for (int i = 0; i < z; ++i)
{va_copy (auxauxvlist, auxvlist);
vfprintf (streams[i], format, auxvlist);
va_end (auxauxvlist);
}
va_end (auxvlist);
va_end (vlist);
free (streams);
}
This code compiles without a hitch, but it does not work properly... For instance, if I write the following complete code:
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
void vffprintf (int z, ...)
{va_list vlist, auxvlist, auxauxvlist;
FILE **streams = malloc (z * sizeof(FILE *));
va_start (vlist, z);
for (int i = 0; i < z; ++i)
{streams[i] = va_arg (vlist, FILE *);
}
char const *format = va_arg (vlist, char const *);
va_copy (auxvlist, va_arg (vlist, va_list));
for (int i = 0; i < z; ++i)
{va_copy (auxauxvlist, auxvlist);
vfprintf (streams[i], format, auxauxvlist);
va_end (auxauxvlist);
}
va_end (auxvlist);
va_end (vlist);
free (streams);
}
void printf_variant (char const *format, ...)
{va_list vlist;
va_start (vlist, format);
vffprintf (1, stdout, format, vlist);
va_end (vlist);
}
int main (void)
{printf_variant ("Ramanujan's number is %d.\n", 1729);
return 0;
}
I get a segfault!... Why?!
P.-S.: Sorry for that very long question; but I wanted it to be perfectly clear, for it is rather technical...
P.-S.2: I used deliberately both tags "va-list" and "variableargumentlists" for this question, because which interests me is va_list, seen as a type, inside a (other) variable argument list, seen as a list... So these are really two different concepts here.
The description of va_arg in the final draft of C11 (N1570) contains (type is the second argument):
if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined
va_list is allowed to be an array type (the standard requires it to be a so-called “complete object type”) and it seems your implementation makes use of this possibility. You probably know that in C arrays can't be passed as arguments as they decay into pointers, and the type of such a pointer isn't compatible with the original array type.
For example: int * isn't compatible with int [1]. So if you really need to pass an array or a va_list portably, then define a struct with a va_list member and pass that (see Why can't we pass arrays to function by value?).
void vffprintf (int z, ...)
{
//...
va_copy (auxvlist, va_arg (vlist, va_list));//this line has the problem
//...
}
Just a quick and tricky way like this, it will work.
void vffprintf (int z, ...)
{
//...
va_copy (auxvlist, va_arg (vlist, void*));
//...
}
Here are some references about var_arg and va_list, which should have provided detailed and thorough explanation.
1) Pass va_list or pointer to va_list?
2) Is GCC mishandling a pointer to a va_list passed to a function?
3) What is the format of the x86_64 va_list structure?
Hope they are helpful.
You might need to wrap the type va_list into a struct, if you want to retrieve it using va_arg():
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
typedef struct
{
va_list list ;
} my_list ;
void vffprintf (int z, ...)
{my_list vlist, auxvlist, auxauxvlist;
FILE **streams = malloc (z * sizeof(FILE *));
va_start (vlist.list, z);
for (int i = 0; i < z; ++i)
{streams[i] = va_arg (vlist.list, FILE *);
}
char const *format = va_arg (vlist.list, char const *);
my_list parent = va_arg (vlist.list, my_list) ;
va_copy (auxvlist.list, parent.list );
for (int i = 0; i < z; ++i)
{va_copy (auxauxvlist.list, auxvlist.list);
vfprintf (streams[i], format, auxauxvlist.list);
va_end (auxauxvlist.list);
}
va_end (auxvlist.list);
va_end (vlist.list);
free (streams);
}
void printf_variant (char const *format, ...)
{my_list vlist;
va_start (vlist.list, format);
vffprintf (1, stdout , format, vlist);
va_end (vlist.list);
}
int main (void)
{printf_variant ("Ramanujan's number is %d.\n", 1729 );
return 0;
}
The problem stems from the fact that array and a pointer of the same type are not compatible, and va_list is defined as an array. Then you try to get that type:
va_arg (vlist, va_list)
So you tell va_arg you are getting an array but if fact the passed va_list has decayed to a pointer. You should use the pointer version of va_list, but you don't know the real definition of va_list, in the first place so you cannot obtain the pointer version of it.
The solution is to wrap va_list into a type you control, a struct.
When learning C I see that printf can receive many arguments as it is passed.
And I don't know how C can implement a function like this, where the user can type as many parameters as the user wants. I have thought about pointers, too but still have no bright idea. If anyone has any ideas about this type of function, please tell me.
You need to use va_args, va_list and the like.
Have a look at this tutorial.
http://www.cprogramming.com/tutorial/c/lesson17.html
That should be helpful.
You have to use the ... notation in your function declaration as the last argument.
Please see this tutorial to learn more: http://www.cprogramming.com/tutorial/c/lesson17.html
#include <stdarg.h>
#include <stdio.h>
int add_all(int num,...)
{
va_list args;
int sum = 0;
va_start(args,num);
int x = 0;
for(x = 0; x < num;x++)
sum += va_arg(args,int);
va_end(args);
return sum;
}
int main()
{
printf("Added 2 + 5 + 3: %d\n",add_all(3,2,5,3));
}
You use C varargs to write a variadic function. You'll need to include stdargs.h, which gives you macros for iterating over an argument list of unknown size: va_start, va_arg, and va_end, using a datatype: va_list.
Here's a mostly useless function that prints out it's variable length argument list:
void printArgs(const char *arg1, ...)
{
va_list args;
char *str;
if (arg1) We
va_start(args, arg1);
printf("%s ", arg1);
while ((str = va_arg(argp, char *)) != NULL)
printf("%s ", str);
va_end(args);
}
}
...
printArgs("print", "any", "number", "of", "arguments");
Here's a more interesting example that demonstrates that you can iterate over the argument list more than once.
Note that there are type safety issues using this feature; the wiki article addresses some of this.