数组名是指针吗?

Posted

技术标签:

【中文标题】数组名是指针吗?【英文标题】:Is an array name a pointer? 【发布时间】:2009-10-29 06:38:05 【问题描述】:

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

【问题讨论】:

没有。但是 array 是一样的 &array[0] @pst: &array[0] 产生一个指针,而不是一个数组;) @Nava(和 pst):array&array[0] 并不完全相同。举个例子:sizeof(array)sizeof(&array[0]) 给出不同的结果。 @Thomas 同意,但就指针而言,当您取消引用数组和 &array[0] 时,它们会产生相同的数组 [0] 值。即*数组==数组[0]。没有人说这两个指针是相同的,但是在这种特定情况下(指向第一个元素),您也可以使用数组的名称。 这些也可能有助于您理解:***.com/questions/381542,***.com/questions/660752 【参考方案1】:

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

这是一个数组:

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。)

因此,总而言之,C 程序中的数组名(在大多数情况下)被转换为指针。一个例外是当我们在数组上使用sizeof 运算符时。如果a 在此上下文中被转换为指针,sizeof a 将给出指针的大小而不是实际数组的大小,这将是相当无用的,所以在这种情况下a 表示数组本身。

【讨论】:

对函数指针应用了类似的自动转换——functionpointer()(*functionpointer)() 意思相同,很奇怪。 他没有问数组和指针是否相同,而是问数组的名字是否是指针 数组名不是指针。它是数组类型变量的标识符,它隐式转换为元素类型的指针。 此外,除了sizeof(),另一个没有数组的上下文->指针衰减是运算符& - 在上面的示例中,&a 将是一个指向数组的指针7 int,不是指向单个int 的指针;也就是说,它的类型将是int(*)[7],它不能隐式转换为int*。这样,函数实际上可以将指针指向特定大小的数组,并通过类型系统强制执行限制。 @onmyway133,查看here 以获得简短的解释和进一步的引用。【参考方案2】:

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

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

【讨论】:

【参考方案3】:

如果数组类型的表达式(例如数组名​​称)出现在更大的表达式中,并且它不是 &sizeof 运算符的操作数,则数组表达式的类型从“T的N元素数组”到“指向T的指针”,表达式的值是数组中第一个元素的地址。

简而言之,数组名不是指针,但在大多数情况下,它被视为就像它是一个指针。

编辑

回答评论中的问题:

如果我使用 sizeof,我是否只计算数组元素的大小?那么数组“head”也占用空间,包含长度和指针信息(这意味着它比普通指针占用更多空间)?

创建数组时,唯一分配的空间是元素本身的空间;没有为单独的指针或任何元数据实现存储。给定

char a[10];

你在记忆中得到的是

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+

表达式 a 指的是整个数组,但没有object a 与数组元素本身分开。因此,sizeof a 为您提供了整个数组的大小(以字节为单位)。表达式&a 为您提供数组的地址,与第一个元素的地址相同&a&a[0] 之间的区别在于结果的类型1 - 第一种情况下的char (*)[10] 和第二种情况下的char *

当你想要访问单个元素时,事情变得奇怪了——表达式a[i]被定义为*(a + i)的结果——给定地址值a,偏移量i元素(不是字节) 从该地址并取消引用结果。

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

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

请记住,我说过数组不存储有关其大小的任何元数据。一旦该数组表达式“衰减”为指针,您所拥有的只是一个指向单个元素的指针。该元素可能是一系列元素中的第一个,也可能是单个对象。没有办法根据指针本身知道。

当您将数组表达式传递给函数时,函数接收到的所有内容都是指向第一个元素的指针 - 它不知道数组有多大(这就是为什么 gets 函数如此威胁并最终从图书馆中删除)。要让函数知道数组有多少元素,您必须使用标记值(例如 C 字符串中的 0 终止符),或者必须将元素数量作为单独的参数传递。


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

【讨论】:

寻找这个答案已经很久了。谢谢!如果您知道,您能否进一步说明什么是数组表达式。如果我使用 sizeof,我是否只计算数组元素的大小?那么数组“head”也会占用空间,其中包含有关长度和指针的信息(这意味着它比普通指针占用更多空间)? 还有一件事。长度为 5 的数组的类型为 int[5]。所以这就是我们在调用 sizeof(array) 时知道长度的地方 - 从它的类型?这意味着不同长度的数组就像不同类型的常量? @AndriyDmytruk: sizeof 是一个运算符,它的计算结果是操作数中的 bytes 数字(表示对象的表达式或括号中的类型名称) .因此,对于数组,sizeof 的计算结果为元素数乘以单个元素中的字节数。如果 int 的宽度为 4 个字节,则 int 的 5 元素数组占用 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) 将具有类型 inta指向int的第一个三元数组,a + 1指向int的第二个三元数组,*(a + 1)的第二个三元数组int*(a + 1) + 2指向int第二个数组的第三个元素,所以*(*(a + 1) + 2)int第二个数组的第三个元素。如何映射到机器代码完全取决于编译器。【参考方案4】:

这样声明的数组

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

【讨论】:

数组并不总是分配在堆栈上。 Yhat 是一个因编译器而异的实现细节。在大多数情况下,静态或全局数组将从与堆栈不同的内存区域分配。 const 类型的数组可以从另一个内存区域分配 我认为 Grumdrig 的意思是“使用自动存储持续时间分配 10 个 ints ”。【参考方案5】:

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

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吗?公平地说,我不知道有任何系统会出现这种情况。【参考方案6】:

我认为这个例子说明了这个问题:

#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指针的地址。

【讨论】:

“它编译得很好(带有 2 个警告)”。那不好。如果您告诉 gcc 通过添加 -std=c11 -pedantic-errors 将其编译为正确的标准 C,您会因为编写无效的 C 代码而收到编译器错误。原因是因为您尝试将int (*)[3] 分配给int** 的变量,这两种类型彼此完全没有关系。所以这个例子应该证明什么,我不知道。 感谢伦丁的评论。你知道有很多标准。我试图澄清我在编辑中的意思。 int ** 类型不是重点,最好使用 void *【参考方案7】:

以下示例提供了数组名称和指针之间的具体区别。假设您想用给定的最大维度表示一维线,您可以使用数组或指针来完成:

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的函数调用中,对象被复制了,所以:

    指针line_as_pointer 仍然包含它指向的相同地址 数组line_as_array 被复制到一个新地址,该地址不超出函数的范围

因此,当您将数组直接输入到函数时,虽然数组不是由值给出的,但当您将它们封装在结构中时,它们是由值给出(即复制),这在这里概述了与使用指针的实现相比在行为上的主要区别。

【讨论】:

【参考方案8】:

数组名的行为类似于指针,指向数组的第一个元素。示例:

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

【讨论】:

【参考方案9】:

数组名是数组第一个元素的地址。所以是的,数组名是一个常量指针。

【讨论】:

以上是关于数组名是指针吗?的主要内容,如果未能解决你的问题,请参考以下文章

数组指针是二级指针吗?请解释一下!谢谢!

C语言中的二维数组名是一个二重指针吗?

c++二维数组和二级指针

指针数组与二级指针问题

C++ 指针与数组

4-指针和数组