虚函数表指针,到底占多少字节?

C++语言 码拜 10年前 (2015-05-11) 5488次浏览 0个评论
 

谁能解释一下,下面两种情况,sizeof(A)大小,为什么是8和24?

考虑了内存对齐之后:
发现第一种情况,虚函数表指针占4字节。
发现第二种情况,虚函数表指针占8字节。

为什么第二中情况,a变量要放在第8个字节的位置?为什么不是放在第4个字节的位置?

****************************************************
class A
{
public:
int a;
A(){}
virtual ~A(){}
};

sizeof(A) == 8
********************************************************
class A
{
public:
int a;
double d;
A(){}
virtual ~A(){}
};

sizeof(A) == 24

***********************************************

32位机上指针是4个字节
我也知道是4个字节,问题是:

为什么第二种情况,sizeof(A)==24那么大?
即使考虑了内存对齐,也应该只是16字节吧?

引用 1 楼 howema 的回复:

32位机上指针是4个字节

sizeof(double) =8;
这个有字节对齐的问题: 第一种,4+4 四字节对齐了
                       第二种,8+8+8 (因为楼上说的sizeof(double)=8)

去找找 字节对齐的问题吧

我知道sizeof(double)==8,

不过,根据内存对齐的规则,int a;仍然是放在第4个字节的位置吧,为什么放在了第8个字节的位置?

引用 3 楼 lile1234_show 的回复:

sizeof(double) =8;

如果是8+8+8,你能解释一下下面的结构体,是8+8+8吗?

struct  AAA
{
int a1;
int a2;
double d1;
};

引用 4 楼 KuaiPengFei_ 的回复:

这个有字节对齐的问题: 第一种,4+4 四字节对齐了
                       第二种,8+8+8 (因为楼上说的sizeof(double)=8)

去找找 字节对齐的问题吧

你的编译器默认对齐是:8
引用 5 楼 ngbbxt 的回复:

我知道sizeof(double)==8,

不过,根据内存对齐的规则,int a;仍然是放在第4个字节的位置吧,为什么放在了第8个字节的位置?

引用 3 楼 lile1234_show 的回复:sizeof(double) =8;

楼主应该看看字节对齐相关章节。
首地址能够被其最宽基本类型成员的大小所整除。

引用 5 楼 ngbbxt 的回复:

我知道sizeof(double)==8,

不过,根据内存对齐的规则,int a;仍然是放在第4个字节的位置吧,为什么放在了第8个字节的位置?

引用 3 楼 lile1234_show 的回复:sizeof(double) =8;

struct  AAA
{
int a1;
int a2;
double d1;
};
sizeof 16

struct  AAA
{
int a1;
double d1;
int a2;
};
zieof 24
这样是完全不一样: 
第一个、第二个都是是8字节对齐 只不过是第一里面放了2个整形

例如:
struct A
{
short a;
int b;
char c;
};

struct B
{
short a;
char c;
int b;

};
都是四字节对齐 不一样的是 第二个结构里面 第二个四字节可以放下 char 与short 所有是8 
第一个里面 因为把int 放在中间了 short剩下的2 int 是不够用的 所有是4+4+4
楼主 自己找找资料吧! 多看书找找具体字节对齐的规则

我懂内存对齐。

那么,你看看,楼顶的第二个例子,是相当于下面的AAA还是BBB?

struct  AAA
{
int a1;
int a2;
double d1;
};

struct  BBB
{
int a1;
double d1;
int a2;
};

引用 9 楼 KuaiPengFei_ 的回复:

引用 5 楼 ngbbxt 的回复:我知道sizeof(double)==8,

不过,根据内存对齐的规则,int a;仍然是放在第4个字节的位置吧,为什么放在了第8个字节的位置?

引用 3 楼 lile1234_show 的回复:sizeof(double) =8;

struct  AAA
{
int a1;
int a2;
double d……

struct  BBB
{
int a1;
double d1;
int a2;
};
为什么?

虚函数表指针,不是必须放在结构体的首地址吗?

引用 11 楼 KuaiPengFei_ 的回复:

struct  BBB
{
int a1;
double d1;
int a2;
};

15分
首先标准没规定怎么布局,gcc多为16

个人猜测,先将所有成员按照结构体对齐排列好,然后再算上虚表指针和这个结构体对齐。

明白你的意思了。按照你的思路,就能解释这一切了。

引用 13 楼 akirya 的回复:

首先标准没规定怎么布局,gcc多为16

个人猜测,先将所有成员按照结构体对齐排列好,然后再算上虚表指针和这个结构体对齐。

放在最前面? 这么确定吗?
  这个我怎么在书上看的是跟大小端对齐差不多、 没有明确规定啊?看编译器而定 

再说放在最前面的是虚表示例、 不是你说的指针好吗?

是确定的。

引用 15 楼 KuaiPengFei_ 的回复:

放在最前面? 这么确定吗?
  这个我怎么在书上看的是跟大小端对齐差不多、 没有明确规定啊?看编译器而定 

再说放在最前面的是虚表示例、 不是你说的指针好吗?

引用 16 楼 ngbbxt 的回复:

是确定的。

引用 15 楼 KuaiPengFei_ 的回复:放在最前面? 这么确定吗?
  这个我怎么在书上看的是跟大小端对齐差不多、 没有明确规定啊?看编译器而定 

再说放在最前面的是虚表示例、 不是你说的指针好吗?

那你抱着你确定的思想过着吧….
    linux 下面GCC  G++ 的编译器就是最末端,只不过windows 下的是最前端而已 
  我刚还告诉你了 你确定放在最前面的不是你说的那个指针 是虚表的示例!

我用的是VS,微软的编译器,能确定已知是这种方式对齐吗?

引用 13 楼 akirya 的回复:

首先标准没规定怎么布局,gcc多为16

个人猜测,先将所有成员按照结构体对齐排列好,然后再算上虚表指针和这个结构体对齐。

引用 17 楼 KuaiPengFei_ 的回复:

引用 16 楼 ngbbxt 的回复:是确定的。

引用 15 楼 KuaiPengFei_ 的回复:放在最前面? 这么确定吗?
  这个我怎么在书上看的是跟大小端对齐差不多、 没有明确规定啊?看编译器而定 

再说放在最前面的是虚表示例、 不是你说的指针好吗?

那你抱着你确定的思想过着吧….
    linux 下面GCC  G++ 的编译器就……

哦哦哦哦哦 说反了 不好意思! 
   windows 下的QT VS VC 都是最末端  linux 下面的GCC G++ 都是最前端其他的不要知道 我没用其他的编译器 谢谢!

不跟你扯了。。我还的上班! 
   都说了 那个是不确定的、、你还执着的说是确定的 我无奈…
  呵呵!
 咱们都相相互抱着确定的态度撤吧….
不明白你说的“虚表的示例”,是什么?

引用 17 楼 KuaiPengFei_ 的回复:

引用 16 楼 ngbbxt 的回复:是确定的。

引用 15 楼 KuaiPengFei_ 的回复:放在最前面? 这么确定吗?
  这个我怎么在书上看的是跟大小端对齐差不多、 没有明确规定啊?看编译器而定 

再说放在最前面的是虚表示例、 不是你说的指针好吗?

那你抱着你确定的思想过着吧….
    linux 下面GCC  G++ 的编译器就……

引用 21 楼 ngbbxt 的回复:

不明白你说的“虚表的示例”,是什么?

引用 17 楼 KuaiPengFei_ 的回复:引用 16 楼 ngbbxt 的回复:是确定的。

引用 15 楼 KuaiPengFei_ 的回复:放在最前面? 这么确定吗?
  这个我怎么在书上看的是跟大小端对齐差不多、 没有明确规定啊?看编译器而定 

再说放在最前面的是虚表示例、 不是你说的指针好吗?

……

“实例” !  看了你没注意看我的回复啊。我说了两次啊 实例 第二次打错了而已!~  
   哈哈、、打拼音的悲哀啦  通假字!

但是,C++的标准规格说明书中不是说了吗:编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。 

引用 20 楼 KuaiPengFei_ 的回复:

不跟你扯了。。我还的上班! 
   都说了 那个是不确定的、、你还执着的说是确定的 我无奈…
  呵呵!
 咱们都相相互抱着确定的态度撤吧….

知道了。

那么,虚表的实例,又是什么呢?

多态说的不就是虚表和虚表指针吗?

哪里再来了一个虚表的实例?

引用 22 楼 KuaiPengFei_ 的回复:

引用 21 楼 ngbbxt 的回复:不明白你说的“虚表的示例”,是什么?

引用 17 楼 KuaiPengFei_ 的回复:引用 16 楼 ngbbxt 的回复:是确定的。

引用 15 楼 KuaiPengFei_ 的回复:放在最前面? 这么确定吗?
  这个我怎么在书上看的是跟大小端对齐差不多、 没有明确规定啊?看编译器而定 

再说放在最前面的是虚……

我只想说: 一切都是浮云、、 动手测试才是硬道理!

 不止我一个人测试了 楼上也有人告诉过你 其他编译器是怎么处理的!

我有测试啊,经过测试,我觉得13楼应该是符合实际的。

引用 25 楼 KuaiPengFei_ 的回复:

我只想说: 一切都是浮云、、 动手测试才是硬道理!

 不止我一个人测试了 楼上也有人告诉过你 其他编译器是怎么处理的!

引用 26 楼 ngbbxt 的回复:

我有测试啊,经过测试,我觉得13楼应该是符合实际的。

引用 25 楼 KuaiPengFei_ 的回复:我只想说: 一切都是浮云、、 动手测试才是硬道理!

 不止我一个人测试了 楼上也有人告诉过你 其他编译器是怎么处理的!

既然你测试了,那你还确定放在最前端? 你是在逗我? 擦….真的不理你了啊 亲!

5分
楼主结贴了吧:
Virtual是放在最前面算的,而且是单独分配内存的 不能和其他类型合并(包括其他Virtual)
猜测↑
引用 2 楼 ngbbxt 的回复:

我也知道是4个字节,问题是:

为什么第二种情况,sizeof(A)==24那么大?
即使考虑了内存对齐,也应该只是16字节吧?

引用 1 楼 howema 的回复:32位机上指针是4个字节

你测试的是Windows吧,就是因为内存对齐才是24字节了,Linux下应该是16

虚表指针放在实例对象的首地址,不是我测试得来的,是C++标准规定的。难道不是吗?

quote=引用 27 楼 KuaiPengFei_ 的回复:]
引用 26 楼 ngbbxt 的回复:我有测试啊,经过测试,我觉得13楼应该是符合实际的。

引用 25 楼 KuaiPengFei_ 的回复:我只想说: 一切都是浮云、、 动手测试才是硬道理!

 不止我一个人测试了 楼上也有人告诉过你 其他编译器是怎么处理的!

既然你测试了,那你还确定放在最前端? 你是在逗我? 擦….真的不理你了啊 亲!

class A
{

}

vs32 编译环境下
sizeof(A) = 1
 
子类以四字节对齐 + 虚函数指针表 = 8

第一反应就是,统统都是实现相关。
在vc2005里试验了下,是24. 前面有一个人说的很对,编译器的默认对齐是8.
如果在项目属性-     c/c++    –    代码生成 –  结构成员对齐,选择对齐为4,则sizeof (A)结果就是严格压紧的16了。。。

在默认情况下,对象布局是:
offset (hex)     field
———————-
0:   vft 指针
4:  
8:   int x;
C: 
10: double d   (low);
14: double d  (high)

不过我发现的是另一个现象。在讲语言的书上好像我没有见到过。可能这个情况有点特殊。下面说的是 windows 系统:

那就是如果父类A是一个 struct。
struct A;
有一个含有虚函数的类B 继承于A:
class B:public A;

则下面的特定代码:
A* p = new B();

编译器会把 B 实例地址 加上 4 以后 (跳过实例的虚函数表指针),赋给 p。这样 p 好像指向的就是一个 struct。

即 B 的实例布局是这样的:
 |  vftptr      |       struct A           |….
—————————————-
 |                |
 实例         |
                  |
                 p

把例子变得更明显一点,是这样的:

#include <stdio.h>
class A
{
public:
	int x;
	void foo1() { printf("A:foo1 \n"); };
};

class B : public A
{
public:
	double y;
	virtual void foo2() { printf("B:foo2 \n"); };
};

int _tmain(int argc, _TCHAR* argv[])
{
	B* pb = (B*)0x00480010;
	A* pa = pb;
	printf(" pb:%p\n pa:%p\n", pb, pa);
	getchar();
	return 0;
}

假设两者都有 vftp (虚函数表指针),或者都没有,两个对象的公共部分是严格对齐的,因此
上面的代码等同于普通的整数赋值。

假设一个有 vftp 另一个没有,
(不管是从子类指针自然转换到父类指针,还是从父类指针强制转换到子类指针)
则地址值会加上一个偏移跳过包含 vftp 的起始部分。
这个偏移可能比指针的尺寸大,因为和具体数据对齐有关,是编译器给出的。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明虚函数表指针,到底占多少字节?
喜欢 (0)
[1034331897@qq.com]
分享 (0)

文章评论已关闭!