使用可变数量的说明符创建格式字符串
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)
我正在尝试使用 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)