【问题标题】:How is `int (*arr1)[10]` different from `int arr2[10]`?`int (*arr1)[10]` 与 `int arr2[10]` 有何不同?
【发布时间】:2020-12-24 02:29:21
【问题描述】:

在浏览一些代码示例时,我遇到了这种声明方式:

int (*arr1)[10]

我知道 C 中数组声明的另一种方式:

int arr2[10]

int (*arr1)[10]int arr2[10] 做的一样吗?

【问题讨论】:

标签: arrays c pointers


【解决方案1】:

区别如下:

int (*arr1)[10];    // arr1 is a pointer to a 10-element array of int
int *arr2[10];      // arr2 is a 10-element array of pointer to int

这很重要 - 尽管最后有 [10]arr1 不是数组;它只存储一个单个指针值,这是int的一些10元素数组的地址。

在两种主要情况下会出现指向数组的指针 - 当 N 维数组表达式“衰减”为指针表达式时,以及当我们为 N 维数组分配内存时。

首先,衰减规则 - 除非它是 sizeof 或一元 & 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,表达式 “T 的 N 元素数组”类型的 em> 将被转换或“衰减”为“指向T 的指针”类型的表达式,表达式的值将是第一个元素的地址数组。你可能以前见过这样的代码:

int arr[10];
int *p = arr; // arr "decays" to a pointer to its first element

表达式arr“衰减”从类型“int”的 10 元素数组到类型“指向int”的指针。

2D 数组也会发生同样的事情:

int arr[5][10];
int (*p)[10] = arr;

表达式arr“衰减”从类型“int 的 10 元素数组的 5 元素数组”到类型“指向 int 的 10 元素数组的指针”。

您还可以使用一元 & 运算符直接获取指向一维数组的指针:

int arr[10];
int (*p)[10] = &arr; // note presence of & operator here

但这并不常见。

数组指针的另一个用途是当我们想要动态分配一个连续二维(或更高维)数组时:

int (*p)[10] = malloc( sizeof *p * 5 ); // allocates space for a 5x10 array of int

表达式*p的类型是“int的10元素数组”,或者int [10],所以sizeof *p等价于sizeof (int [10]),所以@987654345 @call 为 int 的 5 个 10 元素数组留出了足够的空间。您可以像任何其他二维数组一样对该数组进行索引:p[i][j] = some_value();。该数组的行在内存中是连续的并且长度都相同,并且只需要一次调用free 即可解除分配。

您可能已经看到通过多个步骤动态分配二维数组的代码 - 首先您分配一个指针数组,然后为每个指针分配一个目标类型的数组,如下所示:

int **p = malloc( sizeof *p * R ); // allocates an R-element array of pointer to int
if ( p )
{
  for ( size_t i = 0; i < R; i++ )
  {
    p[i] = malloc( sizeof *p[i] * C ); // allocates a C-element array of int
  }
}

这里的区别在于,在第二种情况下,行在内存中不相邻 - 它不是一个单独的连续块。此外,不同的行可能有不同的长度(有时称为“锯齿状”数组)。在释放 p 之前,您还必须分别 free 每一行。

指针声明的基本规则是:

T *p;       // p is a pointer to T
T *a[N];    // a is an array of pointer to T
T *f();     // f is a function returning pointer to T

T (*a)[N];  // a is a pointer to an N-element array of T
T (*f)();   // f is a pointer to a function returning T

T const *p; // p is a non-const pointer to const T - you can modify p to point
            // to a different object, but you cannot modify the thing p points to
const T *p; // same as above

T * const p; // p is a const pointer to non-const T - you can modify what p
             // p points to, but you can't modify p to point to a different object
             // (which means p needs to be initialized to a valid pointer value
             // when it's declared).

T const * const p; // p is a const pointer to const T - you can't update either
                   // p or *p
const T * const p; // same as above.

【讨论】:

    【解决方案2】:

    如果在本规范中

    int (*arr1)[10]
    

    您将删除括号中的术语,您将获得int[10]。括号中的术语表示指针。所以你有一个指向 10 个 int 类型元素的数组的指针。例如

    int a[10];
    int ( *arr )[10] = &a;
    

    解引用指针你会得到数组(指针指向的数组类型的对象)本身。

    这是一个演示程序。

    #include <stdio.h>
    
    int main(void) 
    {
        enum { N = 10 };
        int a[N] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        int ( *arr )[N] = &a;
        
        for ( size_t i = 0; i < N; i++ )
        {
            printf( "%d ", ( *arr )[i] );
        }
        
        putchar( '\n' );
        
        return 0;
    }
    

    程序输出是

    0 1 2 3 4 5 6 7 8 9
    

    您可以使用基于 typedef 的声明来代替这些声明,例如

    enum { N = 10 };
    typedef int Array[N];
    
    Array a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    Array *arr = &a;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-24
      • 2021-12-30
      • 1970-01-01
      • 2022-11-25
      • 2016-11-07
      • 1970-01-01
      • 2013-08-20
      • 2017-08-02
      相关资源
      最近更新 更多