您的理解有些正确,尽管您似乎将太多东西混合在一起并且您的图表缺少细节。以下是我为一些最简单的情况绘制它的方法......
让我们从一个简单的案例开始,将操作员new
排除在外:
#include <cstdio>
struct myobj {
int v;
};
int main()
{
myobj obj[2];
obj[0].v = 1;
obj[1].v = 2;
myobj *ptra = &obj[0];
myobj *ptrb = &obj[1];
myobj **ptrc = &ptrb;
printf("obj size is: %lu\n", sizeof(myobj));
printf("pointer size: %lu\n", sizeof(void *));
printf("obj[0] address: %p\n", (void *)&obj[0]);
printf("obj[1] address: %p\n", (void *)&obj[1]);
printf("ptra address is %p, it points to %p\n", (void *)&ptra, (void *)ptra);
printf("ptrb address is %p, it points to %p\n", (void *)&ptrb, (void *)ptrb);
printf("ptrc address is %p, it points to %p\n", (void *)&ptrc, (void *)ptrc);
}
上面的程序将输出如下内容:
$ g++ -Wall -pedantic -o test ./test.cpp
$ ./test
obj size is: 4
pointer size: 8
obj[0] address: 0x7fff5b73dbc0
obj[1] address: 0x7fff5b73dbc4
ptra address is 0x7fff5b73dbb8, it points to 0x7fff5b73dbc0
ptrb address is 0x7fff5b73dbb0, it points to 0x7fff5b73dbc4
ptrc address is 0x7fff5b73dba8, it points to 0x7fff5b73dbb0
这对应于内存中的以下简单布局:
那么和你的画有什么不同呢?指针和对象的地址。如果指针本身放在地址 0,那么下一个指针就不能放在地址 1 上,因为指针本身占用了更多的空间,所以其他数据只能放在0 + sizeof(void*)
地址上。对于对象,下一个地址至少要大于对象本身的大小(即sizeof(myobj)
)。
当涉及到动态分配时,情况会发生一些变化。例如,当使用“new”运算符分配对象时,如下所示:
myobj *ptra = new myobj();
myobj *ptrb = new myobj();
myobj **ptrc = &ptrb;
...您可以想到这样的内存布局:
现在,指向另一个指针 (**) 的指针只不过是指向一个或多个指向对象的指针中的第一个的指针。容易,对吧?任何您都可以拥有指向指针的指针...无论如何,使用动态分配的指针指针,如下所示:
myobj *ptra = new myobj();
myobj *ptrb = new myobj();
myobj **ptrc = new myobj*[2];
ptrc[0] = ptra;
ptrc[1] = ptrb;
内存布局可能如下所示:
顺便说一句,您在此处的第 3 行有错误 - myobj **ptrc = new *myobj();
。应该是myobj **ptrc = new myobj*();
。
为了解决您以后的问题,下图描绘了myobj *ptrb = new myobj[2]();
表达式的结果,其中您有一个指向动态分配的两个对象的指针。指针本身指向两个已分配对象中的第一个:
还有一次关于指针的指针,以便您可以看到不同之处。考虑以下代码:
struct myobj {
int v;
};
int main()
{
myobj *ptra = new myobj[2]();
myobj *ptrb = new myobj[4]();
myobj **ptrc = new myobj*[2];
ptrc[0] = ptra;
ptrc[1] = ptrb;
ptrc[0][0].v = 1;
ptrc[0][1].v = 2;
ptrc[1][0].v = 3;
ptrc[1][1].v = 4;
ptrc[1][2].v = 5;
ptrc[1][3].v = 6;
}
它将创建以下布局:
如您所见,堆栈包含三个非动态分配的指针(它们也是对象)。这是声明的结果:
myobj *ptra;
myobj *ptrb;
myobj **ptrc;
然后,三个不同的东西被分配了“新”:
myobj
用表达式分配两个类型的对象,new myobj[2]()
第一个对象的地址存储在指针中ptra
。
- 四个类型
myobj
的对象用表达式分配new myobj[4]()
,该表达式的结果是四个对象中第一个的地址,它存储在指针“ptrb”中。
- 使用 with 表达式分配两个指针
new myobj*[2]
。该表达式的结果是两个指针中第一个的地址。该地址存储在变量中ptrc
。
现在,这两个分配的指针(在“块 C”中)指向“无处”。因此,为了举例,我们让它们指向相同的对象,ptra
并ptrb
通过“按值”复制指针来指向:
ptrc[0] = ptra;
ptrc[1] = ptrb;
就这么简单!