数组名称是指针吗?


203

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


4
否。但是数组是相同的&array [0]

36
@pst:&array[0]产生一个指针,而不是阵列;)
jalf

28
@Nava(和pst):array&array [0]并不完全相同。恰当的例子:sizeof(array)sizeof(&array [0])给出不同的结果。
Thomas Padron-McCarthy

1
@Thomas同意,但是就指针而言,当取消引用array和&array [0]时,它们产生的值为array [0]。即* array == array [0]。没有人意味着这两个指针是相同的,但是在这种特定情况下(指向第一个元素),您可以使用数组的名称。
纳瓦·卡蒙

Answers:


255

数组是数组,指针是指针,但是在大多数情况下,数组名称会转换为指针。经常使用的术语是它们衰减到指针。

这是一个数组:

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)

为了使其与普通数组(例如our)一起使用a,必须首先将ain中的名称a[3]转换为指针(指向中的第一个元素a)。然后,我们将3个元素向前移动,并采用其中的任何内容。换句话说:将元素放在数组中的位置3。(这是数组中的第四个元素,因为第一个元素的编号为0。)

因此,总而言之,在大多数情况下,C程序中的数组名称都将转换为指针。一个例外是当我们sizeof在数组上使用运算符时。如果a在这种情况下被转换为指针,sizeof a则将给出指针的大小而不是实际数组的大小,这将是毫无用处的,因此在这种情况下a意味着数组本身。


5
类似的自动转换适用于函数指针-既functionpointer()(*functionpointer)()意思是一样的,奇怪的。
卡尔·诺鲁姆

3
他没有询问数组和指针是否相同,但是数组的名称是否是指针
Ricardo Amores

32
数组名称不是指针。它是类型为array的变量的标识符,该变量具有对元素类型的指针的隐式转换。
帕维尔米纳夫

29
另外,除了上面sizeof()没有&示例的其他上下文,其中没有array-> pointer衰减的是operator- 在上面的示例中,&a将是指向7的数组的int指针,而不是指向单个的指针int;也就是说,其类型为int(*)[7],不能隐式转换为int*。这样,函数实际上可以将指针指向特定大小的数组,并通过类型系统强制执行限制。
帕维尔·米纳夫

3
@ onmyway133,在此处查看简短说明和更多引文。
卡尔·诺鲁姆

36

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

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 */

20

如果数组类型的表达式(例如数组名​​称)出现在较大的表达式中,而不是&or 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元素(而不是bytes)并取消对结果的引用。

问题在于这a不是指针或地址,而是整个数组对象。因此,C语言中的规则是,只要编译器看到数组类型的表达式(例如a,具有type char [10]并且该表达式不是the sizeof或unary运算&符的操作数,该表达式的类型就会被转换(“ decays”)指向指针类型(char *),表达式的值就是数组第一个元素的地址。因此,表达式 a具有与表达式相同的类型和值&a[0](并且通过扩展,表达式*a具有与expression 相同的类型和值a[0])。

C由称为B更早的语言导出,B中a 从数组元素一个单独的指针对象a[0]a[1]等里奇想保持B的数组语义,但他不想惹存储单独的指针对象。所以他摆脱了它。相反,编译器将在转换过程中根据需要将数组表达式转换为指针表达式。

记住,我说过数组不存储任何有关其大小的元数据。只要该数组表达式“衰减”到指针,您所拥有的就是指向单个元素的指针。该元素可以是元素序列中的第一个,也可以是单个对象。无法基于指针本身来了解。

当您将数组表达式传递给函数时,函数收到的所有内容都是指向第一个元素的指针-它不知道数组有多大(这就是为什么gets函数如此危险并最终从库中删除的原因)。为了使函数知道数组有多少个元素,必须使用哨兵值(例如C字符串中的0终止符),或者必须将元素数作为单独的参数传递。


  1. 哪个*可能*影响地址值的解释方式-取决于机器。

一直在寻找这个答案已经很长时间了。谢谢!而且,如果您知道,您能否进一步讲一下数组表达式是什么。如果我使用sizeof,是否只计算数组元素的大小?然后,数组“ head”还占用了有关长度和指针的信息的空间(这意味着它比普通指针要占用更多的空间)?
Andriy Dmytruk

还有一件事情。长度为5的数组的类型为int [5]。那就是从哪里我们知道调用sizeof(array)的长度-从它的类型?这意味着不同长度的数组就像不同类型的常量吗?
Andriy Dmytruk

@AndriyDmytruk:sizeof是一个运算符,其结果为操作数中的字节数(表示对象的表达式或括号中的类型名称)。因此,对于一个数组,sizeof求和结果为元素数乘以单个元素中的字节数。如果an int为4字节宽,则5个元素的数组将int占用20个字节。
约翰·博德

接线员[ ]也不很特别吗?例如,,int a[2][3];然后for x = a[1][2];,尽管可以将其重写为x = *( *(a+1) + 2 );,但此处a不会转换为指针类型int*(尽管如果a是函数的参数,则应将其转换为int*)。
斯坦(Stan)

2
@Stan:表达式a具有type int [2][3],该类型“衰减”为type int (*)[3]。该表达式*(a + 1)具有type int [3],它“衰减”为int *。因此,*(*(a + 1) + 2)将具有type inta分的前3个元素的数组inta + 1指向的第二3元素数组int*(a + 1) 的第二个3元素数组int*(a + 1) + 2指向的第二阵列的第三个元素int,因此*(*(a + 1) + 2) 的所述第二阵列的所述第三元件int。如何将其映射到机器代码完全取决于编译器。
John Bode

5

这样声明的数组

int a[10];

分配内存10 ints。您无法修改,a但可以使用进行指针运算a

这样的指针仅为该指针分配内存p

int *p;

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

p = a;

并通过以下方式使用数组下标:

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

2
数组并不总是分配在堆栈上。是的,实现细节因编译器而异。在大多数情况下,静态或全局数组将从与堆栈不同的内存区域分配。常量类型的数组可以从内存又一区域分配
马克·贝西

1
我认为Grumdrig的意思是“分配10 int秒的自动存储持续时间。”
在轨道

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

1

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

#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

哎呀:-)

因此,结论是否定的,数组不是指针,即使看起来像数组,它也不会作为指针存储在内存中(甚至不是只读的),因为您可以使用&运算符获取其地址。但是-糟糕-该运算符不起作用:-)),无论哪种方式,都已警告您:

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指针。


1
“它编译正常(带有2条警告)”。不好 如果通过添加告诉gcc将其编译为适当的标准C -std=c11 -pedantic-errors,则会由于编写无效的C代码而出现编译器错误。原因是因为您尝试将赋给的int (*)[3]变量int**,这是两种类型,彼此之间完全没有关系。所以这个例子应该证明什么,我不知道。
隆丁'18

谢谢伦丁的评论。您知道有很多标准。我试图澄清我在编辑中的意思。该int **类型不是一点上,我们应该更好地利用void *这一点。
帕洛阿尔托

-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

-4

数组是内存中连续和连续元素的集合。在C中,数组的名称是第一个元素的索引,应用偏移量可以访问其余元素。“第一个元素的索引”确实是指向存储方向的指针。

与指针变量的区别在于,您无法更改数组名称指向的位置,因此类似于const指针(相似,不同之处。请参见Mark的评论)。而且,如果使用指针算术,则无需取消引用数组名称即可获取值:

char array = "hello wordl";
char* ptr = array;

char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'

因此,答案是肯定的。


1
数组名称与const指针不同。给出:int a [10]; int * p = a; sizeof(p)和sizeof(a)不同。
Mark Bessey,2009年

1
还有其他区别。通常,最好坚持使用C标准所使用的术语,它专门将其称为“转换”。Quote:“除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则类型为“ array of type”的表达式将转换为类型为' “指向类型的指针”,它指向数组对象的初始元素,而不是左值。如果数组对象具有寄存器存储类,则行为未定义。
帕维尔·米纳夫

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.