C 数组名是指针吗?

C 数组名是指针吗?,c,arrays,pointers,C,Arrays,Pointers,数组的名称在C中是指针吗? 如果不是,数组名称和指针变量之间有什么区别?数组是数组,指针是指针,但在大多数情况下,数组名称转换为指针。经常使用的一个术语是,它们衰减为指针 这是一个数组: int a[7]; p[3] = 17; printf("value at memory location %p is %d", a + 1, *(a + 1)); a包含七个整数的空间,您可以通过赋值将值放入其中一个整数中,如下所示: a[3] = 9; 这是一个指针: int *p; int a[

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

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

这是一个数组:

int a[7];
p[3] = 17;
printf("value at memory location %p is %d", a + 1, *(a + 1));
a包含七个整数的空间,您可以通过赋值将值放入其中一个整数中,如下所示:

a[3] = 9;
这是一个指针:

int *p;
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);
p不包含任何整数的空格,但它可以指向整数的空格。例如,我们可以将其设置为指向数组a中的一个位置,例如第一个位置:

p = &a[0];
令人困惑的是,你也可以这样写:

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

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

int a[7];
p[3] = 17;
printf("value at memory location %p is %d", a + 1, *(a + 1));
这样做的原因是C中的数组解引用操作符[]是根据指针定义的。x[y]的意思是:从指针x开始,在指针指向的元素之后向前移动y个元素,然后取其中的任何元素。使用指针算术语法,x[y]也可以写成*x+y

为了使用普通数组(如a),必须首先将[3]中的名称a转换为指向a中第一个元素的指针。然后我们向前迈出3步,拿走所有的东西。换句话说:在数组中的位置3获取元素。这是数组中的第四个元素,因为第一个元素的编号为0


总之,C程序中的数组名在大多数情况下都转换为指针。一个例外是在数组上使用sizeof运算符。如果在此上下文中将a转换为指针,那么sizeof a将给出指针的大小,而不是实际数组的大小,这将非常无用,因此在这种情况下,a表示数组本身。

这样声明的数组

int a[10];
为10整数分配内存。不能修改a,但可以使用a执行指针算术

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

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

p = a;
并尽可能使用数组下标:

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

数组名称本身会产生一个内存位置,因此可以将数组名称视为指针:

int *p;
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);
以及其他可以对指针执行的漂亮操作,例如添加/减去偏移量,也可以对数组执行以下操作:

int a[7];
p[3] = 17;
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]);

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

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

如果数组类型的表达式(如数组名称)出现在较大的表达式中,并且它不是&或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]之间的区别在于第一种情况下result1-char*[10]的类型和第二种情况下char*的类型

奇怪的是,当你想访问单个元素时——表达式a[i]被定义为*a+i的结果——给定一个地址值a,偏移量i元素不是该地址的字节,然后取消对结果的引用

问题在于a不是指针或地址,而是整个数组对象。因此,C语言中的规则是,每当编译器看到一个数组类型的表达式,如a,它的类型为char[10],并且该表达式不是sizeof或一元&运算符的操作数时,该表达式的类型将衰减为指针类型char*,表达式的值是a的第一个元素的地址 雷。因此,表达式a具有与表达式a[0]相同的类型和值,并且通过扩展,表达式*a具有与表达式a[0]相同的类型和值

C源于早期的一种称为B的语言,在B中,a是一个独立于数组元素a[0],a[1]等的指针对象。Ritchie想保留B的数组语义,但他不想弄乱单独的指针对象的存储。所以他把它扔掉了。相反,编译器将根据需要在转换期间将数组表达式转换为指针表达式

请记住,我说过数组不存储关于其大小的任何元数据。一旦数组表达式衰减为指针,您就只有一个指向单个元素的指针。该元素可以是一系列元素中的第一个,也可以是单个对象。无法根据指针本身来判断

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

哪个*可能*影响地址值的解释方式-取决于机器。
数组名称的行为类似于指针,并指向数组的第一个元素。例如:

int a[]={1,2,3};
printf("%p\n",a);     //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0
两个print语句将为机器提供完全相同的输出。在我的系统中,它给出了:

0x7fff6fe40bc0

数组名称是数组中第一个元素的地址。因此,yes array name是一个常量指针。

我认为这个示例可以说明这个问题:

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

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

因此,结论是否定的,数组不是指针,它不是存储在内存中的指针,甚至不是只读的指针,尽管它看起来像是指针,因为您可以使用&运算符获取它的地址。但是-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;
}

即使c和a指向同一内存,您也可以获取c指针的地址,但无法获取指针的地址。

以下示例提供了数组名和指针之间的具体区别。假设您想用某个给定的最大维数表示一条1D线,可以使用数组或指针:

int *p;
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);
类型定义结构{ 整数长度; int line_as_数组[1000]; int*线作为指针; }线路; 现在让我们看看以下代码的行为:

void用线做某事{ line.line_作为_指针[0]=0; line.line作为数组[0]=0; } 真空总管{ 我的线; my_line.length=20; my_line.line_as_pointer=int*callocmy_line.length,sizeofint; my_line.line_as_指针[0]=10; my_line.line_as_数组[0]=10; 用我的线做点什么; printf%d%d\n,my_line.line_as_指针[0],my_line.line_as_数组[0]; }; 此代码将输出:

0 10
这是因为在函数调用中,对象被复制,以便使用\u行执行\u某事\u:

指针行_as_指针仍然包含它指向的相同地址 数组行作为数组被复制到一个新地址,该地址不超出函数的作用域
因此,当您直接将数组输入函数时,数组不是由值给出的,当您将它们封装在结构中时,它们是由值给出的,即复制的,这在这里概括了与使用指针的实现相比在行为上的一个主要区别。

否。但是数组是相同的&array[0]@pst:&array[0]生成指针,而不是数组@Nava和pst:array和&array[0]实际上并不相同。举个例子:sizeofarray和sizeof&array[0]给出了不同的结果。@Thomas同意,但就指针而言,当您取消对array和&array[0]的引用时,它们会产生与array[0]相同的值。即*数组==数组[0]。没有人认为这两个指针是相同的,但是在这个特定的例子中,指向第一个元素,你也可以使用数组的名称。这也可能有助于你理解:,一个类似的自动转换应用于函数指针-functionpointer和*functionpointer都意味着相同的东西,奇怪的是,他没有问数组和指针是否相同,但是如果数组的名称是指针,而数组的名称不是指针。它是数组类型变量的标识符,该变量隐式转换为元素类型的指针。此外,除了sizeof之外,没有数组->指针衰减的另一个上下文是运算符-,在上面的示例中,&a将是指向7 int数组的指针,而不是指向单个int的指针;也就是说,它的类型将是int*[7],不能隐式转换为int*。通过这种方式,函数实际上可以获取指向特定大小数组的指针,并强制执行其余部分
通过类型系统进行排序。@onmyway133,检查简短的解释和进一步的引用。数组并不总是在堆栈上分配的。Yhat是一个具体的实现细节,不同的编译器会有所不同。在大多数情况下,静态或全局数组将从与堆栈不同的内存区域分配。const类型的数组可能是从另一个内存区域分配的。我想Grumdrig的意思是用自动存储持续时间分配10个整数。非常感谢。如果你知道的话,你能告诉我数组表达式是什么吗。如果使用sizeof,是否只计算数组元素的大小?然后数组“head”也会占用空间,包含关于长度和指针的信息,这意味着它比普通指针占用更多的空间,还有一件事。长度为5的数组的类型为int[5]。这就是我们称之为sizeofarray时的长度——从它的类型?这意味着不同长度的数组就像不同类型的常量一样?@AndriyDmytruk:sizeof是一个运算符,它的计算结果是操作数中的字节数,或者是表示对象的表达式,或者是括号中的类型名。因此,对于数组,sizeof的计算结果是元素数乘以单个元素中的字节数。如果一个int是4字节宽的,那么一个5元素的int数组会占用20个字节。操作符[]也很特别吗?例如,int a[2][3];,那么对于x=a[1][2];,虽然可以重写为x=**a+1+2;,这里a没有转换为指针类型int*,但如果a是函数的参数,则应将其转换为int*@Stan:表达式a的类型为int[2][3],其衰减为int*[3]类型。表达式*a+1具有int[3]类型,该类型衰减为int*。因此,**a+1+2将具有int类型。a指向int的第一个3元素数组,a+1指向int的第二个3元素数组,*a+1是int的第二个3元素数组,*a+1+2指向int的第二个数组的第三个元素,因此**a+1+2是int的第二个数组的第三个元素。如何将其映射到机器代码完全取决于编译器。它可以编译,但有两个警告。那不好。如果您通过添加-std=c11-pedantic错误告诉gcc将其编译为适当的标准C,那么编写无效的C代码会导致编译器错误。原因是您试图将int*[3]赋值给int**变量,这两种类型彼此完全无关。所以这个例子应该证明什么,我不知道。谢谢伦丁的评论。你知道有很多标准。我试图澄清我在编辑中的意思。int**类型不是这里的重点,最好使用void*。