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

huangapple go评论66阅读模式
英文:

A question about specifying the *shape* of an array of pointers in C

问题

我正在构建一个在C语言中处理三维数组的系统。在我们的玩具示例中,这些数组的形状是3x3x2。我们将其视为二维的3x3网格,每个单元格包含一个数据数组。

有时,我希望访问这种网格的特定子部分;特别是我想要访问网格的行和对角线。更具体地说,给定一个坐标对的数组,我想要获得一个指向包含这些单元格内容指针的数组的指针。我已经能够使用我的函数 `subfinder` 实现这一点,见下面的代码。

```c
#include <stdio.h>

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

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

gcc错误:

test.c:6:10: 错误:‘subfinder’声明为返回数组的函数
subthing subfinder(mygrid p_grid, int indeces[3][2]) {
^~~~~~~~~
test.c: 在函数‘subfinder’中:
test.c:18:12: 警告:return使得整数指针没有类型转换[-Wint-conversion]
return result_array;
^~~~~~~~~~~~
test.c: 在函数‘main’中:
test.c:30:25: 错误:无效的初始化器
subthing diagonal = subfinder(example_grid, diag_indeces);
^~~~~~~~~
英文:

I'm building a system in C that will be handling three-dimensional arrays. For our toy example here these have the shape 3x3x2. We think of them as two-dimensionals 3x3 grids where each cell holds an array of data.

At times I will want to access certain subparts of such a grid; in particular I will want to get access to the rows and diagonals of the grid. More specifically, given an array of coordinate pairs I want to get a pointer to an array that contains pointers to the contents of those cells. This I've been able to do with my function subfinder, see the code below.

#include &lt;stdio.h&gt;

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&lt;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(&quot;Original center is (%d, %d).\n&quot;, 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(&quot;Diagonal is        (%d, %d).\n&quot;, diagonal[1][0], diagonal[1][1]);

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

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

What my problem here is that I would like to have my typedef of subthing to be more descriptive of the fact that the thing it points to always has the shape of 3x2, or even that it has length 3. (I'm always getting either rows or diagonals from the main grid here.) So instead of typedef char** subthing; I would like to say e.g. typedef char* subthing[3]; But the latter fails to compile with function type definition errors shown at the end. What I fail to understand is why inside the subfinder function I can declare the thing to be returned as static char* result_array[3];, but this cannot then be used as the return type of the function itself.

Any help or explanations about what I'm doing here would be much appreciated!

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

答案1

得分: 2

以下是您提供的代码的中文翻译部分:

一个可能的想法是避免将数组作为对象返回,而是将其作为参数传递,并在函数内部填充它。您甚至可以根据需要将其 typedef 为类型。

#include <stdio.h>;

/*
原始中心点是 (2, 2)。
对角线是       (2, 2)。
原始中心点是 (7, 8)。
对角线是       (7, 8)。
原始中心点是 (0, 8)。
对角线是       (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("原始中心点是 (%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("对角线是        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);

    example_grid[1][1][0] = 7;
    example_grid[1][1][1] = 8;
    printf("原始中心点是 (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("对角线是        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);

    diagonal[1][0] = 0;
    printf("原始中心点是 (%d, %d).\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("对角线是        (%d, %d).\n", diagonal[1][0], diagonal[1][1]);
}

注意:翻译中的注释保留了原文中的内容。

英文:

One possible idea is to avoid returning the array as an object, but rather having it passed as an argument, and fill it inside the function. You can even typedef it as you want.

#include &lt;stdio.h&gt;

/*
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&lt;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(&quot;Original center is (%d, %d).\n&quot;, 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(&quot;Diagonal is        (%d, %d).\n&quot;, diagonal[1][0], diagonal[1][1]);

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

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

答案2

得分: 1

以下是你要翻译的内容:

You can go all in and pass only fully typed variables. Normally, as you are aware, arrays are "adjusted" to pointers to their first element when they are passed as arguments. This is the reason for the information loss you are lamenting: The array length is lost in transition. We can prevent this by passing pointers to arrays. The pointer retains its complete type information through assignments and as a function argument (the two have similar semantics). This makes it possible for the compiler to warn about out-of-bounds access, for example if you comment the printf at the end of subfinder() in.

The downside is that you have to dereference the pointer-to-array before you index it. This complicates expressions, especially if the array elements in turn are pointers to arrays, as is the case with diagonal in main().

Pointers to arrays are peculiar in that they have the same numeric value as the first element. The only difference is the type. This makes a difference when that pointer is incremented or indexed: Because each element pointed to is the entire array its "unit" is the entire array size (not the array element size).

Note that this is not very idiomatic C, and I'm not really recommending it. (I'd also think that Dennis Ritchie didn't consider this idiomatic or he would have exchanged the precedence of the index operator [] and the dereference operator *.) The expressions become unwieldy and hard to parse (I just stared 10 minutes at my code because I made an error bracketing (*(*diagonal)[0])[0] correctly). It is also only possible if the array dimensions are known at compile time. But here is a fully typed solution anyway. I used symbolic constants for your dimensions and typedef'ed as much as I could but you should recognize most of the code.

#include <stdio.h>;

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

typedef char CELL[CELL_SZ];          // 基本单元,一个字符对。
typedef CELL MYGRID[X_DIM][Y_DIM];   // 这是一个二维的细胞阵列,如你所解释的
typedef CELL *CELLPTR_ARR[IDX_ARR_LEN]; // 细胞指针的阵列
typedef int IDX_ARR[IDX_ARR_LEN][2];     // 这个2与CELL_SZ无关(代表x和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;
    }

    // 优点:阵列越界警告。
    // 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("原始中心是(%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("对角线是(%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]);

    // 更改中心的细胞到7/8
    example_grid[1][1][0] = 7;
    example_grid[1][1][1] = 8;
    printf("中心是(%d, %d)。\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("对角线是(%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]);

    // 通过对角线更改中间细胞的第一个元素为0
    (*(*diagonal)[1])[0] = 0;
    printf("原始中心是(%d, %d)。\n", example_grid[1][1][0], example_grid[1][1][1]);
    printf("对角线中心是(%d, %d)。\n", (*(*diagonal)[1])[0], (*(*diagonal)[1])[1]);

    // 对对角线访问的适当括号让我头疼。
    // 当然,你可以将其分解成步骤:
    // 对角线是一个指向子元素的指针;取消引用后,它是一个指向细胞的指针阵列;
    // 分配后,它被调整为指向其第一个元素的指针

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

    // 或者你把它交给一个你在测试后再也不看的函数。
    printf("%d %d\n", diagValAt(diagonal,1,0), diagValAt(diagonal,1,1));

}
英文:

You can go all in and pass only fully typed variables. Normally, as you are aware, arrays are "adjusted" to pointers to their first element when they are passed as arguments. This is the reason for the information loss you are lamenting: The array length is lost in transition. We can prevent this by passing pointers to arrays. The pointer retains its complete type information through assignments and as a function argument (the two have similar semantics). This makes it possible for the compiler to warn about out-of-bounds access, for example if you comment the printf at the end of subfinder() in.

The downside is that you have to dereference the pointer-to-array before you index it. This complicates expressions, especially if the array elements in turn are pointers to arrays, as is the case with diagonal in main().

Pointers to arrays are peculiar in that they have the same numeric value as the first element. The only difference is the type. This makes a difference when that pointer is incremented or indexed: Because each element pointed to is the entire array its "unit" is the entire array size (not the array element size).

Note that this is not very idiomatic C, and I'm not really recommending it. (I'd also think that Dennis Ritchie didn't consider this idiomatic or he would have exchanged the precedence of the index operator [] and the dereference operator *.) The expressions become unwieldy and hard to parse (I just stared 10 minutes at my code because I made an error bracketing (*(*diagonal)[0])[0] correctly). It is also only possible if the array dimensions are known at compile time. But here is a fully typed solution anyway. I used symbolic constants for your dimensions and typedef'ed as much as I could but you should recognize most of the code.

#include &lt;stdio.h&gt;
#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&#39;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&#39;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 &lt; IDX_ARR_LEN; i++) {
x = (*idxArr)[i][0];
y = (*idxArr)[i][1];
CELL *addr = &amp;(*p_grid)[x][y];
result_array[i] = addr;
}
// Advantage: array out-of-bounds warning.
// printf(&quot;%d\n&quot;, (*idxArr)[IDX_ARR_LEN][0]);
return &amp;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(&quot;Original center is (%d, %d).\n&quot;, example_grid[1][1][0], example_grid[1][1][1]);
IDX_ARR diag_indeces = { {0,0}, {1,1}, {2,2} };
CELLPTR_ARR *diagonal = subfinder(&amp;example_grid, &amp;diag_indeces);
printf(&quot;Diagonal is        (%d, %d).\n&quot;, (*(*diagonal)[0])[0], (*(*diagonal)[0])[1]);
printf(&quot;                   (%d, %d).\n&quot;, (*(*diagonal)[1])[0], (*(*diagonal)[1])[1]);
printf(&quot;                   (%d, %d).\n&quot;, (*(*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(&quot;Center is (%d, %d).\n&quot;, example_grid[1][1][0], example_grid[1][1][1]);
printf(&quot;Diagonal is        (%d, %d).\n&quot;, (*(*diagonal)[0])[0], (*(*diagonal)[0])[1]);
printf(&quot;                   (%d, %d).\n&quot;, (*(*diagonal)[1])[0], (*(*diagonal)[1])[1]);
printf(&quot;                   (%d, %d).\n&quot;, (*(*diagonal)[2])[0], (*(*diagonal)[2])[1]);
// Changing middle cell&#39;s first elem to 0 through diagonal
(*(*diagonal)[1])[0] = 0;
printf(&quot;Original center is (%d, %d).\n&quot;, example_grid[1][1][0], example_grid[1][1][1]);
printf(&quot;Diagonal center is (%d, %d).\n&quot;, (*(*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(&quot;%d %d\n&quot;, cell[0], cell[1]);
// Or you turf it to a function you never look at again once it&#39;s tested.
printf(&quot;%d %d\n&quot;, diagValAt(diagonal,1,0), diagValAt(diagonal,1,1));
}

huangapple
  • 本文由 发表于 2023年2月23日 21:45:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/75545668.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定