在为它分配内存并用字符值填充它之后,我试图返回一个现代二维动态数组。我尝试解决一些错误,但最终陷入了死胡同。我最初有一个有效的 2D 动态数组实现在使用 C90 中的指针数组方法的函数中返回,但我被告知它有开销并且已经过时了。因此,我决定尝试使用从此处获取的现代 C 二维数组方法进行升级:How do I work with dynamic multi-dimensional arrays in C?.
las,它最终在我的实现中不起作用,所以我现在想知道我可能做错了什么。请参阅我下面的代码以获取完整参考:(目标是在主要辅助函数中制作棋盘的二维矩阵)
char **allocateToBoardC99(const int nthDim)
{
char(*boardArray)[nthDim] = malloc(sizeof(char[nthDim][nthDim]));
return (char **)boardArray;
}
char **getCheckeredBoardC99(const int nthDim)
{
bool startWithWhiteTile = true;
// C99 version (more elegant):
char **arr = allocateToBoardC99(nthDim);
// Make the board checkered by alternating char assignments based on a bool latch
for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
{
if (startWithWhiteTile)
{
for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
{
arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
}
startWithWhiteTile = false;
}
else
{
for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
{
arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
}
startWithWhiteTile = true;
}
}
return (char **)arr;
}
int main(int argc, char *argv[])
{
// Initialize dimension of the checker board:
int dim = 8;
// char **boardMatrix = getCheckeredBoardC90(dim); // Works
char **boardMatrix = getCheckeredBoardC99(dim);
printf("hello world");
printf("\n");
for (int row = 0; row < dim; row++)
{
printf("[");
for (int column = 0; column < dim; column++)
{
printf("%c ", boardMatrix[row][column]);
}
printf("]\n");
}
return 0;
}
如果你对上面的
getCheckeredBoardC90(dim);
工作感到好奇,下面是使它工作的以下代码序列(而较新的 C99 没有):
char **allocateToBoardC90(const int nthDim)
{
// The calloc() function reserves storage space for an array of NUM elements, each of length SIZE bytes:
// calloc(size_t NUM, size_t SIZE)
char *values = calloc(nthDim * nthDim, sizeof(char)); // In our 2D board array, we would have nxn (char)elements (8x8=64 elements)
// Simple way is to allocate a memory block of size row and access its elements using simple pointer arithmetic:
char **rows = malloc(nthDim * sizeof(char)); // pointer to overall space
// Iterate through each *row; 0 to nthRow
for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
{
rows[rowIndex] = values + (rowIndex * nthDim);
}
// Returns an array of pointers (array of arrays)
return rows;
}
char **getCheckeredBoardC90(const int nthDim)
{
bool startWithWhiteTile = true;
//**array[rowIndex][columnIndex]
// char **arr[nthDim][nthDim];
// C90 version (lotta pointers; be aware of performance overhead):
char **arr = allocateToBoardC90(nthDim);
// Make the board checkered by alternating char assignments based on a bool latch
for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
{
if (startWithWhiteTile)
{
for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
{
arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
}
startWithWhiteTile = false;
}
else
{
for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
{
arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
}
startWithWhiteTile = true;
}
}
return arr;
}
int main(int argc, char *argv[])
{
// Initialize dimension of the checker board:
int dim = 8;
char **boardMatrix = getCheckeredBoardC90(dim); // Works
// char **boardMatrix = getCheckeredBoardC99(dim);
printf("hello world");
printf("\n");
for (int row = 0; row < dim; row++)
{
printf("[");
for (int column = 0; column < dim; column++)
{
printf("%c ", boardMatrix[row][column]);
}
printf("]\n");
}
return 0;
}
如果辅助函数正确返回,那么 main 的输出应该如下所示:
hello world
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
在此方面的具体帮助将不胜感激,谢谢!
注意:最初使用这个动态二维数组现代 C 代码,我试图从分配器函数返回二维数组(实际上是对它的引用),将该二维数组的引用传递给元素初始化函数以填充棋盘,如usual,然后最后将最终确定的二维数组(作为参考)传递给main函数,通过终端输出检查二维数组的内容。
最终发生的是解决指向对象的指针问题、指针转换等问题的大量试验和错误。这只会导致(从我认为不正确的 C 指针转换)到最终游戏“分段错误”在
getCheckeredBoardC99
函数中。
A
char**
只能用于指向 char*
数组中的第一项。除了指针查找表可用于类似于二维数组的语法外,char**
与二维数组几乎没有任何关系。
考虑通过参数返回数组:
void allocateToBoardC99 (size_t n, char(**boardArray)[n] )
{
*boardArray = malloc(sizeof(char[n][n]));
}
int main (void)
{
size_t n=5;
char (*arr)[n];
allocateToBoardC99(5, &arr);
}
char**
类型不是二维数组,因此您不能以这种方式返回二维数组。由于可变修改类型的某些特性(即 VLA 和指向 VLA 的指针),它们不能在文件范围内声明。所以函数不能返回 VMT,因为它们的返回类型在文件范围内是可见的。在二维数组的情况下,有一种变通方法,即返回指向未完成数组类型的指针,例如int (*)[]
。该指针不能被取消引用,因为它指向不完整的类型,因此必须在使用前将其分配给一个完整且兼容的数组类型。
// function returning a pointer to an array
char (*allocateToBoardC99(const int nthDim))[]
{
char(*boardArray)[nthDim] = malloc(sizeof(char[nthDim][nthDim]));
return boardArray;
}
... usage ...
char (*arr)[n] = allocateToBoardC99(n);
从函数返回复杂类型需要一些笨拙的语法,但可以使用流行的
typeof
扩展(C23 中的一个特性)让它变得更漂亮。
typeof(char[]) * allocateToBoardC99(const int nthDim);
根据@Lundin 的回答和数组参数方法(及其优点)的切换方法,我得到了以下代码解决方案以在现代 C 中获得所需的输出:
1.) 首先,将我的分配辅助函数更改为传递参数结构:
void allocateToBoardC99(size_t nthDim, char (**boardArray)[nthDim])
{
*boardArray = malloc(sizeof(char[nthDim][nthDim]));
}
2.) 接下来,在填充板之前在主代码中启动分配(在这里声明数组然后继续推卸责任):
int main(int argc, char *argv[])
{
// Initialize dimension of the checker board:
size_t dim = 8;
// char **boardMatrix = getCheckeredBoardC90(dim); // Works
char(*boardMatrix)[dim];
allocateToBoardC99(dim, &boardMatrix);
// NOTE: rest of the main code hidden for now...
}
3.) 现在我们准备开始用值填充方格板。将棋盘矩阵作为参数传递给下标赋值的参考目标。另外,将返回类型更改为
void
,因为它只是执行元素分配操作:
void getCheckeredBoardC99(size_t nthDim, char (*boardArray)[nthDim])
{
bool startWithWhiteTile = true;
// C99 version (more elegant):
// Make the board checkered by alternating char assignments based on a bool latch
for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
{
if (startWithWhiteTile)
{
for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
{
boardArray[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
}
startWithWhiteTile = false;
}
else
{
for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
{
boardArray[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
}
startWithWhiteTile = true;
}
}
}
请注意,在第 3 步中,我们仅传递声明的不完整数组类型指针(此类型的解释归功于@tsanisl),而不是
allocateToBoardC99
中的双指针版本。这是因为 getCheckeredBoardC99
并不是要指代块(如 malloc),而是指板中的单个元素。正如我已经认识到的那样,我正在尝试仔细措辞,并希望回应@Lundin 和@tsanisl,像 char**
这样的双指针类型不等于/意味着 2D 数组。
4.) 在主代码中的分配函数之后调用元素分配函数来完成这个小过程,同时打印现代 2D 板阵列的元素以验证它是否匹配所需的输出:
int main(int argc, char *argv[])
{
// Initialize dimension of the checker board:
size_t dim = 8;
// char **boardMatrix = getCheckeredBoardC90(dim); // Works
char(*boardMatrix)[dim];
allocateToBoardC99(dim, &boardMatrix);
// --SEE REST OF CODE BELOW THAT COMPLETES THE BOARD PROCESS--
getCheckeredBoardC99(dim, boardMatrix);
printf("hello world");
printf("\n");
for (int row = 0; row < dim; row++)
{
printf("[");
for (int column = 0; column < dim; column++)
{
printf("%c ", boardMatrix[row][column]);
}
printf("]\n");
}
return 0;
}
调试后得到如下正确输出:
(带有交替方块的棋盘的 2D 表示;B = 黑色,W = 白色)
PS Drive:\path> '...'
hello world
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
如果有任何我遗漏或没有正确解释的地方,请随时发表评论(也就是说,如果这个问题线程没有被锁定;我是 Stack Overflow 上的新手,请多多包涵)。
感谢所有帮助我解决最初问题并指出我的 C 编程误解的人,这样我就不会再次陷入类似的陷阱。