C语言中多维数组的指针是如何工作的?

4
我正在尝试使用 C 语言中的指向多维数组的指针概念。假设我想通过一个函数处理一个多维数组。代码看起来有点像这样:
#include <stdio.h>

void proc_arr(int ***array)
{
    // some code
}

int main(int argc, char **argv)
{
    int array[10][10];
    for(int i = 0; i < 10; i++)
    {
        for(int j = 0; j < 10; j++)
        {
            array[i][j] = i * j;
        }
    }

    proc_arr(&array);

    return 0;
}

问题在于,当我想要在proc_arr内部访问array时,我无法这样做。据我理解,我们应该这样访问它:
void proc_arr(int ***array)
{
    (*array)[0][1] = 10;
}

所以,我对array进行了取消引用操作,告诉编译器我想去那个地址并获取值。但不知何故,它崩溃了。我尝试了多种*和括号的组合,但仍然无法使其工作。我很确定这是因为我不理解指针以及指针的指针。

哦,我注意到如果我们也使用char **(字符串数组),例如argv和envp,那么就会有所不同。但对于envp,我不知怎的可以使用(*envp)访问它。为什么呢?

这里是处理envp的函数(有效):

int envplen(char ***envp)
{
    int count = 0;

    while((*envp)[count] != NULL)
    {
        count++;
    }

    return count;
}

另外,我是否可以仅通过envp访问envplen函数中的envp,但仍然通过引用传递它?

感谢您的帮助。

3个回答

8
问题在于int array[10][10]在栈上分配时,内存布局并不符合您的预期。这是因为数组不是指针。即使下标可能表示二维数组,内存仍然按线性数组布局而不是"二维"数组。换句话说,int array[10][10]的内存布局如下所示:
starting address:                                    ending address:
| Block_1 of 10 int | Block_2 of 10 int | ... | Block_10 of 10 int |

因此,当您将数组隐式转换为int ***,然后尝试像(* array)[1][10]这样访问数组时,实际上它被翻译成类似于*(*((*array) + 1) + 10)的东西,并且进行这种操作的内存布局希望看到如下设置的内存:

int*** array
|
|
| Pointer |
|
|
| Pointer_0 | Pointer_1 | ... | Pointer 10 |
       |          |                 |
       |          |                 | Block of 10 int |
       |          |
       |          | Block of 10 int |
       |
       |Block of 10 int|

1
好的,我明白了:数组不是指针。谢谢。 - bertzzie

4
你出现了类型不匹配的问题。给定声明 int array[10][10],表达式&array的类型将为int (*)[10][10],而不是int ***。如果你改变函数原型,将其改为:
void proc_arr(int (*array)[10][10])

如果你的代码按照编写的方式工作,则应该可以正常运行。

以下表格显示了给定特定声明的各种数组表达式的类型。

声明:T a[M];
表达式 类型 退化为 ---------- ---- -------- a T [M] T * &a T (*)[M]
*a T a[i] T
声明:T a[M][N];
表达式 类型 退化为 ---------- ---- -------- a T [M][N] T (*)[N] &a T (*)[M][N] *a T [N] T * a[i] T [N] T * &a[i] T (*)[N] *a[i] T a[i][j] T
声明:T a[M][N][O];
表达式 类型 退化为 ---------- ---- -------- a T [M][N][O] T (*)[N][O] &a T (*)[M][N][O] *a T [N][O] T (*)[O] a[i] T [N][O] T (*)[O] &a[i] T (*)[N][O] *a[i] T [N] T * a[i][j] T [N] T * &a[i][j] T (*)[N] *a[i][j] T a[i][j][k] T

更高维数组的模式应该很清晰。


2

这样是行不通的:

void proc_arr(int ***array)
{
    (*array)[0][1] = 10;
}

因为在幕后,编译器必须将其转换为相对于数组开头的内存偏移量。这意味着它需要知道数组的维度。您没有在函数签名中声明它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接