尝试在 C 函数中返回 C99 二维动态数组时出现段错误

问题描述 投票:0回答:3

在为它分配内存并用字符值填充它之后,我试图返回一个现代二维动态数组。我尝试解决一些错误,但最终陷入了死胡同。我最初有一个有效的 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
函数中。

c pointers multidimensional-array segmentation-fault
3个回答
3
投票

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);
}

1
投票

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);


0
投票

根据@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 编程误解的人,这样我就不会再次陷入类似的陷阱。

© www.soinside.com 2019 - 2024. All rights reserved.