【问题标题】:2D array variable pointer confusion二维数组变量指针混淆
【发布时间】:2011-12-25 14:35:24
【问题描述】:

我对二维数组(C 语言)有一个基本的疑问。考虑如下声明一个二维数组

int array[3][5];

现在,当我执行以下操作时,printf 下面的两个输出是相同的:

printf("%u\n", array);
printf("%u\n", *(array));

现在尝试以下操作时:

printf("%u\n", array+1);
printf("%u\n", *(array)+1);

输出不同。我知道第二个 printf 是指数组 [0] [1],第一个是数组 [1] [0]。这是如何运作的?数组是指向什么的指针?

提前致谢

【问题讨论】:

    标签: c arrays multidimensional-array


    【解决方案1】:

    我会尽量给你一个技术上正确的解释,这样你就会知道发生了什么。不是很复杂,但确实违反直觉。

    简介:

    在 C 中,“左值”基本上表示“可分配”对象,这些对象在内存中的某个位置,而“右值”则表示“概念”值(不需要特别放在任何地方)。

    例如,如果您定义int a = 5;,则a 是一个int 类型的左值,值为5。它也可以被解释为(或者更确切地说,转换为)一个int 类型的右值。这样的右值仍会被认为等于 5,但它不再包含有关 a 在内存中的位置的信息。

    有些表达式需要左值(比如 operator= 的左侧,因为你必须分配给一个对象),有些需要右值(比如 operator+,因为你在相加时只需要整数值,或者右侧运算符=)。如果一个表达式需要一个右值但你传递了一个左值,那么它将被转换为一个右值。

    此外,只有右值被传递给 C 中的函数(这意味着 C 是严格按值调用,而不是按引用调用)。

    一些例子:

    int a = 1;
    a; // a is an lvalue of type int and value 1
    a = a+3; // here the `a` is converted to an rvalue of type int and value 1, then after the addition there's an assignment, on the lhs there's an lvalue `a` and an rvalue `4`
    

    从左值到右值的转换通常是微不足道的并且不引人注意(这就像从标有a 的架子上拿数字5)。数组基本上是这里的例外。

    重要的是:C 中没有数组类型的右值。有指针左值和右值,整数左值和右值,结构左值和右值等......但只有左值数组。当您尝试将数组类型的左值转换为右值时,您不再拥有数组,而是拥有指向数组第一个成员的指针。这就是 C(和 C++)中的数组混淆的根源。

    说明:

    • array
    • *(array)
    • array+1
    • *(array)+1

    arrayint[3][5] 类型的左值(3 个整数的数组,5 个整数)。当您尝试将其传递给函数时,它会收到一个 int (*)[5] 类型的指针(指向 5 个整数数组的指针),因为这是左值到右值转换后剩下的内容。

    *(array) 更狡猾。首先执行左值到右值,得到int(*)[5] 类型的右值,然后operator* 采用该右值并返回int[5] 类型的左值,然后您尝试将其传递给函数。因此它再次被转换为一个右值,产生一个int*

    array+1 使数组转换为 int(*)[5] 类型的右值,并且该右值加一,因此(根据指针算术规则)指针向前移动 1 * sizeof(int[5]) 字节。

    *(array)+1:参见前面的 2 点,但 int* 类型的最终右值会增加,再次按照指针算术规则,增加 1 * sizeof(int)

    这里不神秘!

    【讨论】:

    • 很好的解释,但是我有一个查询,对于第二种情况 *(array) ,为什么运算符'*'返回左值,取消引用操作数总是导致左值?
    • 我们并没有将数组传递给函数,这里我们只是打印它,那么左值到右值的类型转换是怎么发生的呢?
    • 如何简单地打印而不将其传递给函数?
    • 是的,我走错了路,我理解你的意思,但我仍然不明白 * 运算符总是返回左值是真的吗,它不取决于操作数吗?
    • 正如您简要介绍的 array+1 ,我有一个困惑,您能解释一下 &array+1 是如何转换为右值的吗?请解释一下,我对 array+1 工作和 &array+1 感到很困惑,请用指针解释清楚
    【解决方案2】:

    C 中的二维数组令人困惑。
    array 和 *array 都是同一个指针,但类型不同。
    array 是 int[3][5] 类型(它是一个大小为 5 的 int[3] 数组的数组)。
    *array 是数组的第一行,类型为 int[3]。
    array+1 表示数组加一个元素。数组的一个元素是 int[3],所以它向前 12 个字节。
    *array+1 表示 *array 加一个元素。 *array 的一个元素是 int,所以它向前 4 个字节。

    【讨论】:

      【解决方案3】:

      数组不是指针。忽略任何试图告诉你的答案、书籍或教程。

      在大多数情况下,数组类型的表达式转换(在编译时)为指向数组第一个元素的指针。例外情况是:

      • sizeof 的操作数(sizeof arr 产生数组的大小,而不是指针的大小)
      • 一元& 的操作数(&arr 产生数组的地址,而不是其第一个元素的地址——相同的内存位置,不同的类型)。这与您的示例特别相关。
      • 用于初始化数组对象的初始化程序中的字符串文字(char s[6] = "hello"; 不会复制字符串文字的地址,它会复制其值)

      二维数组只不过是数组的数组。还有其他数据结构可以使用相同的x[y][z] 语法,但它们不是真正的二维数组。是你的。

      [] 索引运算符是根据指针算术定义的。 x[y] 表示*(x+y)

      您的代码行为遵循这些规则。

      阅读comp.lang.c FAQ 的第 6 节。这是我见过的对这些东西最好的解释。

      并且不要使用"%u" 来打印指针值;转换为void* 并使用"%p"

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

      【讨论】:

        【解决方案4】:

        你可以这样理解:

        数组指向 3 行,每行 5 列

        当您执行 array+1 时,行会发生变化,因此您会转到第一行。您应该尝试使用 *(array + 1) 访问。

        当你做 *(array) 时,你指向第 0 行并且 *(array)+1 在列中向前移动,所以元素是 array[0][1]

        【讨论】:

        • 那么 b/w array+1 和 *(array+1) 有什么区别?当我将它们都打印出来时,我得到相同的内存位置
        【解决方案5】:

        指针的单位增量与数据类型的大小一致。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-10-23
          • 2016-03-27
          • 2021-11-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-21
          相关资源
          最近更新 更多