c - 数组内存存储

c - array memory storage

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

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

输出这个:

0x7fff5386fc40
0x7fff5386fc60
0x7fff5386fc70
0x7fff5386fc70

为什么前三个在内存中的位置不同,而后一个与第三个相同? 为什么前两个之间有 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;换句话说,你很幸运。

这是指针运算的结果。

你的数组是flat,也就是说数据是线性存储的,在内存中一个接一个。首先 [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个元素的大小) ).

数组是线性数据结构。无论它们的维度如何,比如一维或二维或三维,它们都是线性排列的。

您的 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),对于您的编译器来说,是 44*832,当以十六进制打印时,它是 0x20,这就是您所看到的差异。

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