【面试题】sizeof引发的血案

被sizeof虐最早还是从toshi开始的。这货当初听我说我学C++也算入门了吧,他就拿一个sizeof的题来虐我,哈哈,好吧我必须承认当时连门都没看到,更谈不上入门了。


之前,我们先定义个宏,方便输出:

#define PRINT(a) cout << #a << ":" << a << endl

v1.0 最基本的sizeof

void printSize(char aInFunc[]){
    PRINT(sizeof(aInFunc));
}

int main(int argc, _TCHAR* argv[])
{
    char a[] = "abc";
    char *b = "abc";
    PRINT(sizeof(a));
    PRINT(sizeof(b));
    printSize(a);
    getchar();
    return 0;
}
非常简单。很多人一看就知道前两个输出应该是 4 4

第三个呢?

哇,前两个都是4了,难道第三个还能不是4?

恭喜你答对了。这题简单就简单在数组a和指针b居然的sizeof正好一样,所以即便不了解原理也能蒙对。

结果:

sizeof(a):4
sizeof(b):4
sizeof(aInFunc):4

v2.0开始说正事儿

刚才不是正事儿么?对,如果面试官出这题就是非常有诚意的想让你通过。。

void printSize(char aInFunc[]){
    PRINT(sizeof(aInFunc));
}

int main(int argc, _TCHAR* argv[])
{
    char a[] = "abcd";//modified
    char *b = "abcd";//modified
    PRINT(sizeof(a));
    PRINT(sizeof(b));
    printSize(a);
    getchar();
    return 0;
}
注意,这段程序跟1.0的基本一样,变化之处已经注明。

答案是多少呢?我当时拿2.0坑人的时候,已经开始有人犯迷糊了。

是5 5 5吧?对不起55555……

大家都能想到第一个是5,因为我们知道数组里面字符串后面会有一个\0。第二个可是一个指针哎,sizeof指针的时候输出的是指针这个变量占用的空间,所以还是4。

那第三个总该是5了吧---当然不是,是的话肯定就不这么考你了。在数组作为参数传入的时候实际上就退化为普通的指针了,不过这个指针实际上类似于const pointer,不可以其指向的内存空间:注意,还是有不同的!

const pointer指向的内容不一定能够被修改,比如例子当中的b指向“abcd”,这里的“abcd”实际上是字面量,不可以被修改!
如果指向的是堆内存,那当然随便你怎么样了。哈哈
数组可以修改,栈内存
回来回来,说数组形参的事儿呢,因为退化为一个指针的样子,所以sizeof的结果就是指针的大小了,4。其实你也可以想想,平白无故传一个数组的首地址到函数当中,你问函数“那什么,这个数组大小是多少”,你信不信函数会一脸委屈。

结果:

sizeof(a):5
sizeof(b):4
sizeof(aInFunc):4

v3.0来点儿猛料!

如果有高人看到2.0,肯定会笑道你这结果是有问题的。

太对了!因为我用的是32位的编译器!现在条件改变,编译器为64位,程序不变,请问结果几何?

……

这问题就是会处在哪儿呢?

原来32位也好,64位也好,说的是cpu的处理能力,即字长。32位下面的寻址最大为4个字节,所以就是32位嘛,多好理解;64位可以简单的理解为8个字节,我查了一些资料,其实64位的架构很多都是从32位演进的,这个很正常,不信你看看中国移动是怎么从gsm演进到tds-cdma又到hspa又到tdd-lte的。

参考文献:http://blog.csdn.net/jsufcz/article/details/3305073


其实说了半天,就是指针这种类型的变量的占用空间变了,变成了8个字节。

我用的是VS2013,一般默认的都是32位的编译环境,大家可以通过修改为64位试试。

结果:

sizeof(a):5
sizeof(b):8
sizeof(aInFunc):8

v4.0休息一下

刚说了半天这么热闹,其实这回该说点儿实用的了。我们知道sizeof一个指针只能得到它占用内存的大小,可是我想知道它指向的字符串大小怎么办?

……

答案揭晓:strlen函数。需要注意的是,这个函数返回的结果不包含\0,也就是说

strlen("abcd")---->4

v5.0死磕了它

struct S{
	int a;
	char b;
};
PRINT(sizeof(S));
首先说明一点,这样的语法在C里面可能是有问题的,因为我们可能需要到一个typedef,不过C++里面已经默认为我们做了这件事情。

输出多少?我们都知道结构体是元素大小的和。所以理所当然的认为 应该是 4+1=5。这样想对于开发其实是没有太大的问题的。不过编译器为了让程序跑起来更带劲儿,做了优化,这就是传说中的对齐。

我也不瞎扯了,结果是8。

简单说下我对对齐的认识:

对齐是为了内存访问速度而生的,如果内存不对齐,那么指针移动的时候就需要注意下一个变量的类型和大小,并作出准确的移动;对齐以后呢,移动的时候只需要移动到下一个对齐的位置就可以了吧。我说的比较简单,认识有待提高嘛,哈哈。

我们更关心类似于上面的结果输出。当内存对齐的值为8时,将结构体内所有的变量占用的空间加起来,不足8的倍数的,向上对齐。

struct S{
	long long a;//8
	char b;//1
	int c;//4
	int d;//4
};
这个时候sizeof的值为 8+1+4+4 = 17,所以结果为24。

再在S当中增加7个char,就像:

struct S{
	long long a;
	char b[8];
	int c;
	int d;
};
结果仍然是24。不过再增加就超过24了,所以结果就成了32。

参考文献:http://blog.csdn.net/zhangyang0402/article/details/5802876

我用的是vs2013,经过测试,vs2013的默认对齐值就像参考文献当中的vc6.0一样,也是8。


v6.0还没完?

其实我饿了,不过还是要写完再去吃!

6.1定义一个类Base,里面啥都没有。

class Base
{
public:
};
PRINT(sizeof(Base));
结果是1。因为你声明了一个类型,总得给人家个座位坐吧。亲,可千万别站着,给你一字节先。

6.2接着,加构造和析构,加了三个函数。

class Base
{
public:
	Base();

	void funcA(int );
	void funcA(int ,int);
	void funcA(int ,int,int);
	 ~Base();
};
sizeof呢?仍然是1。因为没有实质性的东西在里面。

函数的代码不占位置吗?存在静态区。人家是特派员,哈哈。

参考文献:http://blog.csdn.net/jacqueslim/article/details/6801601


6.3函数加个virtual。

class Base
{
public:
	Base();

	void funcA(int );
	void funcA(int ,int);
	void funcA(int ,int,int);
	virtual ~Base();
};
这时候结果是4。

因为virtual会导致Base类有一个指针指向虚函数表,这是C++的多态实现机制,子类在继承Base类以后应该覆写virtual函数,如果父类指针指向了一个子类的对象,那么这个指针可以通过查虚函数表来调用子类的函数,而不是调用父类的函数。


6.4好多个virtual函数。

算了,肯定还是4,这时候只是虚函数表变大了,指针又不会变。


6.5加成员变量

其实我已经很奇怪了,既然默认对齐到8字节,为什么虚函数表指针还胆敢输出4??

后来修改#pragma pack 试了一下发现当值设置到8的时候,仍然是按照4对齐的。为什么?

class Base0
{
public:
	Base0();

	void funcA(int);
	void funcA(int, int);
	virtual void funcA(int, int, int);
	virtual ~Base0();
	
	int a;
	int b;
	char str[5];
};

一开始我还以为这也算是struct和class的区别,不过我错了。后来突然反应过来,我这个类当中就没有一个字段超过了4个字节,所以你对齐到8字节有什么意义呢?对齐的字节值不仅跟你的设置有关,也跟你的具体字段有关。

这时的sizeof结果为 4+4+4+5 = 17--->20

把int a 和int b替换为一个8字节的类型,比如double类型,结果就是另一番景象:

4+8+5=17 --->24 对齐到8字节了!


总结:

1、指针的大小跟编译器有关,32位为4字节,64位为8字节。

2、数组作为形参时退化为指针。

3、对齐值跟pragma pack设置和结构体(或者类)当中的最大字段有关,前者的值大于后者时,取后者,小于后者时取前者。







  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值