I need to set up a variadic function in C that prints a variable number of 2-D char arrays side-by-side. I'm having a hard time figuring out how to initialize the boards
variable with va_arg()
.
The key problematic line is: boards[i] = va_arg(ap, char*[][BOARDSIZE]);
The line produces a compiler error (currently, Second argument to 'va_arg' is of incomplete type 'char *[][10]'
), but basically I'm sure I'm not doing something right. I'm just not sure what that something is. I've tried several variations to no avail. The rest of the code should be okay though.
(Thank you in advance for any help.)
#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;
}
Actually, I came to the same conclusion like Eric except that I even didn't consider to solve multi-dimension array issues by mere typedef
s.
Out of curiosity, I tried to write down a working version of the OP. As I finally got one I want to present my code (in addition to Eric Postpischils answer).
So, after some fiddling I got this working version 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;
}
Before I got it working there were issues which I tried solve in VS2013. Too sad – VS2013 does not support VLAs. Hence, I had to do it in mind but I got it running.
As already recommended in one of my comments, I chose char
as board element instead of char*
. I could've solved it for char*
as well but I feel that char*
could've been an "accidental" choice in the OP.
Following the idea of Eric, I made a type for the 2D board array:
typedef char Board[NROWS][NCOLS];
The more important is probably the second:
typedef char (*PBoard)[NCOLS];
Remember that arrays in function parameters are always compiled as pointers. C never passes arrays as arguments. Hence, if we call a function with an argument of type Board
we will receive an argument of type PBoard
.
Please, note the parentheses around *PBoard
– this grants that PBoard
is a pointer to array. If you remove them you get an array of pointers instead – big difference and not what is intended.
Having mastered this, things in showBoardVariadic()
become rather easy.
The array of boards is declared as:
PBoard boards[nArgs];
The assignment with va_arg
is simply:
boards[i] = va_arg(ap, PBoard);
The access to the boards is simply:
printf(" %c", boards[i][row][col]);
This might be surprising but in this case the pointer to array behaves like an array of arrays. It just has a different type. (E.g. you should not use sizeof
with PBoard
as in this case the different types would take effect.)
As every board element contains the same contents in this state of development, I was afraid whether issues in board indexing could be unnoticed. Therefore I implemented an alternative initialization where each first element of column gets another character: for playerBoard
'a' + row
, for opponentBoard
'A' + row
. This test assignment is activated by defining the macro CHECK
. In my test session, I compiled once with -D CHECK
once without.
Btw. if you wonder why I introduced NROWS
and NCOLS
: While writing this answer I realized that I wouldn't notice if I accidentally flipped rows and columns somewhere as they have equal size in OP. Thus, I separated things and tested with NROWS
≠ NCOLS
. Phew – it still worked properly.
Last but not least, my sample session in Cygwin (as I'm on 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
– very clever Eric...
The C specification of va_arg
requires “The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type.” The string char*[][BOARDSIZE]
does not satisfy this. You should use a typedef
to give a name to the type.
Additionally, in the parameter list of a function declaration, char*[][BOARDSIZE]
is automatically adjusted to char*(*}[BOARDSIZE]
. In a va_arg
(or typedef
), it is not. You should use the adjusted form.
So, you should define a name for a type that is a pointer to an array of BOARDSIZE
pointers to char
:
typedef char *(*MyType)[BOARDSIZE];
You should change boards
to be an array of these rather than an array of arrays:
MyType boards[numArgs];
and you should change va_arg
to use the new type:
boards[i] = va_arg(ap, MyType);
Also note that you are setting every element of the boards to the string "~". This sets them all to point to a string literal, which is likely not what you want. You are not allowed to modify characters in this string literal, so the only way to change what the boards contain is to change them to point to different strings.
If each board element is going to be a single character, you should use char
instead of char *
. If they are going to be a fixed or small number multiple characters, you might want an array of char
instead of a pointer to char
. If they are going to be a considerable number of multiple characters, you may want to use char *
but allocate space for each board element.
来源:https://stackoverflow.com/questions/47590743/variadic-c-function-printing-multiple-2-d-char-arrays