数组名是一个指针吗?

266

在 C 语言中,数组名不是指针吗? 如果不是,那么数组名和指针变量之间有什么区别?


6
不行。但是array和*&array[0]*是相同的。 - user166390
39
@pst说:&array[0]返回的是指针,不是数组 ;) - jalf
35
@Nava(和pst):array&array[0] 并不完全相同。例如: sizeof(array)sizeof(&array[0]) 返回的结果不同。 - Thomas Padron-McCarthy
2
@Thomas 同意,但就指针而言,当您取消引用数组并使用&array [0] 时,它们产生array [0]的相同值。即* array == array [0]。 没有人想说这两个指针是相同的,但在这种特定情况下(指向第一个元素),您可以使用数组名称。 - Nava Carmon
1
这些也可能有助于您的理解:https://dev59.com/83RC5IYBdhLWcg3wMd9S,https://dev59.com/KHRB5IYBdhLWcg3wUVxd。 - Dinah
显示剩余3条评论
10个回答

322

数组就是数组,指针就是指针,但在大多数情况下,数组名会被“转换”为指针。常用的术语是它们会“退化”成指针。

这里有一个数组:

int a[7];

a 包含七个整数的空间,通过赋值可以在其中一个位置放入一个值,例如:

a[3] = 9;

这里有一个指针:

int *p;

p并不包含整数的任何空间,但它可以指向一个整数的空间。例如,我们可以将其设置为指向数组a中的某个位置,比如第一个位置:

p = &a[0];

有可能会让人感到困惑的是,你也可以这样写:

p = a;

这并不会将数组a的内容复制到指针p中(无论那意味着什么)。相反,数组名a被转换为指向其第一个元素的指针。因此,该赋值与前一个赋值相同。

现在,您可以以类似于数组的方式使用p

p[3] = 17;
这段代码有效的原因在于C语言中的数组解引用操作符[ ]是基于指针定义的。x[y]表示:从指针x开始,在指针所指向的位置后面移动y个元素,然后取得那里的任何内容。使用指针算术语法,x[y]也可以写成*(x+y)
要使这对普通数组(比如我们的a)起作用,a[3]中的名称a必须首先转换为一个指针(指向a中第一个元素)。然后我们向前移动3个元素,并取出那里的任何内容。换句话说,就是取出数组中第3个位置的元素。(由于第一个元素编号为0,所以它是数组中的第4个元素。)
因此,在总结一下,C程序中的数组名(在大多数情况下)会被转换为指针。一个例外是当我们在数组上使用sizeof运算符时。如果在这种情况下将a转换为指针,则sizeof a将给出指针的大小,而不是实际数组的大小,这将相当无用。所以在这种情况下a表示数组本身。

7
函数指针也会被自动转换 - functionpointer()(*functionpointer)()两者的含义是相同的,这有些奇怪。 - Carl Norum
3
他并没有询问数组和指针是否相同,而是询问一个数组的名称是否是一个指针。 - Ricardo Amores
40
数组名不是指针,而是类型为数组的变量的标识符,它具有到元素类型指针的隐式转换。 - Pavel Minaev
37
除了sizeof()以外,另一个不发生数组指针衰减的情况是运算符&。在您上面的示例中,&a将是指向7个int数组的指针,而不是指向单个int的指针;也就是说,它的类型将是int(*)[7],不能隐式转换为int*。这样,函数实际上可以接受特定大小的数组指针,并通过类型系统强制执行限制。 - Pavel Minaev
4
@onmyway133,请点击这里(http://c-faq.com/ptrs/funccall.html)获取简短的解释和更多引用。 - Carl Norum
显示剩余15条评论

50

当数组作为值使用时,其名称表示第一个元素的地址。
当数组不作为值使用时,其名称表示整个数组。

int arr[7];

/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */

/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */

34
如果一个数组类型的表达式(如数组名称)出现在较大的表达式中,并且它不是“&”或“sizeof”运算符的操作数,则数组表达式的类型从“T的N元素数组”转换为“指向T的指针”,并且表达式的值是数组中第一个元素的地址。
简而言之,数组名称不是指针,但在大多数情况下,它被视为指针。
编辑
回答评论中的问题:
如果我使用sizeof,我只计算数组元素的大小吗?那么数组“head”也占用信息长度和指针的空间(这意味着它占用的空间比普通指针还要大)?
当您创建一个数组时,分配的唯一空间是元素本身的空间;不会为单独的指针或任何元数据实现存储。给定
char a[10];

你在内存中得到的是什么

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+
a 表达式表示整个数组,但是除了数组元素本身外,没有单独的 a 对象。因此,sizeof a 给出整个数组的大小(以字节为单位)。表达式 &a 给出数组的地址,该地址与第一个元素的地址相同&a&a[0] 之间的区别是结果类型1,在第一种情况下为 char (*)[10],而在第二种情况下为 char *
当您想要访问单个元素时,情况就变得奇怪起来 - 表达式 a[i] 定义为 *(a + i) 的结果-给定一个地址值 a,从该地址偏移 i 个元素(而不是字节)并解引用结果。
问题在于,a 不是指针或地址-它是整个数组对象。 因此,在 C 中的规则是,每当编译器看到数组类型的表达式(例如具有类型 char [10]a)并且该表达式不是 sizeof 或一元 & 运算符的操作数时,该表达式的类型转换为指针类型(char *),并且表达式的值是数组的第一个元素的地址。 因此,表达式 a 具有与表达式 &a[0](以及由此推出的表达式 *a 具有与表达式 a[0] 相同的类型和值)。
C 衍生自一种称为 B 的早期语言,在 B 中,a 是一个单独的指针对象,而不是数组元素 a [0]a [1] 等。Ritchie 想保持 B 的数组语义,但他不想处理存储单独的指针对象。所以他将其删除了。相反,编译器会在必要时将数组表达式转换为指针表达式。
请记住,我说过数组不存储关于其大小的任何元数据。一旦该数组表达式“衰减”为指针,您只有一个指向单个元素的指针。该元素可能是一系列元素中的第一个元素,也可能是单个对象。仅根据指针本身无法知道它属于哪种情况。
当您将一个数组表达式传递给函数时,函数实际接收到的只是指向第一个元素的指针,它不知道数组的大小(这就是为什么gets函数是如此的危险并最终被从库中删除的原因)。为了让函数知道数组有多少元素,您必须使用哨兵值(例如C字符串中的0终止符)或者将元素数量作为单独的参数传递。
  1. *这可能*会影响地址的解释方式——这取决于计算机。

@AndriyDmytruk:sizeof是一个运算符,它的值是操作数(可以是表示对象的表达式或用括号括起来的类型名称)中的字节数。因此,对于数组,sizeof的值等于元素数量乘以单个元素的字节数。如果int占用4个字节,则包含5个int元素的数组占用20个字节。 - John Bode
1
@Stan:下标操作a[i]的定义为*(a + i) - 给定起始地址a,找到数组中第i个对象的地址,并对结果进行解引用。如果a是数组类型的表达式,则在执行加法之前将其转换为指针。请记住,在指针算术中,a + 1产生指向所指类型的下一个对象的地址,而不是下一个字节的地址。如果a指向4字节的int,那么a + 1指向下一个4字节的int。如果a指向128字节的struct,那么a + 1指向下一个128字节的struct - John Bode
2
@Stan:表达式a的类型为int [2][3],它会“衰减”为类型int (*)[3]。表达式*(a + 1)的类型为int [3],它会“衰减”为int *。因此,*(*(a + 1) + 2)的类型将是inta指向int的第一个3元素数组,a + 1指向int的第二个3元素数组,*(a + 1)就是int的第二个3元素数组,*(a + 1) + 2指向int的第二个数组中的第三个元素,所以*(*(a + 1) + 2)就是int的第二个数组中的第三个元素。如何将其映射到机器代码完全取决于编译器。 - John Bode
我已经阅读了答案,想要澄清一些事情。这个语句-当数组表达式“衰变”为指针时,你所拥有的只是一个指向单个元素的指针。因此,这意味着为了存储数组的基地址,会分配一些内存空间(除了已经为数组元素分配的内存空间之外),对吗? - It's probable
如果它不会腐烂,为什么你说&a[0]和*a的值相同,如果你知道我的意思。 - Paralax01
显示剩余7条评论

6

像这样声明的数组

int a[10];

为10个 int 分配内存。你不能修改 a,但可以使用指针算术运算。

这样的指针只为指针 p 分配内存:

int *p;

它不分配任何int。您可以修改它:

p = a;

您可以像使用 a 一样使用数组下标:

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect

2
数组并不总是分配在堆栈上。这是一个实现细节,会因编译器而异。在大多数情况下,静态或全局数组将从与堆栈不同的内存区域分配。常量类型的数组可能会从另一个内存区域分配。 - Mark Bessey
1
我认为Grumdrig的意思是“分配了10个具有自动存储期限的int”。 - Lightness Races in Orbit

4

数组名本身表示一个内存地址,因此您可以像处理指针一样处理数组名:

int a[7];

a[0] = 1976;
a[1] = 1984;

printf("memory location of a: %p", a);

printf("value at memory location %p is %d", a, *a);

除了可以对指针进行一些有趣的操作(例如添加/减去偏移量),您还可以对数组执行相同的操作:

printf("value at memory location %p is %d", a + 1, *(a + 1));

就编程语言而言,如果C语言没有将数组仅公开为一种"指针"(严谨来说它只是一个内存位置。它不能指向任意内存位置,也不能被程序员控制),我们总是需要编写以下代码:

printf("value at memory location %p is %d", &a[1], a[1]);

这段代码在 sizeof (int*) != sizeof (void*) 的情况下是否会导致 UB?公平地说,我不知道有任何系统出现过这种情况。 - 12431234123412341234123

2
我认为这个例子可以阐明这个问题:

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %d\n", a == b);
        return 0;
}

在gcc 4.9.2中编译正常(有2个警告),并输出以下内容:

a == &a: 1

糟糕的消息是,数组不是指针,它不被存储在内存中(甚至不是只读内存)作为一个指针,即使看起来是这样,因为你可以用 & 运算符获得它的地址。但是 - oops - 这个运算符不起作用 :-)),无论如何,已经警告过你了:

p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
  int **b = &a;
            ^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
  printf("a == &a: %d\n", a == b);

C++在编译时会拒绝任何此类尝试并报错。

编辑:

这是我想要展示的内容:

#include <stdio.h>
int main()
{
    int a[3] = {9, 10, 11};
    void *c = a;

    void *b = &a;
    void *d = &c;

    printf("a == &a: %d\n", a == b);
    printf("c == &c: %d\n", c == d);
    return 0;
}

尽管ca都指向同一块内存,你可以获取c指针的地址,但你无法获取a指针的地址。


2
它编译得很好(有2个警告)。这并不好。如果你告诉gcc通过添加-std=c11 -pedantic-errors来将其编译为适当的标准C,你会因为编写无效的C代码而得到一个编译器错误。原因是因为你试图将int (*)[3]分配给int **类型的变量,这两种类型完全没有任何关系。所以这个例子想要证明什么,我不知道。 - Lundin
谢谢Lundin的评论。你知道有许多标准。 我在编辑中试图澄清我的意思。 int **类型并不是重点,最好使用void * - Palo

2
以下示例提供了数组名称和指针之间的具体区别。假设您想用给定的最大维度表示一条1D线,您可以使用数组或指针来实现:
typedef struct {
   int length;
   int line_as_array[1000];
   int* line_as_pointer;
} Line;

现在让我们来看一下以下代码的行为:

void do_something_with_line(Line line) {
   line.line_as_pointer[0] = 0;
   line.line_as_array[0] = 0;
}

void main() {
   Line my_line;
   my_line.length = 20;
   my_line.line_as_pointer = (int*) calloc(my_line.length, sizeof(int));

   my_line.line_as_pointer[0] = 10;
   my_line.line_as_array[0] = 10;

   do_something_with_line(my_line);

   printf("%d %d\n", my_line.line_as_pointer[0], my_line.line_as_array[0]);
};


这段代码将输出:
0 10

那是因为在对函数 do_something_with_line 进行调用时,对象被复制了,因此:
  1. 指针 line_as_pointer 仍然包含它原本所指向的地址
  2. 数组 line_as_array 被复制到一个新的地址,该地址不会超出函数作用域
因此,虽然当您直接将数组输入函数时它们不会被按值传递,但当您将它们封装在结构体中时,它们会被按值传递(即被复制),这与使用指针的实现相比,在行为上存在重大差异。

0

不是。数组名不是指针。你不能给数组名赋值或修改它,但可以对指针进行操作。

int arr[5];
int *ptr;

/* CAN assign or increment ptr */

ptr = arr;
ptr++;

/* CANNOT assign or increment arr */  

arr = ptr;
arr++;

/* These assignments are also illegal */

arr = anotherarray;
arr = 0;

来自K&R书:

数组名和指针之间有一个必须记住的区别。指针是一个变量,但数组名不是变量。

sizeof是另一个重要的区别。

sizeof(arr); /* size of the entire array */
sizeof(ptr); /* size of the memory address */

在某些情况下,数组的行为类似于或会衰变成指针(&arr[0])。您可以查看其他答案以获取更多示例。以下是其中一些情况的重申:

void func(int *arr) { }
void func2(int arr[]) { }  /* same as func */

ptr = arr + 1;  /* pointer arithmetic */
func(arr);      /* passing to function */

尽管您无法分配或修改数组名称,但当然可以修改数组内容

arr[0] = 1;

0
这是我理解数组和指针之间的区别(以及相似之处)的方式。
int v = 0;

    |       |
    |-------|
100 |       |   Name 1 - v
    |   0   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

Even though *100 is technically not a name, and is instead a unary operator
(i.e. *) along with its operand (i.e. 100), we can still think of it as a name
in order to understand how addresses/pointers work.

Now, &v means the starting address of the variable whose name is v (i.e. 100).
Also, the data type of 100 is int* (i.e. pointer-to-int).

1. printf("%d\n", v)
   is equivalent to printf("%d\n", 0)
   is equivalent to printf("%d\n", *100)
2. v = 10
   is equivalent to *100 = 10
3. v + 1
   is equivalent to 0 + 1
   is equivalent to *100 + 1
4. scanf("%d", &v)
   is equivalent to scanf("%d", &(*100))
   is equivalent to scanf("%d", 100)
5. func(&v)
   is equivalent to func(&(*100))
   is equivalent to func(100)
and so on.

Since the data type of 10 is int, therefore it can be assigned to the variable
whose name is v because that variable's data type is also int.
Similarly, since the data type of 100 is int* (i.e. pointer-to-int), therefore
it can be assigned to a variable whose data type is also int*.

////////////////////////////////////////////////////////////////////////////////

int* ptr = &v; is equivalent to int* ptr = 100;

    |       |
    |-------|
100 |       |   Name 1 - v
    |   0   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

    |       |
    |-------|
200 |       |   Name 1 - ptr
    |       |   Name 2 - *200
    |       |
    |  100  |
    |       |
    |       |
    |       |
207 |       |
    |-------|
    |       |

Now, for eg., when evaluating v + 1, v is equivalent to 0 because 0 is the value
which is stored in the variable whose name is v.
Similarly, when evaluating *ptr, ptr is equivalent to 100 because 100 is the
value which is stored in the variable whose name is ptr.

1. printf("%d\n", v)
   is equivalent to printf("%d\n", 0)
   is equivalent to printf("%d\n", *100)
   is equivalent to printf("%d\n", *ptr)
2. v = 10
   is equivalent to *100 = 10
   is equivalent to *ptr = 10
3. v + 1
   is equivlent to 0 + 1
   is equivalent to *100 + 1
   is equivalent to *ptr + 1
4. scanf("%d", &v)
   is equivalent to scanf("%d", &(*100))
   is equivalent to scanf("%d", 100)
   is equivalent to scanf("%d", &(*ptr))
   is equivalent to scanf("%d", ptr)
5. func(&v)
   is equivalent to func(&(*100))
   is equivalent to func(100)
   is equivalent to func(&(*ptr))
   is equivalent to func(ptr)
and so on.

So, we can also think that the variable whose name is v gets a third name, i.e.
*ptr.

    |       |
    |-------|
100 |       |   Name 1 - v
    |   0   |   Name 2 - *100
    |       |   Name 3 - *ptr
103 |       |
    |-------|
    |       |

Also, to get the address of this variable, we can use &v, 100 or ptr.

////////////////////////////////////////////////////////////////////////////////

int main(void)
{
    int v;
    ...
    func(&v);
    ...
}

void func(int* ptr)
{
    ...
    *ptr = 5;
    ...
}

Here, the function call func(&v) is equivalent to func(100).

So, when func() is executed, the name v of the variable which is local to main()
goes out of scope.

    |       |
    |-------|
100 |       |   Name 1 - xxxx
    |   0   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

Now, a variable which is local to func() whose name is ptr and whose data type
is pointer-to-int is created.

    |       |
    |-------|
200 |       |   Name 1 - ptr
    |       |   Name 2 - *200
    |       |
    |  100  |
    |       |
    |       |
    |       |
207 |       |
    |-------|
    |       |

Now, the variable which is local to main() gets a new name.

    |       |
    |-------|
100 |       |   Name 1 - xxxx
    |   0   |   Name 2 - *100
    |       |   Name 3 - *ptr
103 |       |
    |-------|
    |       |

And, to get the address of the variable local to main(), we can use 100 or ptr.

After executing *ptr = 5, when func() returns, the variable which is local to
func() and whose name is ptr is destroyed, and the name v of the variable which
is local to main() is restored.
Also, since the variable which is local to func() and whose name is ptr is
destroyed, therefore the name *ptr of the variable which is local to main() is
also destroyed.

    |       |
    |-------|
100 |       |   Name 1 - v
    |   5   |   Name 2 - *100
    |       |
103 |       |
    |-------|
    |       |

////////////////////////////////////////////////////////////////////////////////

int arr[3] = {7, 8, 9};

arr is the name of the entire array.

    |       |
    |-------|
300 |       |   Name - *300
    |   7   |
    |       |
303 |       |
    |-------|
304 |       |   Name - *304
    |   8   |
    |       |
307 |       |
    |-------|
308 |       |   Name - *308
    |   9   |
    |       |
311 |       |
    |-------|
    |       |

When the expression v + 1.0 is evaluated, the data type of v is converted from
int to double only for the purpose of evaluating that expression.

Similarly, except for those 4 cases (&, sizeof, alignof, string literal used to
initialize an array), arr is converted to the address of the first element of
the array only for the purpose of evaluating the corresponding expression.

Also, for eg., if the array consists of elements of data type int, then the
data type of the resultant address is pointer-to-int. So, the resultant address
can be assigned to a variable of data type pointer-to-int.
For eg., int* ptr = arr;

So, in other words, except for those 4 cases, arr is converted to a pointer to
the first element of the array only for the purpose of evaluating the
corresponding expression.

When an integer is added to an address/pointer, the resultant address is
calculated according to the data type of the address/pointer.
For eg., 300 + 1 gives 304.

arr[i] is equivalent to *(arr + i).

Now, arr[1]
     is equivalent to *(arr + 1)
     is equivalent to *(300 + 1)
     is equivalent to *304

And, &(arr[1])
     is equivalent to &(*(arr + 1))
     is equivalent to &(*(300 + 1))
     is equivalent to &(*304)
     is equivalent to 304

Also, arr + 1
      is equivalent to 300 + 1
      is equivalent to 304

1. printf("%d\n", arr[1])
   is equivalent to printf("%d\n", 8)
   is equivalent to printf("%d\n", *(arr + 1))
   is equivalent to printf("%d\n", *304)
2. arr[1] = 10
   is equivalent to *(arr + 1) = 10
   is equivalent to *304 = 10
3. arr[1] + 1
   is equivalent to 8 + 1
   is equivalent to *(arr + 1) + 1
   is equivalent to *304 + 1
4. scanf("%d", &(arr[1]))
   is equivalent to scanf("%d", &(*(arr + 1)))
   is equivalent to scanf("%d", &(*304))
   is equivalent to scanf("%d", 304)
   is equivalent to scanf("%d", arr + 1)
5. func(&(arr[1]))
   is equivalent to func(&(*(arr + 1)))
   is equivalent to func(&(*304))
   is equivalent to func(304)
   is equivalent to func(arr + 1)
and so on.

So, we can also think that the elements of the array have other names.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |   7   |   Name 2 - arr[0]
    |       |   Name 3 - *(arr + 0)
303 |       |
    |-------|
304 |       |   Name 1 - *304
    |   8   |   Name 2 - arr[1]
    |       |   Name 3 - *(arr + 1)
307 |       |
    |-------|
308 |       |   Name 1 - *308
    |   9   |   Name 2 - arr[2]
    |       |   Name 3 - *(arr + 2)
311 |       |
    |-------|
    |       |

Also, to get the address of, for eg., arr[1], we can use &(arr[1]), 304 or
arr + 1.

////////////////////////////////////////////////////////////////////////////////

int main(void)
{
    int arr[3];
    ...
    func(arr);
    ...
}

void func(int* arr)
{
    ...
    arr[1] = 6;
    ...
}

Here, the function call func(arr) is equivalent to func(300).

So, when func() is executed, the name arr of the entire array which is local to
main() goes out of scope, which means that Name 2 and Name 3 of every element of
the array also go out of scope.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |   7   |   Name 2 - xxxx
    |       |   Name 3 - xxxx
303 |       |
    |-------|
304 |       |   Name 1 - *304
    |   8   |   Name 2 - xxxx
    |       |   Name 3 - xxxx
307 |       |
    |-------|
308 |       |   Name 1 - *308
    |   9   |   Name 2 - xxxx
    |       |   Name 3 - xxxx
311 |       |
    |-------|
    |       |

Now, a variable which is local to func() whose name is arr and whose data type
is pointer-to-int is created.

    |       |
    |-------|
400 |       |   Name 1 - arr
    |       |   Name 2 - *400
    |       |
    |  300  |
    |       |
    |       |
    |       |
407 |       |
    |-------|
    |       |

It should be noted that when the expression arr[1] = 6 is evaluated inside
func(), arr isn't the name of the array which is local to main(), and is instead
the name of the variable which is local to func().
So, there is no need to convert arr to a pointer, as arr is already a pointer.

Now, similar to main(), inside func() also
arr[1]
is equivalent to *(arr + 1)
is equivalent to *(300 + 1)
is equivalent to *304

And, &(arr[1])
     is equivalent to &(*(arr + 1))
     is equivalent to &(*(300 + 1))
     is equivalent to &(*304)
     is equivalent to 304

Also, arr + 1
      is equivalent to 300 + 1
      is equivalent to 304

So, the elements of the array which is local to main() get new names.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |       |   Name 2 - xxxx
    |   7   |   Name 3 - xxxx
    |       |   Name 4 - arr[0]
303 |       |   Name 5 - *(arr + 0)
    |-------|
304 |       |   Name 1 - *304
    |       |   Name 2 - xxxx
    |   8   |   Name 3 - xxxx
    |       |   Name 4 - arr[1]
307 |       |   Name 5 - *(arr + 1)
    |-------|
308 |       |   Name 1 - *308
    |       |   Name 2 - xxxx
    |   9   |   Name 3 - xxxx
    |       |   Name 4 - arr[2]
311 |       |   Name 5 - *(arr + 2)
    |-------|
    |       |

And, to get the address of, for eg., arr[1], we can use &(arr[1]), 304 or
arr + 1.

After executing arr[1] = 6, when func() returns, the variable which is local to
func() and whose name is arr is destroyed, and the name arr of the entire array
which is local to main() is restored, which means that Name 2 and Name 3 of
every element of the array are also restored.
Also, since the variable which is local to func() and whose name is arr is
destroyed, therefore Name 4 and Name 5 of every element of the array are also
destroyed.

    |       |
    |-------|
300 |       |   Name 1 - *300
    |   7   |   Name 2 - arr[0]
    |       |   Name 3 - *(arr + 0)
303 |       |
    |-------|
304 |       |   Name 1 - *304
    |   6   |   Name 2 - arr[1]
    |       |   Name 3 - *(arr + 1)
307 |       |
    |-------|
308 |       |   Name 1 - *308
    |   9   |   Name 2 - arr[2]
    |       |   Name 3 - *(arr + 2)
311 |       |
    |-------|
    |       |


-3
数组名的行为类似于指针,指向数组的第一个元素。例如:
int a[]={1,2,3};
printf("%p\n",a);     //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0

对于机器来说,这两个打印语句将会给出完全相同的输出。在我的系统中,它输出了:

0x7fff6fe40bc0

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