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)
,对于您的编译器来说,是 4
。 4*8
是 32
,当以十六进制打印时,它是 0x20
,这就是您所看到的差异。
如果您选择 row
和 column
的值,其中 ((row*4)+column))
为 12(=3*4)
或更大,您的代码将计算数组外的地址。尝试使用这样的指针指针(例如,在该地址设置值)会产生未定义的行为。您很幸运,您选择的索引恰好在数组中。
我对 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)
,对于您的编译器来说,是 4
。 4*8
是 32
,当以十六进制打印时,它是 0x20
,这就是您所看到的差异。
如果您选择 row
和 column
的值,其中 ((row*4)+column))
为 12(=3*4)
或更大,您的代码将计算数组外的地址。尝试使用这样的指针指针(例如,在该地址设置值)会产生未定义的行为。您很幸运,您选择的索引恰好在数组中。