【问题标题】:Variadic C function printing multiple 2-D char arrays打印多个二维字符数组的可变 C 函数
【发布时间】:2017-12-01 09:54:02
【问题描述】:

我需要在 C 中设置一个可变参数函数,它可以并排打印可变数量的二维字符数组。我很难弄清楚如何用va_arg() 初始化boards 变量。

有问题的关键行是:boards[i] = va_arg(ap, char*[][BOARDSIZE]); 该行产生编译器错误(当前为Second argument to 'va_arg' is of incomplete type 'char *[][10]'),但基本上我确定我没有做对。我只是不确定那是什么东西。我尝试了几种变体均无济于事。其余代码应该没问题。

(提前感谢您的帮助。)

#include <stdio.h>
#include <stdarg.h>

#define BOARDSIZE 10

void showBoardVariadic(int numArgs, ...) {

    va_list ap;
    va_start(ap, numArgs);

    // Assign an array of 2-D char arrays.
    char *boards[numArgs][BOARDSIZE][BOARDSIZE];
    for (int i = 0; i < numArgs; i++)
        boards[i] = va_arg(ap, char*[][BOARDSIZE]); // TODO: Fix this line

    // Print the 2-D arrays side-by-side
    for (int row = 0; row < BOARDSIZE; row++) {
        for (int i = 0; i < numArgs; i++) {
            for (int column = 0; column < BOARDSIZE; column++) {
                printf(" %c", *boards[i][row][column]);
            }
            printf("\t");
        }
        printf("\n");
    }

    va_end(ap);
}

int main() {

    char *playerBoard[BOARDSIZE][BOARDSIZE];
    char *opponentBoard[BOARDSIZE][BOARDSIZE];

    // Initialize playerBoard and opponentBoard to all tildes.
    for (int row = 0; row < BOARDSIZE; row++) {
        for (int column = 0; column < BOARDSIZE; column++) {
            playerBoard[row][column] = "~";
            opponentBoard[row][column] = "~";
        }
    }

    showBoardVariadic(2, playerBoard, opponentBoard);

    return 0;
}

【问题讨论】:

  • 您没有说明问题所在。编译器错误消息?崩溃?来自 va_arg 的值错误?
  • 你不能将数组作为参数传递——你总是传递指针。如果您将数组记为函数参数,则编译器会将其读取为指针。考虑到这一点,我对va_arg(ap, char*[][BOARDSIZE]) 感到难过。我会尝试改为读取指针并将其转换为数组类型。 (不,实际上我会尝试找到另一种解决方案。var args 很吸引人,但与多维数组结合使用......)
  • 嗯...char *boards[numArgs][BOARDSIZE][BOARDSIZE] 是一个由char* 组成的三维数组。根据你的评论,你想要一个指向二维数组的指针数组......(我讨厌这个多维数组垃圾。它让你很想掌握它,但实际上它没有生产价值。单一的暗淡。数组和明确的“多-dim indexing”简单、健壮且更易于维护。)
  • 还有一个问题:当您只打印printf(" %c", *boards[i][row][column]); 中板的第一个字符时,这让我假设每个单元格始终只有一个字符。在这种情况下,我会放弃 char* 并改用 char

标签: c arrays 2d variadic


【解决方案1】:

va_arg 的 C 规范要求“参数 type 应是指定的类型名称,以便可以通过简单地通过后缀来获得具有指定类型的对象的指针的类型*type。”字符串char*[][BOARDSIZE] 不满足这一点。您应该使用 typedef 为类型命名。

另外,在函数声明的参数列表中,char*[][BOARDSIZE] 会自动调整为char*(*}[BOARDSIZE]。在va_arg(或typedef)中,它不是。您应该使用调整后的表格。

因此,您应该为一个类型定义一个名称,该名称是指向BOARDSIZE 指向char 的数组的指针:

typedef char *(*MyType)[BOARDSIZE];

您应该将boards 更改为这些数组而不是数组数组:

MyType boards[numArgs];

您应该更改va_arg 以使用新类型:

boards[i] = va_arg(ap, MyType);

还请注意,您将板的每个元素都设置为字符串“~”。这会将它们全部设置为指向字符串文字,这可能不是您想要的。您不允许修改此字符串文字中的字符,因此更改板包含的内容的唯一方法是将它们更改为指向不同的字符串。

如果每个棋盘元素都是一个字符,您应该使用char 而不是char *。如果它们将是一个固定的或少量的多个字符,您可能需要char 的数组,而不是指向char 的指针。如果它们将是相当数量的多个字符,您可能希望使用char *,但为每个板元素分配空间。

【讨论】:

    【解决方案2】:

    其实我和 Eric 得出的结论是一样的,只是我什至没有考虑仅仅通过typedefs 来解决多维数组问题。

    出于好奇,我尝试写下 OP 的工作版本。当我终于得到一个时,我想展示我的代码(除了Eric Postpischils 答案)。

    所以,经过一番摆弄,我得到了这个工作版本testVarArgMDimArray.c

    #include <stdio.h>
    #include <stdarg.h>
    
    #define BOARDSIZE 10
    #define NCOLS BOARDSIZE
    #define NROWS BOARDSIZE
    
    typedef char Board[NROWS][NCOLS];
    typedef char (*PBoard)[NCOLS];
    
    void showBoardVariadic(int nArgs, ...)
    {
      va_list ap;
      va_start(ap, nArgs);
      /* Attention! VLAs are an optional feature of C11. */
      PBoard boards[nArgs];
      for (int i = 0; i < nArgs; ++i) {
        boards[i] = va_arg(ap, PBoard);
      }
      /* print the 2D arrays side-by-side */
      for (int row = 0; row < NROWS; ++row) {
        for (int i = 0; i < nArgs; ++i) {
          if (i) putchar('\t');
          for (int col = 0; col < NCOLS; ++col) {
            printf(" %c", boards[i][row][col]);
          }
        }
        putchar('\n');
      }
      va_end(ap);
    }
    
    int main()
    {
      Board playerBoard;
      Board opponentBoard;
      /* initialize boards */
      for (int row = 0; row < NROWS; ++row) {
    #ifdef CHECK /* for checking */
        /* insert some pattern in col 0 for checking */
        playerBoard[row][0] = 'a' + row;
        opponentBoard[row][0] = 'A' + row;
        for (int col = 1; col < NCOLS; ++col) {
          playerBoard[row][col] = opponentBoard[row][col] = '~';
        }
    #else /* productive code */
        for (int col = 0; col < NCOlS; ++col) {
          playerBoard[row][col] = opponentBoard[row][col] = '~';
        }
    #endif /* 1 */
      }
      showBoardVariadic(2, playerBoard, opponentBoard);
      /* done */
      return 0;
    }
    

    在我让它工作之前,我尝试在 VS2013 中解决一些问题。太伤心了——VS2013 不支持VLAs。因此,我必须记住它,但我让它运行起来了。

    正如我的一个 cmets 中已经推荐的那样,我选择 char 作为板元素而不是 char*。我也可以为 char* 解决它,但我觉得 char* 可能是 OP 中的“意外”选择。

    按照 Eric 的思路,我为 2D board 数组做了一个类型:

    typedef char Board[NROWS][NCOLS];
    

    更重要的可能是第二个:

    typedef char (*PBoard)[NCOLS];
    

    请记住,函数参数中的数组总是编译为指针。 C 从不将数组作为参数传递。因此,如果我们使用Board 类型的参数调用函数,我们将收到PBoard 类型的参数。

    请注意*PBoard 周围的括号——这表明PBoard 是一个指向数组的指针。如果你删除它们,你会得到一个指针数组——很大的不同,而不是预期的。

    掌握了这一点,showBoardVariadic() 的事情就变得相当简单了。

    板子数组声明为:

    PBoard boards[nArgs];
    

    va_arg 的赋值很简单:

    boards[i] = va_arg(ap, PBoard);
    

    对板子的访问很简单:

    printf(" %c", boards[i][row][col]);
    

    这可能令人惊讶,但在这种情况下,指向数组的指针的行为类似于数组数组。它只是有不同的类型。 (例如,您不应将sizeofPBoard 一起使用,因为在这种情况下不同的类型会生效。)

    由于在这种开发状态下每个板块元素都包含相同的内容,我担心板块索引中的问题是否会被忽视。因此,我实现了另一种初始化,其中列的每个第一个元素都有另一个字符:playerBoard'a' + rowopponentBoard'A' + row。通过定义宏CHECK 激活该测试分配。在我的测试会话中,我使用-D CHECK 编译了一次,没有编译一次。

    顺便说一句。如果您想知道为什么我介绍了NROWSNCOLS:在写这个答案时,我意识到如果我不小心翻转了某处的行和列,因为它们在 OP 中具有相同的大小,所以我不会注意到。因此,我将事物分开并使用NROWSNCOLS 进行测试。呼——它仍然可以正常工作。

    最后但同样重要的是,我在 Cygwin 中的示例会话(就像我在 Windows 10 上一样):

    $ gcc --version
    gcc (GCC) 6.4.0
    
    $ gcc -std=c11 -D CHECK -o testVarArgMDimArray testVarArgMDimArray.c 
    
    $ ./testVarArgMDimArray
     a ~ ~ ~ ~ ~ ~ ~ ~ ~     A ~ ~ ~ ~ ~ ~ ~ ~ ~
     b ~ ~ ~ ~ ~ ~ ~ ~ ~     B ~ ~ ~ ~ ~ ~ ~ ~ ~
     c ~ ~ ~ ~ ~ ~ ~ ~ ~     C ~ ~ ~ ~ ~ ~ ~ ~ ~
     d ~ ~ ~ ~ ~ ~ ~ ~ ~     D ~ ~ ~ ~ ~ ~ ~ ~ ~
     e ~ ~ ~ ~ ~ ~ ~ ~ ~     E ~ ~ ~ ~ ~ ~ ~ ~ ~
     f ~ ~ ~ ~ ~ ~ ~ ~ ~     F ~ ~ ~ ~ ~ ~ ~ ~ ~
     g ~ ~ ~ ~ ~ ~ ~ ~ ~     G ~ ~ ~ ~ ~ ~ ~ ~ ~
     h ~ ~ ~ ~ ~ ~ ~ ~ ~     H ~ ~ ~ ~ ~ ~ ~ ~ ~
     i ~ ~ ~ ~ ~ ~ ~ ~ ~     I ~ ~ ~ ~ ~ ~ ~ ~ ~
     j ~ ~ ~ ~ ~ ~ ~ ~ ~     J ~ ~ ~ ~ ~ ~ ~ ~ ~
    
    $ gcc -std=c11 -o testVarArgMDimArray testVarArgMDimArray.c 
    
    $ ./testVarArgMDimArray
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
    
    $
    

    typedef – 非常聪明的埃里克...

    【讨论】:

      猜你喜欢
      • 2017-12-29
      • 1970-01-01
      • 1970-01-01
      • 2014-12-21
      • 2015-12-13
      • 1970-01-01
      • 2019-10-24
      • 2018-09-29
      相关资源
      最近更新 更多