【问题标题】:Using pointers to get value from multidimensional array - C使用指针从多维数组中获取值 - C
【发布时间】:2014-04-23 04:12:50
【问题描述】:

我正在尝试从多维数组中的“第二行”中获取值。但我有一些问题。 我认为数字是按顺序存储在内存中的,所以tab[2][2] 的存储方式与tab[4] 相同。不过好像我错了。

这是我尝试过的:

int b[2][2] = {{111,222},{333,444}};
int i = 0;
for(;i < 100; i++)
    printf("%d \n", **(b+i));

问题是我只得到111333 作为结果。其他 98 个结果中没有 222444。为什么?

【问题讨论】:

  • 是的,双 * 是问题所在。使用 *(b+i) 会很好。

标签: c arrays pointers multidimensional-array


【解决方案1】:

问题是**(b+i) 并没有按照你的想法去做。它评估为:

b[i][0]

作为Matt McNabbnoted

**(b+i)

相当于:

*(*(b+i)+0)

由于*(b+i)等价于b[i],所以表达式整体可以看成:

*(b[i]+0)

因此:

b[i][0]

由于您的数组只有 2 行,因此只有 0 和 1 的 i 值在数组的范围内(即 111 和 333)。其余的都非常超出范围。

你可以做的是:

#include <stdio.h>

int main(void)
{
    int b[2][2] = { { 111, 222 }, { 333, 444 } };
    int *base = &b[0][0];

    for (int i = 0; i < 4; i++)
        printf("%d: %d\n", i, base[i]);
    return 0;
}

输出:

0: 111
1: 222
2: 333
3: 444

【讨论】:

  • 谢谢!这正是我所需要的。
  • 再解释一下,*(a+b) 等同于a[b]*a 等同于*(a+0) 在检索存储值的上下文中。
【解决方案2】:

您可以将二维数组视为一维数组的一种特殊形式。您的数组包含 2 个元素 (!)。 (每个元素恰好是一个包含两个元素的数组,但让我们暂时忽略它。)让我们使维度不同,以便我们可以区分它们:

int arr2d[2][3] 包含 2 个元素(每个元素都是 3 个元素的数组)。先写“主索引”,也就是说,如果你有一个像int arr1d[3]这样的一维数组,并且想要一个像arr2d这样的具有三个元素的数组,你必须写arr2d[2][3]。您可以使用 typedef 来安排它,这清楚地表明 C 中的所有数组本质上都是一维的:

typedef int arr3IntT[3];
arr3IntT arr2d[2] = { {0,1,2}, {3,4,5} };

现在arr2d+i 是什么意思? arr2d 与任何数组一样,衰减为指向其第一个元素的指针(这是一个 3 个整数的数组)。 arr2d+1 将这些元素中的 1 个偏移量添加到地址中,这样表达式就会像往常一样产生第二个元素的地址(即第二个 3 个整数数组)。像 *(arr2d+1) 一样取消引用它会产生该元素,即 3 个整数的一维子数组。它衰减到指向其第一个元素的指针,即指向第二个子数组中第一个 int 的指针。像往常一样,在表达式 **(arr2d+1) 中取消引用 that 会产生该 int。总结一下:在您的原始代码中,您从一个子数组迭代到另一个子数组,总是引用它们的第一个元素,偶然超出 i>1 的范围。

但基本上你是对的,C 中的 n 维数组的元素在内存中是连续的,所以如果你愿意,你可以一个一个地访问它们。你只需要索引一个指向 int 的指针,而不是一个指向 int[3] 的指针。方法如下:表达式arr2d 衰减为指向其第一个元素的指针,该元素是一个由 3 个整数组成的数组。解引用给出第一个元素,一个 3 个整数的一维数组。像所有数组一样,它衰减为指向其第一个元素的指针,一个 int,它是数据中的第一个元素:

#include<stdio.h>
int main()
{
    int arr2d[2][3] = { {0,1,2}, {3,4,5} };
    int *p_el1 = *arr2d;
    int i, j;


    // Sanity check by indexing 2-dimensionally
    for(i=0; i<2; i++) for(j=0; j<3; j++) printf("%d\n", arr2d[i][j]);

    // iterate the elements 1 by 1
    for(i=0; i<3*2; i++) printf("%d\n", p_el1[i]);
}

【讨论】:

    【解决方案3】:

    多维数组并不是一种全新的类型。它是一种数组类型,其中元素本身就是数组。引用 C99 标准 §6.2.5 ¶20(类型)

    数组类型描述了一个连续分配的非空集合 具有特定成员对象类型的对象,称为元素类型。 数组类型的特征在于它们的元素类型和数量 数组中的元素。

    int b[2][2] = {{111, 222}, {333, 444}};
    

    上述语句将b 定义为2 元素的数组,其中每个元素的类型为int[2] - 一个2 整数数组。它还使用数组初始值设定项列表初始化数组。在某些情况下,数组会隐式转换为指向其第一个元素的指针。

    printf 调用中,b 衰减为指向其第一个元素的指针。因此,它等价于&amp;b[0] 并且具有int (*)[2] 类型——一个指向2 整数数组的指针。请注意,访问数组边界之外的元素是未定义的行为。因此,for 循环条件i &lt; 100 是错误的。应该是i &lt; 2。现在,让我们试着揭开**(b+i)这个表达式的神秘面纱。

    b -------------> pointer to a[0]
    b + i ---------> pointer to a[i]
    *(b + i)  -----> a[i]
    *(*(b + i)) ---> *(a[i]) ----> *(&(a[i])[0]) ----> a[i][0]
    

    如前所述,数组的元素本身就是数组。因此,a[i] 是一个数组。同样,数组衰减为指向其第一个元素的指针,即指向&amp;(a[i])[0]。在这个指针上应用间接运算符* 可以得到该地址处的值a[i][0]

    您可以通过指针访问数组的元素,但指针类型必须是指向数组元素类型的指针。

    #include <stdio.h>
    
    int main(void) {
        int b[2][2] = {{111, 222}, {333, 444}};
        int (*p)[2] = b;
    
        // (sizeof b / sizeof b[0]) evaluates to 
        // the size of the array b
        for(int i = 0; i < (sizeof b / sizeof b[0]); i++)
            // (sizeof *p / sizeof (*p)[0]) evaluates to
            // the size of element of the array which is
            // itself an array.
            for(int j = 0; j < (sizeof *p / sizeof (*p)[0]); j++)
                printf("%d\n", *(*(p + i) + j));
    
        return 0;
    }
    

    这里,表达式*(*(p + i) + j)可以解码为

    p  ---------------> pointer to the first element of b, i.e., &b[0]
    (p + i) ----------> pointer to b[i], i.e., &b[i]
    *(p + i) ---------> the array element b[i] ---> decays to &(b[i])[0]
    *(p + i) + j -----> &(b[i])[j]
    *(*(p + i) + j) --> the element b[i][j]
    

    因此,表达式*(*(p + i) + j) 等价于b[i][j]。事实上,C99 标准 §6.5.2.1 ¶2 说 -

    下标运算符[]的定义是E1[E2]是 等同于(*((E1)+(E2)))

    这意味着我们有以下与上述程序的上下文等效 -

    *(*(p + i) + j)
    // equivalent to
    p[i][j]
    // equivalent to
    b[i][j]
    // equivalent to
    *(*(b + i) + j)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-12
      • 1970-01-01
      • 1970-01-01
      • 2021-12-02
      相关资源
      最近更新 更多