我对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*8
是32
,当打印为十六进制时,0x20
,就是你所看到的区别。
如果选择的值为 row
并column
((row*4)+column))
12(=3*4)
或更多,则代码将计算数组外部内容的地址。 尝试使用这样的指针指针(例如,在该地址设置值(将给出未定义的行为。 您只是很幸运,您选择的索引恰好在数组中。