2维数组的名称和第一个元素是什么?

3
在一小时学会C编程中,有这样一句话:
“与一维数组类似,多维数组的名称是指向第一个数组元素的指针。”
#include <stdio.h>

int m[2][4];

int main() {
    printf("%p\n", m);        // 1
    printf("%p\n", &(m[0]));  // 2
    printf("%p\n", m[0]);     // 3
    printf("%p\n", &m[0][0]); // 4
}

这会对所有语句输出相同的值。根据引用,1 == 2 和 3 == 4 是有道理的。但是我不理解 2 == 3 是怎么回事。m [0] 的地址怎么可能与 m [0] 相同呢?


因为这就是它的实现方式,正是由于这个原因 int *p = malloc(n * sizeof(int)) 才能够工作。尝试打印 &m,结果也会相同。因为对于数组来说,所有这些都没有被实际实现并且不占用内存,但仍然可以被使用并视为指向数组(或地址)的指针。 - infinite loop
OT: 转换说明符%p仅适用于void指针,因此代码应该像这样printf("%p\n", (void*) m);。对于其他三个printf()调用也是如此。否则将会引发未定义的行为。 - alk
这个问题已经以多少种方式被问过了? - StoryTeller - Unslander Monica
与一维数组类似,多维数组的名称是指向第一个数组元素的指针。但是,不是所有情况下数组都会转换为指针,因此m仍然是一个数组,而不是指针。 - chux - Reinstate Monica
3个回答

6
当分配二维(或任意维度)数组时,它只占用其元素的内存(不会为数组名或任何其他数组元素如 m[0] 和 m[1] 分配内存,在这种情况下,它们的作用类似于常量指针)。
因为实际上并没有为它们分配任何物理内存(甚至不需要),所以在尝试打印它们时将获得相同的地址。在你的情况下,由于它是一个二维数组,因为 m 指向 m[0],而 m[0] 又指向 m[0][0],获取所有这些地址都将给出相同的值(对于 m[1] 和 m[1][0] 也是如此)。
**为了更好地理解,可以参考以下内容(如果打印这些内容,它们将是这样的)
      _____         _____         _____
     |0x100|  -->  |0x100|  -->  |value|
0x100|_____|  0x100|_____|  0x100|_____|
       m            m[0]         m[0][0]

*value是m[0][0]的值。

在这里,mm[0]像指针一样工作,但它们并没有被物理实现(不占用内存),编译器会处理如何对待它们。

*虽然我已经展示了mm[0]都包含0x100,但实际上0x100包含m[0][0]的数据/值,但如果您执行任何操作,编译器就会按照这种方式处理它们。

**正因为如此,int *p = malloc(n * sizeof(int))是可能的,不同之处在于p现在占用内存,指向数组的起始地址。如果您将p视为int const *p,则可以像处理普通数组一样处理它,无需进行动态分配。


3
一个数组仅包含数据,不包含其他任何东西。这些数据具有一个起始地址 - 即第一个元素的地址。
"多维数组的名称是指向第一个数组元素的指针"是一种过度简化且不正确的说法。数组不是指针,指针也不是数组。
但是,任何维度的数组名称在表达式中使用时,都会产生一个临时指向数组第一个元素的指针。这经常被称为"数组退化为指针"。
现在,这些行之间唯一不同的是指针类型:
  1. 类型为int [2] [4]的数组退化为指向第一个元素的指针。2D数组中的第一个元素是类型为int [4]的1D数组。指向这样的数组的指针是一个数组指针int (*)[4]

  2. &m [0]给出第一个元素的地址,即第一个1D数组的地址。因此,这与1)完全等价。

  3. m [0]给出第一个元素,仍然是一个1D数组。它退化为指向其第一个元素的指针。 int [4]的第一个元素是一个int,因此我们得到了一个指向它的指针,即int*

  4. &m [0] [0]给出第一个数组的第一个元素的指针,即int*。与3)等价。

所有这些指针类型将具有相同的地址,因为2D数组从其第一项 - 1D数组 - 开始的地址相同。而1D数组又从其第一个整数开始的地址相同。
各种不同的指针类型只是高级语言语法。这些类型不是在机器代码中持久存在的东西,在那里一切都只是原始地址。

0

第一个维度将存储子数组(行)的地址。

例如,您有m[2] [4],因此m [0]计算为包含来自m [0] [0]m [0] [3]元素的第一行的地址,而m [1]计算为包含来自m [1] [0]m [1] [3]元素的第二行的地址。

如果您尝试打印m [0]m [1],则会看到地址差异是在m [0]字节处的整个行的总大小。


好的。现在来谈另一个不准确之处:“m[0]存储第一行的地址”,实际上它并没有存储。发生的情况是m[0]“衰变”为第一行的地址。 - alk
我同意m[0]的值不是从存储中读取的,而是在引用时计算得出的。 - Pras

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