【问题标题】:What is being displayed when I read a two-dimensional array as a one-dimensional array?当我将二维数组读取为一维数组时显示什么?
【发布时间】:2014-05-28 18:46:04
【问题描述】:

我试图理解 C 处理数组的方式,在这种情况下,通过读取一个二维数组,就好像它是一个一维数组一样。

鉴于这个简单的 C 程序

#include <stdio.h>

int main(int argc, char *argv[]){

    int array[4][4];
    int i, j;

    for(i = 0; i < 4; i++){
        for(j = 0; j < 4; j++){
            array[i][j] = (i+1)*(j+1);
            printf("%d ", array[i][j]);
        }
        printf("\n");
    }

    for(i = 0; i < 16; i++){
        printf("%d ", array[i]);    
    }

    printf("\n");

}

我得到这个奇怪的输出。

1 2 3 4 
2 4 6 8 
3 6 9 12 
4 8 12 16 
56319776 56319792 56319808 56319824 56319840 56319856 56319872 56319888 56319904 56319920 56319936 56319952 56319968 56319984 56320000 56320016 

在第二个for 循环中打印了什么?

【问题讨论】:

  • 在垃圾打印中打印数组地址。
  • 这个“垃圾”实际上不是指向存储值的地址(或指针)吗?如果我们做&amp;(array[i]),我们会得到什么?谢谢。

标签: c arrays multidimensional-array


【解决方案1】:

在第二个 for 循环中打印了什么?

简短的回答是“这是垃圾”。它的正式名称是“未定义的行为”,但它本质上是一个任意十进制数字的序列。

长答案有点棘手:您正在传递一维数组的printf 地址,这些地址被重新解释为整数。请注意数字是如何以 16 的相同步长分开的。这是您系统上四个 ints 的大小。

如果您想通过一维数组获取原始数字,您可以强制对数组进行不同的重新解释 - 作为指向 int 的指针:

int *ptr = (int*)&array;
for(i = 0; i < 16; i++){
    printf("%d ", ptr[i]);
}

这会从二维数组中生成数字序列,数组存储在内存中的方式(逐行)。

Demo on ideone.

【讨论】:

  • 这是否意味着我已经创建了十六个一一的数组?我原以为我创建了四个一四的数组,但打印的结果似乎是十六个不同的重新解释的地址。
  • @JoshuaCook 不,您创建了四个数组,每个数组 4x1。 array[i]i-th 这样的数组的地址。只有前四个地址在数组中;剩下的 12 个在数组之外。但是,printf 并不关心,因为它会打印地址本身,而不是在该地址读取或写入值。
  • 那么我从array[i] 获得了哪些关于i&gt;3 的信息?
  • @JoshuaCook 好吧,C 数组在很大程度上与指针运算有关。一旦你知道了初始元素的地址和单个元素的大小,你就可以计算任何元素的地址——一个真实的、有效的地址,或者一个理论地址,这可能是有效的,也可能是无效的。 C 会将您的数组“免费”转换为指针,从而导致人们得出错误的结论,即数组是指针(它们不是指针)。我在 ideone 上发布的示例使用单独的指针来遍历数组,但您不需要显式声明它:您也可以通过强制转换为 int* 来做到这一点。
  • 一旦ptr超出array[0]的范围,这个例子也是UB;为避免这种情况,请执行 int *ptr = (int *)&amp;array;(int *)array
【解决方案2】:

二维数组被认为是一维数组的一维数组。它正在打印一维数组的地址,但您应该使用%p 说明符来打印地址,否则它会调用未定义的行为

for(i = 0; i < 4; i++){
    printf("%p", (void *)array[i]);    
}  

int array[4][4] 是 16 个整数的 2D 数组类型,或者您可以说 arrayint 的 4 个 1D 数组的 1D 数组。

您应该注意,只有您的程序调用未定义的行为有两个原因:
1.错误的说明符%d用于打印地址。
2.您在最后一个循环中越界访问。 for(i = 0; i &lt; 16; i++) 应该是 for(i = 0; i &lt; 4; i++)

【讨论】:

  • 技术上你应该在使用%p 时强制转换为void *char *,否则行为未定义,尽管在大多数平台上它是无操作的。在实践中,没有人关心。
  • 感谢%p 说明符。
  • 我知道我已经创建了一个四个,但我不应该只有四个不同的地址吗?
  • @JoshuaCook;更新了我的答案。
猜你喜欢
  • 2021-12-28
  • 2010-11-29
  • 2011-02-28
  • 1970-01-01
  • 2018-11-20
  • 2013-04-04
  • 1970-01-01
  • 2016-02-18
  • 2018-02-19
相关资源
最近更新 更多