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

Live Demo on coliru

备注:

  1. 一个困难是正确识别可变参数的数量。我为此使用了计数。另一个经常使用的选项是注释结尾(例如使用 nullptr 参数)。关于 printf(),预期参数的数量直接取决于格式化参数的内容。 (因此,printf() 被认为是脆弱和不安全的。)

  2. 参数只有特定类型。更多相关信息: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);
}