关于在C中指定指针数组的 *shape* 的问题

oyxsuwqo  于 2023-03-01  发布在  其他
关注(0)|答案(2)|浏览(167)

我正在用C语言构建一个处理三维数组的系统,在我们的玩具例子中,这些数组的形状是3x 3x 2,我们把它们看作是二维的3x 3网格,每个网格包含一个数据数组。
有时我会想访问这样一个网格的某些子部分;特别是我想访问网格的行和对角线,更具体地说,给定一个坐标对数组,我想得到一个数组指针,该数组包含指向这些单元格内容的指针,我已经可以用我的函数subfinder来实现,见下面的代码。

#include <stdio.h>

typedef char mygrid[3][3][2];
typedef char** subthing;

subthing subfinder(mygrid p_grid, int indeces[3][2]) {
    static char* result_array[3];
    int x, y;

    for(int i=0; i<3; i++) {
        x = indeces[i][0];
        y = indeces[i][1];

        char* new = p_grid[x][y];
        result_array[i] = new;
    }

    return result_array;
}

int main() {
    mygrid example_grid = {
            { {1, 1}, {1, 2}, {1, 3} }, 
            { {2, 1}, {2, 2}, {2, 3} }, 
            { {3, 1}, {3, 2}, {3, 3} }
        }; 
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);

    int diag_indeces[3][2] = {{0,0}, {1,1}, {2,2}};
    subthing diagonal = subfinder(example_grid, diag_indeces);
    printf("Diagonal is        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);

    example_grid[1][1][0] = 7;
    example_grid[1][1][1] = 8;
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("Diagonal is        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);

    diagonal[1][0] = 0;
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("Diagonal is        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);
}

这里我的问题是,我希望我的typedef为subthing,来更好地描述这样一个事实,即它所指向的东西总是具有3x 2的形状,或者甚至其具有长度3。(我总是从主网格中获取行或对角线。)因此,我想使用例如typedef char** subthing;来代替typedef char** subthing;typedef char* subthing[3];但是后者编译失败,函数类型定义错误显示在末尾.我不明白的是为什么在subfinder函数中我可以声明返回的东西为static char* result_array[3];,但是这不能作为函数本身的返回类型.
任何帮助或解释我在这里做什么将不胜感激!
gcc errors:

test.c:6:10: error: ‘subfinder’ declared as function returning an array
 subthing subfinder(mygrid p_grid, int indeces[3][2]) {
          ^~~~~~~~~
test.c: In function ‘subfinder’:
test.c:18:12: warning: return makes integer from pointer without a cast [-Wint-conversion]
     return result_array;
            ^~~~~~~~~~~~
test.c: In function ‘main’:
test.c:30:25: error: invalid initializer
     subthing diagonal = subfinder(example_grid, diag_indeces);
                         ^~~~~~~~~
6g8kf2rb

6g8kf2rb1#

一个可能的想法是避免将数组作为对象返回,而是将其作为参数传递,并将其填充到函数中。您甚至可以根据需要对其进行typedef。

#include <stdio.h>

/*
Original center is (2, 2).
Diagonal is        (2, 2).
Original center is (7, 8).
Diagonal is        (7, 8).
Original center is (0, 8).
Diagonal is        (0, 8).
*/

typedef char mygrid[3][3][2];
typedef char* subthing_type[3];

void subfinder(mygrid p_grid, int indeces[3][2], subthing_type subthing) {
    static char* result_array[3];
    int x, y;

    for(int i=0; i<3; i++) {
        x = indeces[i][0];
        y = indeces[i][1];

        char* new = p_grid[x][y];
        subthing[i] = new;
    }

}

int main() {
    mygrid example_grid = {
            { {1, 1}, {1, 2}, {1, 3} }, 
            { {2, 1}, {2, 2}, {2, 3} }, 
            { {3, 1}, {3, 2}, {3, 3} }
        }; 
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);

    int diag_indeces[3][2] = {{0,0}, {1,1}, {2,2}};
    subthing_type diagonal;
    subfinder(example_grid, diag_indeces,diagonal);
    printf("Diagonal is        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);

    example_grid[1][1][0] = 7;
    example_grid[1][1][1] = 8;
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("Diagonal is        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);

    diagonal[1][0] = 0;
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("Diagonal is        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);
}
wz3gfoph

wz3gfoph2#

你可以全力以赴,只传递完全类型化的变量。通常情况下,正如你所知道的,当数组作为参数传递时,它们会被“调整”到指向第一个元素的指针。这就是你所哀叹的信息丢失的原因:数组长度在转换中丢失了。我们可以通过传递 pointers 到数组来防止这种情况。指针通过赋值和作为函数参数保留了它的完整类型信息(两者有相似的语义)。这使得编译器可以警告越界访问,例如,如果你在中的subfinder()末尾注解printf。
缺点是在索引数组指针之前,必须解引用它,这会使表达式复杂化,特别是当数组元素反过来又是数组指针时,就像diagonalmain()中的情况一样。
指向数组的指针的特殊之处在于,它们与第一个元素具有相同的数值,唯一的区别是类型,这在指针递增或索引时会产生差异:因为指向的每个元素都是整个数组,所以它的“单位”是整个数组大小(而不是数组元素大小)。
注意,这不是非常惯用的C语言,我并不是真的建议你这么做。(我还认为Dennis里奇没有考虑到这种习惯用法,否则他会交换索引操作符[]和解引用操作符*的优先级。)表达式变得笨拙且难以解析(我花了10分钟看我的代码,因为我错误地将(*(*diagonal)[0])[0]括起来了)。这也只有在编译时知道数组维数的情况下才有可能。但是这里有一个完全类型化的解决方案。我使用符号常量表示维数,并使用typedef'尽我所能艾德,但您应该认识大部分代码。

#include <stdio.h>

#define X_DIM 3
#define Y_DIM 3
#define CELL_SZ 2

#define IDX_ARR_LEN 3 // not the same as X or Y_DIM!

typedef char CELL[CELL_SZ];          // the basic unit, a char pair.
typedef CELL MYGRID[X_DIM][Y_DIM];   // it's a 2-dimensional grid of cells, as you explained
typedef CELL *CELLPTR_ARR[IDX_ARR_LEN]; // IDX_ARR_LEN pointers to cells
typedef int IDX_ARR[IDX_ARR_LEN][2];     // the 2 is unrelated to CELL_SZ (it's x and y)

CELLPTR_ARR *subfinder(MYGRID *p_grid, IDX_ARR *idxArr) {
    static CELLPTR_ARR result_array;
    int x, y;

    for (int i = 0; i < IDX_ARR_LEN; i++) {
        x = (*idxArr)[i][0];
        y = (*idxArr)[i][1];

        CELL *addr = &(*p_grid)[x][y];
        result_array[i] = addr;
    }

    // Advantage: array out-of-bounds warning.
    // printf("%d\n", (*idxArr)[IDX_ARR_LEN][0]);
    return &result_array;
}

char diagValAt(CELLPTR_ARR* cellPtrArrPtr, int arrIdx, int cellIdx)
{
    return (*(*cellPtrArrPtr)[arrIdx])[cellIdx];
}

int main() {
    MYGRID example_grid = {
            { {1, 1}, {1, 2}, {1, 3} },
            { {2, 1}, {2, 2}, {2, 3} },
            { {3, 1}, {3, 2}, {3, 3} }
    };
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);

    IDX_ARR diag_indeces = { {0,0}, {1,1}, {2,2} };
    CELLPTR_ARR *diagonal = subfinder(&example_grid, &diag_indeces);

    printf("Diagonal is        (%d, %d).\n", (*(*diagonal)[0])[0], (*(*diagonal)[0])[1]);
    printf("                   (%d, %d).\n", (*(*diagonal)[1])[0], (*(*diagonal)[1])[1]);
    printf("                   (%d, %d).\n", (*(*diagonal)[2])[0], (*(*diagonal)[2])[1]);
                                                                                   
    // Changing center cell to 7/8
    example_grid[1][1][0] = 7;
    example_grid[1][1][1] = 8;
    printf("Center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("Diagonal is        (%d, %d).\n", (*(*diagonal)[0])[0], (*(*diagonal)[0])[1]);
    printf("                   (%d, %d).\n", (*(*diagonal)[1])[0], (*(*diagonal)[1])[1]);
    printf("                   (%d, %d).\n", (*(*diagonal)[2])[0], (*(*diagonal)[2])[1]);

    // Changing middle cell's first elem to 0 through diagonal
    (*(*diagonal)[1])[0] = 0;
    printf("Original center is (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("Diagonal center is (%d, %d).\n", (*(*diagonal)[1])[0], (*(*diagonal)[1])[1]);

    // the proper bracketing of the diagonal access gave me a headache.
    // Of course, one could brake it down into steps:
    // diagonal is a pointer to a subthing; de-referenced, it is an array of pointers to cell; 
    // assigned, it is adjusted to a pointer to its first element

    CELL** cellPtrArr = *diagonal;
    CELL* cellPtr = cellPtrArr[1]; // middle
    char* cell = *cellPtr;
    printf("%d %d\n", cell[0], cell[1]);

    // Or you turf it to a function you never look at again once it's tested.
    printf("%d %d\n", diagValAt(diagonal,1,0), diagValAt(diagonal,1,1));

}

相关问题