C - 阵列内存存储



我对C语言比较陌生,只是在学习程序期间内存的存储方式。有人可以解释为什么以下代码:

int main(int argc, char** argv){
    float x[3][4];
    printf("%pn%pn%pn%pn", &(x[0][0]), &(x[2][0]), &(x[2][4]), &(x[3][0]));
    return 0;
}

输出此内容:

0x7fff5386fc40
0x7fff5386fc60
0x7fff5386fc70
0x7fff5386fc70

为什么前 3 个在内存中的位置不同,而最后一个与第三个相同?为什么前两者之间有 20 大小的差距,而第二个和第三个之间有 10 大小的差距?&(x[2][0]( 和 &(x[2][4]( 之间的距离似乎不是 &(x[0][0]( 和 &(x[2][0]( 之间距离的一半。提前谢谢。

声明大小为 n 数组时,索引范围从 0 到 n - 1。因此,x[2][4]x[3][0]实际上超出了数组的范围。

如果您还不知道,您声明的多维数组实际上是一个数组数组

编译器在内存中一个接一个地布置每个数组。因此,在内存中,您的元素按以下顺序排列:x[0][0]x[0][1]x[0][2]x[0][3]x[1][0]x[1][1]等等。

看起来你已经了解指针是如何工作的,所以我会掩盖它。最后两个元素相同的原因是x[2][4]超出界限,因此它指的是x[2]数组结束后内存中的下一个插槽。这将是x[3]数组的第一个元素,如果有的话,这将是x[3][0] .

现在,由于x[3][0]引用的地址没有变量映射到,因此取消引用它完全可能导致分段错误。在您的程序上下文中,恰好有一些东西存储在 0x7fff5386fc70 ;换句话说,你很幸运。

这是

由于指针算术。

您的数组是平面的,这意味着数据以线性方式存储,每个数据一个接一个地存储在内存中。首先[0][0]然后[0][1],等等。

[x][y]的地址计算为 (x*4+y)*float_size+starting_address

所以前两个[0][0][2][0]之间的差距是8*float_size。十六进制的差值为 20,十进制为 32,float_size为 4。

在第二个和第三个之间,你有(2*4+4)-(2*4)*float_size十进制为 16,所以十六进制为 10。这正好是前一行大小的一半,因为它是一行的大小(第三行中 4 个元素的大小(,而前一行是两行的大小(第一行和第二行中 8 个元素的大小(。

数组

是线性数据结构。无论它们的维度如何,例如 1 维、二维或 3 维,它们都是线性排列的。

您的 x[3][4] 将作为连续的固定大小的单元格存储在内存中,例如:

| (0,0) | (0, 1) | (0,2) | (0,3) | (1,0) | (1,1) | (1,2) | (1,3) | (2,0) | (2,1) | (2,2) | (2,3) |

这个 x[0][0] 表示法是矩阵表示法。在编译时,它被转换为指针表示法。计算如下:

x[i][j] = y * i + j  where y in your case is 4.

因此,以这种方式计算输出是完美的。

C 中的数组元素按行顺序连续存储。 因此,在您的示例中,&x[row][column] 正好等于 &x[0][0]+((row*4)+column))*sizeof(float)(当这些地址转换为字节数时,这就是您要输出的内容(。

您正在打印的第三个地址具有第二个索引越界(有效值 0 到 3(,第四个地址的第一个索引越界(有效值 0 到 2(。 恰好您选择的值在内存中的相同位置工作,因为这些行在内存中是端到端布局的。

&(x[0][0])&(x[2][0])之间有 8 个元素。 内存的实际差异乘以sizeof(float)对于编译器来说,这是 4 . 4*832,当打印为十六进制时,0x20 ,就是你所看到的区别。

如果选择的值为 rowcolumn ((row*4)+column)) 12(=3*4)或更多,则代码将计算数组外部内容的地址。 尝试使用这样的指针指针(例如,在该地址设置值(将给出未定义的行为。 您只是很幸运,您选择的索引恰好在数组中。

最新更新