char 数组的可变参数
Variadic arguments on char arrays
我想调用一个函数来建立一个类似这样的菜单:
Mode_Menu("Item 1", "Item A1c", "Item Fred", ..... "Item xxx")
其中 n
可以是任何合理的数字,每个项目的长度可以是随机的。
我尝试了以下方法(忽略 Offer_Mode
中的数字 - 那只是 LCD 上的 Y
坐标)
void Mode_Menu(char *m1, ...)
{
va_list argp;
va_start(argp, *m1);
Offer_Mode(2, m1++);
Offer_Mode(33, m1++);
Offer_Mode(64, *m1++;
Offer_Mode(97, *m1++);
Offer_Mode(130, *m1++);
}
但我得到的是
Item 1
tem 1
em 1
m 1
1
即指针沿着第一个元素移动,在函数调用中甚至看不到第二个元素。
我已经尝试了所有我能想到的咒语,在函数定义和 va_start
中使用 *m1[]
或 m1[2]
之类的东西来逐步执行以下元素,但一切我尝试抛出一个错误。
有人能告诉我正确的方法吗?
(我首先进行了广泛的搜索,所以请不要标记为重复。有很多使用整数的示例,但是 none 我可以找到使用字符数组的示例)。
vardiac 模板的 C 接口使得它:
void Mode_Menu(char *arg, ...)
{
va_list argp;
va_start(argp, &arg);
Offer_Mode(2, arg);
arg=va_arg(argp, char*)
Offer_Mode(33, arg);
arg=va_arg(argp, char*)
Offer_Mode(64, arg);
....
va_end(argp);
}
编辑:实际代码应该有办法找出参数的结尾。它可以是整数第一个参数,来自字符串信息(如 printf 格式,或字符串中的模式匹配),或 nullptr 标记(最后一个参数)。
C++方式(使用SFINAE检查类型正确性)
template <typename ...T>
std::enable_if_t<(std::is_same_v<T, char>&&...)>
Mode_Menu(const T*... args)
{
const char * texts[] = { args... };
Offer_Mode(2, texts[0]);
Offer_Mode(33, texts[1]);
Offer_Mode(64, texts[2]);
....
}
编辑 2:模板变体在 sizeof...(T)
和 std::size(texts)
中已经包含尺寸信息。因此,与 C 变体不同,无需努力检测最后一个字符串。
我重复我在评论中已经说过的话:
在 C++ 中,我更喜欢可变参数模板,或者可能是在容器中提供参数(例如 std::vector)。如果那根本不是一个选项,那么我会使用 va_arg 来访问参数。并且,请不要忘记 va_end()。如果不小心使用,在 va_ 宏中处理可变参数可能非常依赖于平台并且很脆弱。
然而,(只是为了纪念我自己在过去试图模仿 printf()
)一个例子:
#include <iostream>
#include <cstdarg>
void printMenu(int len, ...)
{
va_list argp;
va_start(argp, len);
for (int i = 0; i < len; ++i) {
const char *item = va_arg(argp, const char*);
std::cout << '[' << (i + 1) << "]: " << item << '\n';
}
va_end(argp);
}
int main()
{
printMenu(3, "Item 1", "Item A1c", "Item Fred");
return 0;
}
输出:
[1]: Item 1
[2]: Item A1c
[3]: Item Fred
备注:
一个困难是正确识别可变参数的数量。我为此使用了计数。另一个经常使用的选项是注释结尾(例如使用 nullptr
参数)。关于 printf()
,预期参数的数量直接取决于格式化参数的内容。 (因此,printf()
被认为是脆弱和不安全的。)
参数只有特定类型。更多相关信息:Variadic arguments - Default conversions
您可以解决指定要传递的附加参数的数量问题。
每个附加参数都必须来自列表 "extracted"。
我用了cout
因为我不知道Offer_mode
是干什么的。
#include<iostream>
#include <stdarg.h>
void Mode_Menu(char *m1, unsigned int count,...);
int main(int argc, char* argv[])
{
Mode_Menu("Item 1", 3,"Item A1c", "Item Fred", "Item xxx");
return 0;
}
void Mode_Menu(char *m1, unsigned int count, ...)
{
va_list ap;
va_start(ap, count); /* Requires the last fixed parameter (to get the address) */
for (int j = 0; j < count; j++)
{
std::cout<<va_arg(ap, char*)<<std::endl; //ap automatically incremented
}
va_end(ap);
}
有了问题和答案,我得出了如下结果
// gcc -std=c99 vardiac.c -o vardiac -Wall
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
char * formatStr(char *dest,int narg, ...) {
*dest='[=10=]';
va_list pargs;
va_start(pargs, narg);
for(int i=0;i<narg;i++) {
char *str = va_arg(pargs,char *);
strcat(dest,str);
strcat(dest," ");
}
va_end(pargs);
printf("Inside function: %s\n",dest);
return dest;
}
int main() {
char *t_str;
int narg;
char first[] = "First";
char second[] = "Second";
char third[] = "Third";
t_str = (char *) malloc( ( strlen(first) + strlen(second) + strlen(third) + 1 ) * sizeof(char) );
narg = 3;
formatStr(t_str,narg,first,second,third);
printf("Inside main: %s\n",t_str);
narg = 2;
formatStr(t_str,narg,first,second);
printf("Inside main: %s\n",t_str);
narg = 1;
formatStr(t_str,narg,first);
printf("Inside main: %s\n",t_str);
free(t_str);
}
我想调用一个函数来建立一个类似这样的菜单:
Mode_Menu("Item 1", "Item A1c", "Item Fred", ..... "Item xxx")
其中 n
可以是任何合理的数字,每个项目的长度可以是随机的。
我尝试了以下方法(忽略 Offer_Mode
中的数字 - 那只是 LCD 上的 Y
坐标)
void Mode_Menu(char *m1, ...)
{
va_list argp;
va_start(argp, *m1);
Offer_Mode(2, m1++);
Offer_Mode(33, m1++);
Offer_Mode(64, *m1++;
Offer_Mode(97, *m1++);
Offer_Mode(130, *m1++);
}
但我得到的是
Item 1
tem 1
em 1
m 1
1
即指针沿着第一个元素移动,在函数调用中甚至看不到第二个元素。
我已经尝试了所有我能想到的咒语,在函数定义和 va_start
中使用 *m1[]
或 m1[2]
之类的东西来逐步执行以下元素,但一切我尝试抛出一个错误。
有人能告诉我正确的方法吗?
(我首先进行了广泛的搜索,所以请不要标记为重复。有很多使用整数的示例,但是 none 我可以找到使用字符数组的示例)。
vardiac 模板的 C 接口使得它:
void Mode_Menu(char *arg, ...)
{
va_list argp;
va_start(argp, &arg);
Offer_Mode(2, arg);
arg=va_arg(argp, char*)
Offer_Mode(33, arg);
arg=va_arg(argp, char*)
Offer_Mode(64, arg);
....
va_end(argp);
}
编辑:实际代码应该有办法找出参数的结尾。它可以是整数第一个参数,来自字符串信息(如 printf 格式,或字符串中的模式匹配),或 nullptr 标记(最后一个参数)。
C++方式(使用SFINAE检查类型正确性)
template <typename ...T>
std::enable_if_t<(std::is_same_v<T, char>&&...)>
Mode_Menu(const T*... args)
{
const char * texts[] = { args... };
Offer_Mode(2, texts[0]);
Offer_Mode(33, texts[1]);
Offer_Mode(64, texts[2]);
....
}
编辑 2:模板变体在 sizeof...(T)
和 std::size(texts)
中已经包含尺寸信息。因此,与 C 变体不同,无需努力检测最后一个字符串。
我重复我在评论中已经说过的话:
在 C++ 中,我更喜欢可变参数模板,或者可能是在容器中提供参数(例如 std::vector)。如果那根本不是一个选项,那么我会使用 va_arg 来访问参数。并且,请不要忘记 va_end()。如果不小心使用,在 va_ 宏中处理可变参数可能非常依赖于平台并且很脆弱。
然而,(只是为了纪念我自己在过去试图模仿 printf()
)一个例子:
#include <iostream>
#include <cstdarg>
void printMenu(int len, ...)
{
va_list argp;
va_start(argp, len);
for (int i = 0; i < len; ++i) {
const char *item = va_arg(argp, const char*);
std::cout << '[' << (i + 1) << "]: " << item << '\n';
}
va_end(argp);
}
int main()
{
printMenu(3, "Item 1", "Item A1c", "Item Fred");
return 0;
}
输出:
[1]: Item 1
[2]: Item A1c
[3]: Item Fred
备注:
一个困难是正确识别可变参数的数量。我为此使用了计数。另一个经常使用的选项是注释结尾(例如使用
nullptr
参数)。关于printf()
,预期参数的数量直接取决于格式化参数的内容。 (因此,printf()
被认为是脆弱和不安全的。)参数只有特定类型。更多相关信息:Variadic arguments - Default conversions
您可以解决指定要传递的附加参数的数量问题。
每个附加参数都必须来自列表 "extracted"。
我用了cout
因为我不知道Offer_mode
是干什么的。
#include<iostream>
#include <stdarg.h>
void Mode_Menu(char *m1, unsigned int count,...);
int main(int argc, char* argv[])
{
Mode_Menu("Item 1", 3,"Item A1c", "Item Fred", "Item xxx");
return 0;
}
void Mode_Menu(char *m1, unsigned int count, ...)
{
va_list ap;
va_start(ap, count); /* Requires the last fixed parameter (to get the address) */
for (int j = 0; j < count; j++)
{
std::cout<<va_arg(ap, char*)<<std::endl; //ap automatically incremented
}
va_end(ap);
}
有了问题和答案,我得出了如下结果
// gcc -std=c99 vardiac.c -o vardiac -Wall
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
char * formatStr(char *dest,int narg, ...) {
*dest='[=10=]';
va_list pargs;
va_start(pargs, narg);
for(int i=0;i<narg;i++) {
char *str = va_arg(pargs,char *);
strcat(dest,str);
strcat(dest," ");
}
va_end(pargs);
printf("Inside function: %s\n",dest);
return dest;
}
int main() {
char *t_str;
int narg;
char first[] = "First";
char second[] = "Second";
char third[] = "Third";
t_str = (char *) malloc( ( strlen(first) + strlen(second) + strlen(third) + 1 ) * sizeof(char) );
narg = 3;
formatStr(t_str,narg,first,second,third);
printf("Inside main: %s\n",t_str);
narg = 2;
formatStr(t_str,narg,first,second);
printf("Inside main: %s\n",t_str);
narg = 1;
formatStr(t_str,narg,first);
printf("Inside main: %s\n",t_str);
free(t_str);
}