为什么不能将2d数组的名称分配给2d指针?

时间:2019-08-18 09:54:59

标签: c arrays pointers

在练习一些简单的数组和指针基础知识时,遇到了一些我不明白的事情:

在我能找到的所有书面资料中,他们说2D数组的名称实际上是2D指针, 意思是如果我写:

int a[3][4];

并声明一个指针:

int **d2;

它们都是相同的类型,我可以放心地分配:

d2 = a;

使指针指向第一行的开头。

但最令人惊讶的是,这行不通,我的问题是-为什么?

我会为您复制我收到的代码和警告:

#include <stdio.h>

int main() {
    int **d2;
    int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };

    d2 = a;
    while (d2 < a + 2) {
        printf("%d", **d2);
        d2++;
    }
    return 0;
} 

我得到此诊断信息:

warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
  d=a;
   ^

ptr.c:11:9: warning: comparison of distinct pointer types lacks a cast
  while(d<a+2)

4 个答案:

答案 0 :(得分:4)

它们的内部结构完全不同,因此它们是不可互换的。

2D数组

2D数组是这样的:

p[3][3]
|
v 
+---------+---------+---------+---------+---------+---------+---------+
| p[0][0] | p[0][1] | p[0][2] | p[1][0] | p[1][1] | p[1][2] |   ...   |
+---------+---------+---------+---------+---------+---------+---------+

一个连续的空间。

指针

指针是这样的:

**p
|
v 
+------+   +---------+---------+---------+---------+---------+
| p[0] |-->| p[0][0] | p[0][1] | p[0][2] | p[0][3] |   ...   |
+------+   +---------+---------+---------+---------+---------+
| p[1] |-->| p[1][0] | p[1][1] | p[1][2] | p[1][3] |   ...   |
+------+   +---------+---------+---------+---------+---------+
| p[2] |-->| p[2][0] | p[2][1] | p[2][2] | p[2][3] |   ...   |
+------+   +---------+---------+---------+---------+---------+
| .... |
+------+

每个p[i]可能指向大小不同的不同空间。

答案 1 :(得分:1)

int **d2;不是2D指针,它是指向int的指针的指针,有时也称为双指针。

int a[3][4];定义了一个由3个4个int组成的数组,也称为2D数组。

这2个对象的类型不兼容:这里没有int指针变量,其地址可以存储在d2中。

举个比喻:

  • int就像一间拥有32个房间的房子;
  • 街道是一排排房子;
  • 街区是一组连续的街道;
  • 指针是通讯录中带有房屋地址的条目;
  • 双指针是一张纸,您在其中写了存储通讯录的位置。

二维数组中的行在内存中是连续的,因此您可以通过以下方式枚举数组中的条目:

#include <stdio.h>

int main() {
    int *p;
    int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };

    printf("iterating as a 2D array:\n");
    p = &a[0][0];
    for (int row = 0; row < 5; row++) {
        for (int col = 0; col < 2; col++) {
            printf("%d ", *p);
            p++;
        }
        printf("\n");
    }
    printf("\n");

    printf("iterating as a single array:\n");
    p = &a[0][0];
    while (p < &a[5][0]) {
        printf("%d ", *p);
        p++;
    }
    printf("\n");
    return 0;
}

输出:

iterating as a 2D array:
1 2
3 4
5 7
7 8
9 11

iterating as a single array:
1 2 3 4 5 7 7 8 9 11

答案 2 :(得分:1)

int **d2定义了一个指向int的指针。您也可以将其视为指向数组的指针,并且每个数组元素都包含一个指向int的指针。 ang敏杰的回答很好地显示了这一点。

数组int a[3][4];连续的内存块,因此等效于int a[3*4];。如您所见,它不是指向行的指针数组。

请注意,编译器知道何时使用连续数组和指针数组,并使用正确的索引方法。例如:

void f(int **d2)
{
    printf("%d", d2[3][4]);
}
void g(void)
{
    int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };

    printf("%d", a[3][1]);
}

如您所见,即使基础表示形式完全不同,编写索引表达式的方式也相同。我知道这令人困惑。只要记住“编译器知道”即可。

答案 3 :(得分:1)

这是因为int **d2是指向指针的指针。没有2d指针之类的东西。

例如,让我们考虑一维数组:

int a[2] = {1, 2};

现在,如果按以下方式分配,则整数指针将指向数组的第一个元素:

int *d = a;

谈论指针的指针时,指向指针的指针指向一个保存其他指针地址的内存位置!

例如,

// This will declare an integer pointer
int *a;

上面的指针将存储其他一些变量的地址。

例如,您可以为其分配值,如下所示:

int x = 5;
int *a = &x;

现在,这是一个指向指针的指针:

  

指向指针的指针保存其他指针的内存地址。

这意味着,在上面的示例中,我们可以声明一个指向指针的指针,该指针保存指针a的地址:

int x = 5;
int *a = &x;

// A pointer to pointer
int **b = &a;

// To print value of x using b
printf("%d", **b);

上面的代码可以理解如下:

  1. a =&x;所以* a = *(&x)= 5
  2. b =&a;所以* b = *(b中的地址)= *(a的地址)= x的地址
  3. 但是** b = *(* b)= *(x的地址)= 5

希望这可以消除您对指针的困惑。

现在,针对您的用例,如果您想要一个指针指向您的2d整数数组,则只需使用一个指针并为其分配数组第一个元素的地址即可。可以按照以下步骤进行操作:

int a[2][2] = {{1, 2}, {3, 4}};

// Declare a pointer that points to first element of array
int *b = &a[0][0];

现在,如果要使用指针访问此数组的元素a[i][j],可以将其作为b + 2*i + j进行访问。

通常,如果数组的大小为p*q,则可以使用a[i][j]访问元素b + q * i + j

例如,要使用2个for循环打印数组,

int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];

for(int i=0; i<rows; i++) {
    for(int j=0; j<cols; j++) {
        // Print a[i][j] using pointer b
        printf("%d ", *(b + cols * i + j));
    }
}

// 1 2 3 4 5 6

您还可以使用单循环,

int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];

for(int i=0; i<rows*cols; i++) {
    printf("%d ", *(b + i));
}

// 1 2 3 4 5 6