可视化多维数组如何放置在内存中的方法

Ways to visualize how multidimensional array is laid in memory?

本文关键字:内存 方法 何放置 数组 可视化      更新时间:2023-10-16

这些都是我必须处理的类工作的一部分,当我身边总是有一个可靠的编译器来处理这些复杂的数组&指针算术对我来说,但遗憾的是事实并非如此。

就我个人而言,我想知道为了教育,事物是如何被放在电脑的记忆中的!

有没有为单曲&多维的,是什么让下面这个令人憎恶的代码如此不言自明?

如果我能找到,我将不胜感激。

//Here's where the classwork starts
//Given this declaration: 
double points[3][4] = {};
//Surprisingly, these 6 lines of code contain same address!
points;        //= 0012F5A4   
&points;       //= 0012F5A4
points[0];     //= 0012F5A4   
&points[0];    //= 0012F5A4
*points;       //= 0012F5A4
&points[0][0]; //= 0012F5A4   
double *pd0 = points[0][1];
//-> error: incompatible type (cannot convert 'double' to 'double*' in initialization)
double *pd1 = &points[0][1];
//-> compatible type
double *pd2 = &points[5][2];
//-> compatible type (possible runtime error; points to the element out of bound)
double *pd3 = &**points;
//-> compatible type
double (*p4d0)[4] = points[0];
//-> error: incompatible type (cannot convert 'double*' to 'double (*)[4]' in initialization)
double (*p4d1)[4] = &points[0];
//-> compatible type
double (*p4d2)[4] = (double (*)[4])points[0];
//-> compatible type
double (*p4d2_1)[5] = (double (*)[5])points[0];
//-> compatible type (possible runtime error; out of bound)
double (*p4d3)[4] = (double (*)[4])&points[0][0];
//-> compatible type
double (*p4d4)[] = &points[0];
//-> error: incompatible type (cannot convert 'double (*)[4]' to 'double (*)[]' in initialization)
double (*p4d5)[3][4] = points;
//-> error: incompatible type (cannot convert 'double (*)[4]' to 'double (*)[3][4]' in initialization)
double (*p4d6)[3][4] = &points;
//-> compatible type
double (*p4d7)[][4] = &points;
//-> error: incompatible type (cannot convert 'double (*)[3][4]' to 'double (*)[][4]' in initialization)
double (*p4d8)[][] = &points;
//-> error: incompatible type (multidimensional array must have bounds for all dimensions except the first)
//End

这里有一种思考points[3][4]数组的方法。它是一个由3行4列组成的数组。行的编号分别为0、1和2,列的编号分别是0、1、2和3。

让我们用值填充数组,以帮助可视化它

      0  1  2  3
  --            --
0 |  11 12 13 14  |
1 |  21 22 23 24  |
2 |  31 32 33 34  |
  --            --

十二个值按以下顺序排列在存储器中。每一行连续放置在前一行之后:

11 12 13 14 21 22 23 24 31 32 33 34

尽管变量被定义为二维数组,但它在内存中的表现就像是一个由12个值组成的一维数组。

一个双字节占用8个连续字节。因此,每个数组元素的地址比前一个地址高8。因此,如果数组从地址0x0012F5A4开始,这里是所有元素的地址。

Address     Value
-------     -----
0012F5A4    11.0
0012F5AC    12.0
0012F5B4    13.0
0012F5BC    14.0
0012F5C4    21.0
0012F5CC    22.0
0012F5D4    23.0
0012F5DC    24.0
0012F5E4    31.0
0012F5EC    32.0
0012F5F4    33.0
0012F5FC    34.0

为了强调这些是double值,我在上表中的每个值上都添加了".0"。在本文的剩余部分中,当提到这些值时,我将省略".0"一词。

现在让我们逐一分析每个案例。我只解释你问题中的前七种情况。

案例1

double *pd0 = points[0][1];
//-> error: incompatible type (cannot convert 'double' to 'double*' in initialization)

这里,pd0被声明为指向双精度的指针。指针变量包含其他变量的地址。
在赋值语句的右侧,points[0][1]包含示例数组中的双值12。发生此错误的原因是您正试图将双值分配给指针变量

情况2

double *pd1 = &points[0][1];
//-> compatible type

这很好,因为&是引用运算符。它提供它前面的变量的地址。因此&points[0][1]是存储在points[0][1]的双值的地址。在我的示例数组中,此地址为0x0012F5AC。指针pd1因此被分配地址0x0012F5AC。

案例3

double *pd2 = &points[5][2];
//-> compatible type (possible runtime error; points to the element out of bound)

如果编译器足够聪明,可以实现运行时数组边界检查,那么在执行语句时可能会产生异常,因为数组没有定义为具有行[5]。指针CCD_ 10将被分配远远超出12元素数组末尾的假设元素的地址。即使没有发生异常,如果使用该地址的数据,也肯定不会产生可预测的结果,并可能导致程序最终崩溃。

案例4

double *pd3 = &**points;
//-> compatible type

为了理解这一点,让我们在右手边添加一些括号,以强调运算符的应用顺序。

double *pd3 = &(*(*points));

现在从内到外工作

  • 术语CCD_ 11标识整个二维阵列
  • 术语(*points)标识阵列中第一行的地址
  • 术语(*(*points))(或**points)标识阵列的第一行中的第一个元素的值
  • 术语&(*(*points))(或&**points)标识该元素的地址

因此,我们将该单个元素的地址分配给指针变量pd3

案例5

double (*p4d0)[4] = points[0];
//-> error: incompatible type (cannot convert 'double*' to 'double (*)[4]' in initialization)

对于这种情况,我将参考John Bode在这篇文章中给出的答案。博德说,

当数组表达式出现在大多数上下文中时,其类型为从"T的N元素数组"隐式转换为"指向T的指针",并且其值被设置为指向数组中的第一个元素。这个此规则的例外情况是数组表达式是的操作数sizeof或(&)运算符的地址,或者当数组是在声明中用作初始值设定项的字符串文字。

因此,points[0]被隐式地从"双精度的四元数组"转换为"双精度指针"
CCD_ 21被明确声明为指向4个双精度数组的指针。出现不兼容的原因是"指向双精度的指针"与"指向4个双精度数组的指针"的类型不同。

案例6

double (*p4d1)[4] = &points[0];
//-> compatible type

与情况5一样,p4d1是指向4个双精度数组的指针
CCD_ 23提供第0行的地址。回想一下,在John Bode的文章摘录中,他说当使用(&)运算符的地址时,将数组表达式转换为指针的规则会出现异常。因此&points[0]也是一个指向4个双精度数组的指针。在执行上述语句之后,p4d1包含该地址。

案例7

double (*p4d2)[4] = (double (*)[4])points[0];
//-> compatible type

这解决了案例5的问题。它将points[0]强制转换为与p4d2相同的类型,从而有效地取消了对"指针到双精度"的隐式转换。

我把剩下的案子留给你处理。

内存中的布局:多维数组在内存中按行主顺序排列。

这意味着在顺序计算机内存中,它是一行接一行。。。这与初始化多维数组完全一样,例如:

int a[3][2] = { {1,2}, {2,3}, {4,5} };

关于:

points;        //= 0012F5A4   // means the address of the first element
&points;       //= 0012F5A4   // yields the address of the array
points[0];     //= 0012F5A4   // means: address of the first element of the first row of the array
&points[0];    //= 0012F5A4   // means: address of the first row of the array
*points;       //= 0012F5A4   // yields the first row, which is an address
&points[0][0]; //= 0012F5A4   // yields adress of first element of first row.

在上文中,请记住a[2][2]的意思是a+2*3+2a加上2行的大小,再加上2个元素)。

所有其他编译器错误都不言自明。