繁体   English   中英

为什么解除指向一个整数数组(在2d数组中)的指针返回(或衰减到)指向第一个元素的指针?

[英]why does derefrencing a pointer to an array of integers(in 2d array) return(or decay to) pointer to first element?

我已经阅读了许多指针和二维数组关系的帖子,但我似乎无法理解概念。 假设有一个2d数组int a[3][2]和数组int b[2]
现在a返回一个指向大小为3的整数数组的指针。它的类型为int (*)[2]

正如我对这个概念的理解所做的那样( *a )会给我数组本身, 这会衰减到指向1d数组的第一个元素并且类型为(int *)的指针 现在大胆的部分是怀疑。

为什么以及如何在数组本身(这是完整的1d数组a [0])衰减到1d数组的第一个元素时发生这种衰变?
(似乎无法得到原因和部分)并且似乎无法在其他链接上找到它。

作为a[0]*a&a[0][0]表示相同的指针。 这里作为[0]是第一个1d数组。 那么这是真的, b (在上面声明)和a[0]代表相同的东西(都是1d数组然后衰减到指向类型(int *)数组中第一个元素的指针?

为什么以及如何在数组本身衰减到1d数组的第一个元素时发生这种衰变?

C11:6.3.2.1 p(3):

除非它是sizeof运算符的操作数, _Alignof运算符1 ,一元&运算符,或者是用于初始化数组的字符串文字,具有类型''数组类型''的表达式将转换为表达式输入''指向类型'的指针,指向数组对象的初始元素,而不是左值。

简单; 数组名称可以转换为指向其第一个元素的指针。

由于a是一个数组名称,它衰减到指向其第一个元素的指针,该元素是一维数组。 它的类型是int(*)[2] 由于a (后衰减)是指向的数组2 int*a是阵列2 int秒。 由于这是一个数组类型,即*a是第一个1D数组的数组名称 ,它衰减到指向数组对象的第一个元素( a[0][0] )的指针。 所以,它的类型是int *

这是真的, b (在上面声明)和a[0]代表相同的东西(都是1d数组,然后衰减到指向类型( int* )数组中第一个元素的指针?

ba[0]都是int *类型(衰变后)。


1.阅读Keith Thompson的commat。 还要阅读这个答案 ,其中指出: C11标准的草案说数组有另一个例外,即当数组是新_Alignof运算符的操作数时。 这是草案中的错误,在最终公布的C11标准中得到纠正; _Alignof只能应用于带括号的类型名称,而不能应用于表达式。

你的“为什么”问题的答案是:因为语言规范是这样说的。 数组总是衰减到指向它们的第一个元素(除了许多特定的上下文)。 所以,正如你正确指出的那样*a有类型int [2] 这是一个数组。 并且因为它是一个数组,所以语言需要衰减到指向(*a)[0]的类型int *指针(同样,除少数特定的上下文之外)。 你的原始a是一个int [3][2]类型的数组,衰减到int (*)[2]指针。 同时*a是一个int [2]类型的数组,衰减到int *指针。 这就是为什么。

至于“如何”,目前尚不清楚你究竟在问什么。 在这种情况下,“衰减”意味着在表达式中,数组类型的值被指针类型的值隐式替换。 这里的所有都是它的。

是的,你的例子中a[0]b都是int [2]类型的数组,它们与int *类型的指针相同地衰减。 即它们在衰变行为方面是“同样的事情”。

指定此转换的规则隐藏在C11第6.3.2节中:

...具有类型“数组类型”的表达式将转换为类型为“指向类型的指针”的表达式,该表达式指向数组对象的初始元素,而不是左值。

这意味着如果你有一个表达式,其中使用int b[3]的数组名称b ,它将被转换为指向int的指针,即int*

此规则是通用的。 如果你有一个数组int a[3][2] ,那么正式你有一个“大小为3的大小为2的int数组”。 这将转换为指向type的指针,它是“指向int类型为2的数组的指针”, int(*)[2]

请注意,“衰减”仅“向下”一个级别,因此通过使用二维数组名称a ,您将始终获得数组指针而不是指向第一个元素的指针。 数组指针和普通int指针之间的区别主要是风格,以保持语言的一致性。 如果将指针的实际地址打印为整数,则无论指针类型如何都是相同的。

所以我对这个概念的理解就是解除它(*a)会给我数组本身,并且这会衰减到指向1d数组的第一个元素并且类型为(int*)的指针

它将为您提供数组本身,它将衰减为数组指针。 它不会再从那里腐烂,只有一个层次。 所以*a不会给你一个指向第一个元素的指针。

为什么以及如何在数组本身衰减到1d数组的第一个元素时发生这种衰变?

无论何时在表达式中使用数组名称,它都会衰减,从“数组到类型”到“指向类型的指针”。 如果“type”恰好是一个数组,则会得到一个指向数组的指针。

还有一个疑问。 作为a[0]*a&a[0][0]表示相同的指针。 这里作为[0]是第一个1d数组。

a[0]*a是等价的,两者都给出一个数组指针。 &a[0][0]是指向int的指针。 它们有不同的类型,但在实践中指向相同的记忆。

请尝试以下代码:

int x[3][10];
printf("%d\n", x[0]);
printf("%d\n", x[1]);
printf("%d\n", x[2]);

看下面的表达式

if(&x[0][9] + 1 == &x[1][0])
    printf("I've entered here");

这个值为true ...当你向指针添加1时,它的地址增加4(或者你的机器具有的任何int大小),显示在内存中,元素x [1] [0]正好在元素x之后[ 0] [9]

一个整数大小为4,你会看到这些版画之间的差异是40(4 * 10)个元素......

这个数组是在编译时分配的,因此它已经知道它需要多少空间,它为3 * 10个整数分配连续的空间,就像它被声明为

 int x[30];

当你单独使用名称x时,你得到的是它指向的第一个元素的地址......

第二个维度只适合我们的程序员...但是编译器的成本很低......当你访问时

 x[1][8];

它必须计算添加到起始地址以访问该元素的程度,

这将是(1 * 10 + 8)* SIZE_OF_ARRAY_TYPE(这里是int)...

在上面的表达式中,1 =第一个维度上的数字,乘以10(第二个维度中第一行中的元素数量)加到第二个[]中的元素个数中

如果你有一个1维数组:

 x[23]

唯一的帐户是23 * SIZE_OF_ARRAY_TYPE(这里是int)

你也可以在这里尝试这个IF语句:

 if(&x[0][10] == &x[1][0])
    printf("I've entered here");

甚至是作业等

x[1][4] = 20;

printf("%d\n", x[0][14]);

指针为你提供了很大的力量,但是拥有巨大的力量带来了巨大的责任;)

数组int a[3][2]行主要顺序存储在内存中作为1d数组,因此该数组:

int a[3][2] = {
  {1, 2},
  {3, 4},
  {5, 6}
};

将存储为:

int a[] = {1, 2, 3, 4, 5, 6};

例如:

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **argv)
{
  size_t i;

  int a[3][2] = {
    {1, 2},
    {3, 4},
    {5, 6}
  };

  for(i = 0 ; i < sizeof(a) / sizeof(int) ; i++)
  {
    printf("%d\n", ((int *) a)[i]);
  }

  return 0;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM