【问题标题】:Why does * indirection operator gives address in a 2-dimensional array?为什么 * 间接运算符在二维数组中给出地址?
【发布时间】:2020-09-14 14:42:26
【问题描述】:
#include <stdio.h>

int main()
{
    int s[5][3] = { {1,2,6}, {7,3,4}, {8,5,6}, {2555,7,8}, {766,9,0} };
    
    printf("%u %u %u %u %u\n", (s+0), *(s+1), *(s+2), *(s+3), *(s+4));

    return 0;
}

C 语言代码。

在 printf 语句中,当我使用 (s+0) 时,它会在上面的代码中打印地址。所以如果我使用 * 运算符
即 *(s+1) 它应该在地址 (s+1) 处给我价值。但它仍然给了我一个地址。

所以我的问题是:

  1. 为什么 * 不在地址上赋值?
  2. (s+0) 是否与 *(s+0) 相同?

ps. 对于一维数组 int s[] = {1,2,3}; s 给出第零个元素的值,*(s+i) 给出数组 s 的第 i 个元素的值。

【问题讨论】:

  • s[0][0] 从地址 0xffff089 开始(无论如何)。 s+0 为您提供数组第一个元素的指针。 *(s+0) 为您提供指向 [][] 数组的第一个元素的指针,它仍然是指向值 1
  • 所有这些表达式都是地址。 s 是二维数组,所以即使解引用也会给你一个地址。
  • 提示:启用编译器警告。使用 gcc 添加 -Wall -Wextra -Wpedantic 以查看所有内容。你可以通过这个来了解更多
  • *(s+4)s[4] 相同,*(*(s+4)+3)s[4][3] 相同。
  • 使用%u 格式说明符打印地址会导致未定义的行为。使用%p 并将值转换为void*

标签: arrays c pointers


【解决方案1】:

三个简单的步骤:

  • s 是一个由 5 个数组组成的数组,每组 3 个 int
  • *(s+1)s 的元素 1。由于s是一个数组数组,它​​的一个元素就是一个数组;它是一个由 3 个int 组成的数组。
  • 在表达式中使用数组时,会将其转换为其第一个元素的地址。1

因此打印*(s+1)s[1]会打印s[1]的第一个元素的地址,即&amp;s[1][0]

但是,%u 不是用于打印地址或int 的正确规范。要打印地址,请使用%p,同时将地址转换为void *

printf("%p\n", s[1]);

要打印int,请使用%d,而不是%u%u 代表unsigned int

脚注

1 除了当数组是sizeof 的操作数、是一元&amp; 的操作数或者是用于初始化数组的字符串字面量时,它不会转换为地址。

【讨论】:

    【解决方案2】:

    当您访问数组 s 时,它会衰减为指向长度为 3 或 int (*)[3] 的数组的指针 int。向它添加 1 意味着衰减的指针现在指向下一个包含 3 个元素的数组。当您取消引用此指针时,您将获得指向int 的指针,向此添加1 您将将此指针增加到下一个int

    如果您有一个 unsigned char 数组和这样的指针(很抱歉没有使用与您相同的数组,但使用 unsigned char 更容易解释。

    unsigned char s[5][3] = { {1,2,6}, {7,3,4}, {8,5,6}, {255,7,8}, {14,9,0} };
    unsigned char *p=*(s+0); //This is equivalent with unsigned char *p=s[0]
    

    这个数组的数据会像这样在内存中(每个|之间有一个字节):

    1|  2|  6|  7|  3|  4|  8|  5|  6|255|  7|  8| 14|  9|  0 
    ^ p will point to here
    

    如果您现在访问s 并向其中添加一些内容,例如p=*(s+2);(与p=s[2]; 相同)。 s 将添加 2 个数组元素的长度,在本例中为 2*3,因为 s 在长度为 3 的数组中衰减。指针 p 现在将指向数组索引 0 处的 unsigned char s 中的索引 2:

    1|  2|  6|  7|  3|  4|  8|  5|  6|255|  7|  8| 14|  9|  0 
                            ^ p will now point to here
    

    如果在取消引用后向其添加某些内容,指针运算现在将适用于单个 unsigned char 元素。使用p=*(s+2)+1;(与p=&amp;s[2][1];相同),s将添加2个数组元素的长度,本例为2*3,然后在取消引用后添加1个数组元素,在这种情况 1,这意味着 p 现在指向 5

    1|  2|  6|  7|  3|  4|  8|  5|  6|255|  7|  8| 14|  9|  0 
                                ^ p will now point to here
    

    【讨论】:

      【解决方案3】:

      这样的表达式 s + i 用于数组声明

      int s[5][3] = { {1,2,6}, {7,3,4}, {8,5,6}, {2555,7,8}, {766,9,0} };
      

      给出指向数组第 i 个元素的指针。该元素的类型为int[3]。所以表达式s + i 的类型为int ( * )[3]

      解除对指针的引用,您将获得第 i 个元素的左值,您将获得 int[3] 类型的对象,即数组。

      在表达式中使用的数组指示符再次被转换为指向其第一个元素的指针。即int[3] 类型的对象被转换为指向数组第一个元素的int * 类型。

      因此使用例如表达式

      *(s+1)
      

      您将获得指向原始数组第二“行”的第一个元素的指针。

      请记住,表达式*(s+1) 等价于具有数组类型int[3] 的表达式s[1]

      如果要输出二维数组中每一“行”的第一个元素,则需要应用间接运算符。例如

      #include <stdio.h>
      
      int main( void )
      {
          int s[5][3] = { {1,2,6}, {7,3,4}, {8,5,6}, {2555,7,8}, {766,9,0} }; 
      
          for (size_t i = 0; i < sizeof( s ) / sizeof( *s ); i++)
          {
              printf( "%d ", **( s + i ) );
          }
          putchar( '\n' );
      }
      

      程序输出是

      1 7 8 2555 766
      

      要使用指针算法输出整个数组,您可以编写示例

      #include <stdio.h>
      
      int main( void )
      {
          int s[5][3] = { {1,2,6}, {7,3,4}, {8,5,6}, {2555,7,8}, {766,9,0} }; 
      
          for (size_t i = 0; i < sizeof( s ) / sizeof( *s ); i++)
          {
              for (size_t j = 0; j < sizeof( *( s + i ) ) / sizeof( **( s + i ) ); j++ )
              {
                  printf( "%d ", *( *( s + i ) + j ) );
              }
              putchar( '\n' );
          }
      
          putchar( '\n' );
      }
      

      程序输出是

      1 2 6
      7 3 4
      8 5 6
      2555 7 8
      766 9 0
      

      【讨论】:

        【解决方案4】:

        首先,s 不是指针,它是一个数组。对其应用指针算术将迫使它衰减为指针。在二维数组的情况下,衰减的s 的类型为int(*)[3] - 指向三个整数数组的指针。只有外部数组会衰减。

        s+0 == s
        /* decayed*/ s == &s[0] // Array decays to a pointer to its first element
        &s[0][0] == &s[0] == s // From the previous two (address operator does not stack)
        s+1 == &s[1] //Pointer to {7,3,4}
        *(s+1) == s[1] // Array {7,3,4}
        *(s+1)+0 == s[1]+0 = &s[1][0] // Array {7,3,4} decays to a pointer to its first element
        *(*(s+1)+0) == **(s+1) == 7 // First element of array s[1]
        

        作为一个练习,尝试确定两边表达式的类型。

        回答你的问题:

        1. 它给出了s[0] 的值,它本身是一个衰减为指针的数组,因此你得到一个地址。这也与s本身的地址相同,因为s== (s[0]+0)
        2. 没有。
        >>printf("%u %u %u %u %u\n", **(s+0), **(s+1), **(s+2), **(s+3), **(s+4));
        1 7 8 2555 766
        

        【讨论】:

        • &amp;s[0][0] == &amp;s[0] == s 是假的。 C不是python。同样的问题*(*(s+1)+0) == **(s+1) == 7
        • s[1]+0 = &amp;s[1][0] 不起作用,因为s[1]+0 不是左值。
        猜你喜欢
        • 2021-03-10
        • 1970-01-01
        • 2021-06-02
        • 1970-01-01
        • 1970-01-01
        • 2021-10-17
        • 2021-11-20
        • 2021-11-11
        • 1970-01-01
        相关资源
        最近更新 更多