I am receiving a string (e.g: "3 10 ABC") from a function and want to pass it to another function like function(3,10,ABC). I tried to capture it in an array. but there may be variable number of arguments so is there any way to do this using va_list.
转载于:https://stackoverflow.com/questions/53112103/how-to-pass-space-seprated-substring-of-string-as-aguments-of-a-function-in-c
va_list is an interface to the underlying variable number of arguments implementation, and isn’t a method of constructing a variable length array. Although intriguing, part of va_* is doing things like spilling register based calling conventions into a traditional array, which makes it too cumbersome to rely upon for what you want.
Constructing an array of the sort you are interested in isn’t difficult. Your question indicates you want mixed types; that implies that each member has to be “fat”:
struct Element {
enum { Int, String, Other } Type;
union {
long val;
char *str;
void *anon;
};
};
Then you just want to (re)allocate an array of them until your parsing is done. Presuming your parsing is space delimited, you can use a traditional:
for (s = strtok(str, “ \t”); s; s = strtok(NULL, “ \t”)) {
struct Element *e = Extend(&EList);
if (isdigit(*s)) {
e->type = Int;
e->val = strtol(s, NULL, 0);
} else if (isalpha(*s)) {
e->type = String;
e->str = strdup(s);
} else {
e->type = Other;
e->anon = strdup(s);
}
}
...
And finally, you want a way to construct the list:
struct ElementList {
struct Element *list;
int nalloc;
int nused;
};
struct Element *Extend(struct ElementList *el) {
if (el->nused == el->nalloc) {
int nsize = el->nalloc ? el->nalloc * 3 / 2 : 10 ;
struct Element *t = realloc(el->list, sizeof(*t) * nsize);
if (t == NULL) {
return NULL;
}
el->nalloc = nsize;
el->list = t;
}
return el->list + el->nused++;
}
#define ELIST_INITIALIZER { .list = NULL, .nalloc = 0, .nused = 0 }
ps/disclaimer: I wrote this in this editor, it likely has small problems, but should illustrate what you want to do.
The short answer is No. va_list
requires you to determine the number of arguments on your own. So if you wanted to call:
function(3, "3", "10", "ABC");
Where the first argument is the count, that's one way to do it. Functions like printf()
et.al. get around this by counting the number of %
in the format string. But we could do something like look for an ending-tag of NULL
:
function("3", "10", "ABC", NULL);
Giving the code:
void function(const char *first, ...)
{
va_list ap;
const char *arg = first;
va_start(ap, first);
while (arg != NULL)
{
/* do whatever with <arg> */
printf("Argument: [%s]\n", arg);
/* get the next argument */
arg = va_arg(ap, char*);
}
va_end(ap);
}
EDIT: Brute-force, but incomplete approach (handles up to 20 arguments), based upon idea found in the comment, and elsewhere.
#define TWENTY2TH_ARGUMENT(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, ...) arg22
#define COUNT_ARGUMENTS(...) TWENTY2TH_ARGUMENT(dummy, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
void _printArgs2(size_t count, ...)
{
va_list ap;
const char *arg;
va_start(ap, count);
for (size_t i=0; i<count; i++)
{
/* get the next argument */
arg = va_arg(ap, char*);
/* do whatever with <arg> */
printf("Argument: [%s]\n", arg);
}
va_end(ap);
}
#define printArgs2(...) _printArgs2(COUNT_ARGUMENTS(__VA_ARGS__), ##__VA_ARGS__)
int main(void)
{
printArgs2("One");
printf("\n");
printArgs2("One", "Two");
printf("\n");
printArgs2("One", "Two", "Three");
printf("\n");
}