【问题标题】:2D array pointer二维数组指针
【发布时间】:2021-12-17 15:53:14
【问题描述】:

我知道二维数组基本上是指向数组的指针,所以在下面的代码中,a 是指向本身是数组的第 0 个索引的指针,*a 应该返回第 0 个索引元素的地址,a 和 *a 如何返回相同的值

#include <stdio.h>
#include <stdio.h>
      int main () {
          int a[4] [5] = {{1, 2, 3, 4, 5},
                          {6, 7,8, 9, 10},
                          {11, 12, 13, 14, 15},
                          {16, 17,18, 19, 20}};
     
      
         printf("%d\n",a);
         printf("%d\n",*a);
    
    }

【问题讨论】:

  • 您的问题是什么?您应该使用%p 打印指针并将它们转换为void*
  • a*a 具有相同的值但类型不同
  • 而且...不,数组不是指针。它在大多数表达式中都转换为指针,但这并不意味着数组是指针
  • 不是直接的欺骗,而是接近...stackoverflow.com/questions/2528318/…

标签: c pointers multidimensional-array printf implicit-conversion


【解决方案1】:

对于要输出指针值的初学者,您应该使用转换说明符%p 而不是%d

     printf( "%p\n", ( void * )a );
     printf( "%p\n", ( void * )*a) ;

用作参数表达式的数组指示符a 被隐式转换为指向其第一个元素的指针。正如数组声明为二维数组一样

      int a[4] [5] = {{1, 2, 3, 4, 5},
                      {6, 7,8, 9, 10},
                      {11, 12, 13, 14, 15},
                      {16, 17,18, 19, 20}};

那么它的元素的类型为int [5],而数组隐式转换到的指针(类型为int ( * )[5])将具有数组的第一“行”的地址,即数组的初始地址数组占用的内存范围。

解除对指针的引用,您将获得第一个“行”,即数组a[0],类型为int [5]。再次用作参数表达式,它被隐式转换为 int * 类型的指针,指向其第一个元素 a[0][0]。并输出第一个“行”的第一个元素的地址,即数组占用的内存范围的初始地址。

这是整个数组的地址,它的第一个“行”和第一个“行”的第一个元素的地址彼此相等。但对应的表达式&amp;aa*a(隐式转换为指针后)有不同的类型。表达式&amp;a 具有int ( * )[4][5] 类型,表达式a(在隐式转换之后)具有int ( * )[5] 类型,而表达式*a(也在隐式转换之后)具有int * 类型。但是它们的值是相等的。

【讨论】:

    【解决方案2】:

    我知道二维数组基本上是指向数组的指针

    不,不是。在大多数表达式中使用数组时“衰减”到指向第一个元素的指针,但这不会使数组成为指针。

    所以在下面的代码中 a 是指向第 0 个索引的指针

    仅在表达式printf("%d\n",a); 中,a 衰减为指向其第一个元素的指针。 a 的第一个元素是 int [5] 类型的数组,所以在这个 printf 表达式中,a 衰减为指向此类元素的指针,int (*)[5] 类型。

    使用%d 打印指针不是明确定义的行为,因此代码错误,您应该使用%p 并将参数转换为void*printf("%p\n", (void*)a);

    a 和 *a 如何返回相同的值

    对于任何数组,数组本身和它的第一个元素自然位于相同的地址,否则数组的概念将没有任何意义。数组的真正定义是分配在连续地址上的具有相同类型的连续项目块。

    当您取消引用*a 时,您取消引用类型为int(*)[5] 的衰减数组a 并获得一个类型int [5]。但是由于大多数表达式中不能使用数组,这也可以说是衰减为指向int [5]的第一个元素的指针,即类型int*,指向第一个数组中的项[0]。

    【讨论】:

      【解决方案3】:

      上面已经有两个有价值的答案,但我认为您仍然对这种情况感到困惑;因为我还是新手时也有同样的感觉(尽管我还不是职业选手)。

      1. 下面的行没有返回指针。实际上,它返回一个内存地址,第一个地址保存数组的第一个元素作为数据;但由于它在堆栈上,C 将其视为指针。
      int a[4][5] = { ... };
      
      1. 下面的行再次返回堆栈上的内存地址,但它们保存其他内存地址,这一次,它们是字面意思强>指针;它们指向不同的内存地址——很可能在堆上。
      int** b = malloc(4 * sizeof(int*));
      int*  c = malloc(5 * sizeof(int));
      

      不要保持双手清洁,调试下面的代码并检查输出,同时注意内存转储:

      #include <stdio.h>
      #include <stdlib.h>
      
      int** copyArray(int row, int col, int pSrc[row][col]) {
          int** pDst = malloc(row * sizeof(int*));
      
          for (int i = 0; i < row; i++) {
              pDst[i] = malloc(col * sizeof(int));
      
              for (int j = 0; j < col; j++) {
                  pDst[i][j] = pSrc[i][j];
              }
          }
      
          return pDst;
      }
      
      int main() {
          int a[4][5] = { {  1,  2,  3,  4,  5 },
                          {  6,  7,  8,  9, 10 },
                          { 11, 12, 13, 14, 15 },
                          { 16, 17, 18, 19, 20 } };
      
          int** b = copyArray(4, 5, a);
      
          printf("%2d %2d %2d %2d %2d\n", a[0][0], a[0][1], a[0][2], a[0][3], a[0][4]);
          printf("%p\n", &a);
          printf("%p\n", a);
          printf("%p\n", *a);
          printf("%p\n", a[0]);
          printf("%p\n", &a[0]);
          printf("\n");
          printf("%2d %2d %2d %2d %2d\n", b[0][0], b[0][1], b[0][2], b[0][3], b[0][4]);
          printf("%p\n", &b);
          printf("%p\n", b);
          printf("%p\n", *b);
          printf("%p\n", b[0]);
          printf("%p\n", &b[0]);
      
          for (int i = 0; i < 4; i++) {
              free(b[i]);
          }
      
          free(b);
          return 0;
      }
      

      【讨论】:

        【解决方案4】:

        我知道二维数组基本上是指向数组的指针

        没有。二维数组是数组的数组 - 不涉及指针。它将在内存中布置为

           +––––+
        a: |  1 | a[0][0]
           +––––+
           |  2 | a[0][1]
           +––––+
           |  3 | a[0][2]
           +––––+
           |  4 | a[0][3]
           +––––+
           |  5 | a[0][4]
           +—–––+
           |  6 | a[1][0]
           +––––+
           |  7 | a[1][1]
           +—–––+
           |  8 | a[1][2]
           +––—–+
           |  9 | a[1][3]
           +––––+
           | 10 | a[1][4]
           +––––+
           | 11 | a[2][0]
           +––––+
           | 12 | a[2][1]
           +–——–+
             ...
        

        除非它是sizeof 或一元&amp; 运算符的操作数,或者是用于在声明中初始化字符数组的内容的字符串文字,表达式 类型“T 的 N 元素”将被转换或“衰减”为“指向 T 的指针”类型的表达式。

        从上图可以看出,aa[0]a[0][0]这几个表达式的起始地址都是一样的。

        表达式 a 的类型为“int 的 5 元素数组的 4 元素数组” - 除非它是 sizeof 或一元 &amp; 运算符的操作数,它“衰减”为“指向int 的5 元素数组的指针”(int (*)[5]) 类型的表达式,其值为a[0] 的地址。

        表达式 *a 与表达式 a[0] 相同,并且类型为“int 的 5 元素数组” - 同样,除非它是 sizeof 或一元 &amp; 运算符的操作数,否则它“衰减”为“指向int的指针”类型的表达式,其值为(*a)[0]的地址(与a[0][0]的地址相同)。

        表达式a*a&amp;aa[0]&amp;a[0][0] 都产生相同的地址值(以任何类型之间的表示差异为模),但它们具有不同的类型:

        Expression    Type            "Decays" to        Equivalent expression
        ----------    ----            -----------        ---------------------
                 a    int [4][5]      int (*)[5]         &a[0]
                *a    int [5]         int *              &(*a)[0] or &a[0][0]
                &a    int (*)[4][5]   n/a                n/a
              a[i]    int [5]         int *              &a[i][0]
             &a[i]    int (*)[5]      n/a                n/a
           a[i][j]    int             n/a                n/a
          &a[i][j]    int *           n/a                n/a
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-09-09
          • 2017-04-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多