Variadic C 函数打印多个二维字符数组

Variadic C function printing multiple 2-D char arrays

我需要在 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 的 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 * 但为每个板元素分配 space。

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

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

所以,经过一些摆弄,我得到了这个工作版本 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。因此,我必须牢记这一点,但我明白了 运行。

正如我在其中一条评论中所建议的那样,我选择 char 作为版块元素而不是 char*。我也可以为 char* 解决它,但我觉得 char* 可能是 OP 中的 "accidental" 选择。

按照Eric的想法,我做了一个二维板阵列的类型:

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 一起使用,因为在这种情况下,不同的类型会生效。)

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

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

最后但同样重要的是,我在 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 – 非常聪明的埃里克...