0

I am trying to use va_list & its associated macros with vsprintf() to create a format string that has a variable number of specifiers. Here is an example program I wrote in which the number of specifiers can only be altered via the NUM_ARG macro:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#define MAXBUF 4096
#define SPECIFIER "(%s)"
#define NUM_ARG 5
char *strmaker(int num_args, ...)
{
 char form[MAXBUF] = { [0] = '0円' };
 char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
 va_list strings;
 
 for (int i = 0; i < num_args; ++i)
 strcat(form, SPECIFIER);
 
 va_start(strings, num_args); 
 vsprintf(prnt, form, strings);
 va_end(strings);
 return prnt;
}
int main(int argc, char *argv[])
{
 if (argc != (NUM_ARG + 1))
 return -1;
 char *s = strmaker(NUM_ARG, argv[1], argv[2], argv[3], argv[4], argv[5]); 
 printf("%s\n", s);
 free(s);
 return 0;
}

However, this isn't exactly what I want to achieve. How could I do this with a variable number of arguments? How could a variable number of strings be passed to a function and used to initialise a va_list?

asked Aug 2, 2020 at 15:05

2 Answers 2

1

As far as I know, it is not possible to do that. If you are not so keen about using variadic functions and can redefine the function. The below code suits your need; Iterate through each item in the array and append to the string using snprintf.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXBUF 4096
#define SPECIFIER "(%s)"
char *strmaker(int num_args, char** strings)
{
 char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
 int cur = 0;
 /* Append the strings to the prnt buffer */
 for (int i = 0; i < num_args; i++) {
 int p_return = snprintf(prnt + cur, MAXBUF - cur, SPECIFIER, strings[i]); // If no error, return the number characters printed excluding nul (man page)
 if (p_return >= MAXBUF - cur) // If buffer overflows (man page)
 return prnt;
 cur = cur + p_return; // Update the index location.
 }
 return prnt;
}
int main(int argc, char *argv[])
{
 if (argc <= 1)
 return -1;
 char *s = strmaker(argc - 1, argv + 1);
 printf("%s\n", s);
 free(s);
 return 0;
}

Terminal Session:

$ ./a.out 1 2 3 
(1)(2)(3)
$ ./a.out 1 2 3 4 5 6 7
(1)(2)(3)(4)(5)(6)(7)
$ ./a.out Hello, This is stackoverflow, Bye 
(Hello,)(This)(is)(stackoverflow,)(Bye)
answered Aug 2, 2020 at 15:45
Sign up to request clarification or add additional context in comments.

2 Comments

Hmmm, could this work with a format string that has already been created? I.e. imagine a format string such as "he(%s)llo(%s)" (contains specifiers & text). I have attached a variable "count" to keep track of the specifiers. How would I then print the correct amount of strings to the format string using one of the printf functions?
@GabrielSaul Your question is already answered. If you have a new question, create a new question instead of updating the constraints in a comment like this. Thank you.
1

Short answer is: You can't.

However you can work around it by using arrays of strings, possibly dynamically allocated. Then you could basically use the same technique you do now, but iterate over the array instead.


Perhaps something like this:

char *strmaker(size_t count, char *strings[])
{
 // First get the length of all strings in the array
 size_t result_length = 0;
 for (size_t i = 0; i < count; ++i)
 {
 // +1 for space between the strings
 // And for the last string adds space for the string null-terminator
 result_length += strlen(strings[i]) + 1;
 }
 // Now allocate the string (using calloc to initialize memory to zero, same as the string null-terminator)
 char *result = calloc(1, result_length);
 // And not concatenate all strings in the array into one large string
 for (size_t i = 0; i < count; ++i)
 {
 strcat(result, strings[i]);
 if (i != count - 1)
 {
 strcat(result, " "); // Add space, except after last string
 }
 }
 // Return the resulting string
 return string;
}
int main(int argc, char *argv[])
{
 // Create an array for all arguments
 char **arguments = malloc(sizeof(char *) * argc - 1);
 for (int a = 1; a < argc)
 {
 arguments[a - 1] = argv[a];
 }
 // Now create the single string
 char *result = strmaker(argc - 1, arguments);
 // ... and print it
 printf("%s\n", result);
 // Finally clean up after us
 free(result);
 free(arguments);
}

For the command-line arguments in argv you don't really need to create a new array to hold them, but it showcases how to create an array of string to pass to strmaker. You can use any strings you want instead of the command-line arguments.

answered Aug 2, 2020 at 15:23

2 Comments

So, have an array of strings, iterate over it and call the function for each iteration? If I am therefore adding one string at a time to the format string, would the format string & the printed string have to be statically allocated?
@GabrielSaul Not quite what I meant. Added example to answer.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.