数组名称是C中的指针吗?如果不是,数组名称和指针变量之间有什么区别?
&array[0]
产生一个指针,而不是阵列;)
数组名称是C中的指针吗?如果不是,数组名称和指针变量之间有什么区别?
&array[0]
产生一个指针,而不是阵列;)
Answers:
数组是数组,指针是指针,但是在大多数情况下,数组名称会转换为指针。经常使用的术语是它们衰减到指针。
这是一个数组:
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
,必须首先将a
in中的名称a[3]
转换为指针(指向中的第一个元素a
)。然后,我们将3个元素向前移动,并采用其中的任何内容。换句话说:将元素放在数组中的位置3。(这是数组中的第四个元素,因为第一个元素的编号为0。)
因此,总而言之,在大多数情况下,C程序中的数组名称都将转换为指针。一个例外是当我们sizeof
在数组上使用运算符时。如果a
在这种情况下被转换为指针,sizeof a
则将给出指针的大小而不是实际数组的大小,这将是毫无用处的,因此在这种情况下a
意味着数组本身。
functionpointer()
和(*functionpointer)()
意思是一样的,奇怪的。
sizeof()
没有&
示例的其他上下文,其中没有array-> pointer衰减的是operator- 在上面的示例中,&a
将是指向7的数组的int
指针,而不是指向单个的指针int
;也就是说,其类型为int(*)[7]
,不能隐式转换为int*
。这样,函数实际上可以将指针指向特定大小的数组,并通过类型系统强制执行限制。
如果数组类型的表达式(例如数组名称)出现在较大的表达式中,而不是&
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终止符),或者必须将元素数作为单独的参数传递。
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*
)。
a
具有type int [2][3]
,该类型“衰减”为type int (*)[3]
。该表达式*(a + 1)
具有type int [3]
,它“衰减”为int *
。因此,*(*(a + 1) + 2)
将具有type int
。 a
分的前3个元素的数组int
,a + 1
指向的第二3元素数组int
,*(a + 1)
是的第二个3元素数组int
,*(a + 1) + 2
指向的第二阵列的第三个元素int
,因此*(*(a + 1) + 2)
是的所述第二阵列的所述第三元件int
。如何将其映射到机器代码完全取决于编译器。
这样声明的数组
int a[10];
分配内存10 int
s。您无法修改,a
但可以使用进行指针运算a
。
这样的指针仅为该指针分配内存p
:
int *p;
它不分配任何int
s。您可以修改它:
p = a;
并通过以下方式使用数组下标:
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
int
秒的自动存储持续时间。”
数组名称本身会产生一个内存位置,因此您可以将数组名称视为指针:
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]);
我认为这个例子可以阐明这个问题:
#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;
}
尽管c
与a
“点”,以相同的内存,你可以得到的地址c
指针,但你不能获得的地址a
指针。
-std=c11 -pedantic-errors
,则会由于编写无效的C代码而出现编译器错误。原因是因为您尝试将赋给的int (*)[3]
变量int**
,这是两种类型,彼此之间完全没有关系。所以这个例子应该证明什么,我不知道。
int **
类型不是一点上,我们应该更好地利用void *
这一点。
数组名称的行为类似于指针,并指向数组的第一个元素。例:
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
数组是内存中连续和连续元素的集合。在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'
因此,答案是肯定的。
数组名称是数组第一元素的地址。因此,数组名称是const指针。