使用可变数量的说明符创建格式字符串

Creating a format string with a variable number of specifiers

我正在尝试使用 va_list 及其关联的宏和 vsprintf() 来创建具有可变数量的说明符的格式字符串。这是我编写的示例程序,其中只能通过 NUM_ARG 宏更改说明符的数量:

#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] = '[=10=]' };
    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;
}

然而,这并不是我想要实现的。我怎么能用可变数量的参数来做到这一点?如何将可变数量的字符串传递给函数并用于初始化 va_list?

简短的回答是:你不能。

不过,您可以使用可能动态分配的字符串数组来解决这个问题。然后您基本上可以使用与现在相同的技术,但改为遍历数组。


也许是这样的:

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);
}

对于 argv 中的命令行参数,您实际上不需要创建一个新数组来保存它们,但它展示了如何创建一个字符串数组以传递给 strmaker .您可以使用任何您想要的字符串来代替命令行参数。

据我所知,这是不可能的。如果您不太热衷于使用可变参数函数并且可以重新定义该函数。以下代码适合您的需要;遍历数组中的每个项目并使用 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;
}

终端会话:

$ ./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 Whosebug, Bye 
(Hello,)(This)(is)(Whosebug,)(Bye)