今天网上看到这么一个问题:

int array[5]= {}; sizeof(array)=?

int Fuction(int a[])
{
return sizeof(a);
}
Fuction(array)=?

很有意思的问题,大概很多人会认为结果是20、20,但其实正确的答案是20、4,也就是说,函数Fuction里的sizeof求值结果为4,这应该就是int*的字节大小,看起来sizeof求的是数组a[]的首指针字节大小。奇怪的地方也就是这里,一个在函数外面,一个在函数内部,sizeof的参数完全一样啊,为什么结果却完全不同呢?
这里大家对sizeof的用法大致了解,其实出现这个结果的原因不在sizeof,当sizeof传入的参数是数组时,其结果的确是该数组占内存的大小,不过,c++中函数有一个特性:
数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址。
也就是说,在int Fuction(int a[])中,编译器认为传入的参数不是数组,而是把数组a[]的首地址指针传进去,所以这时sizeof(a)求的是指针而不是数组的大小。
至于为什么编译器为什么会这样,我想大概是考虑到栈空间的限制吧。C++里函数的参数由栈空间保存,而这个栈空间的大小一般都是受限的,比较小,所以可能会出现栈空间耗尽导致函数无法调用的情况,为了防止这种情况的发生,就需要做一些优化,所以才会将传入的数组参数的首指针进栈,这样可以节约很多的栈空间。
另外想到以前面试时碰到的一个问题,也是跟sizeof有关。

struct MEM
{
int a;
char b;
float c;
}test;
sizeof(test) =
这个问题考察的其实不再是sizeof的用法了,而是字节对齐的知识:在计算类似结构和类变量的大小就必须考虑字节对齐问题。为了CPU存取的速度最快,C++在处理数据时经常把结构变量中的成员的大小按照4或8的整数倍的地址存储,这就叫数据对齐(data alignment)。这样做可能会浪费一些内存,但理论上速度快了。
比如在上面的例子中,如果把test的起始地址认为是0,那么test.a的起始地址为0,test.b的起始地址为0 + sizeof(int) = 4,然后test.c的地址呢?注意,test.c的起始地址不是0 + sizeof(int) + sizeof(char) = 5,而应该是0 + sizeof(int) + sizeof(float) = 8,在这里,C++为了存取效率的考虑,将char后面的3个字节空出来,这都是为了而将test.c的起始地址放在4的整数倍的位置,这样是为了更方便这个float数据的存取。因为32位CPU存取数据都是按照4个字节来操作,也就是说一次性的读出或者写入4字节(也就是32bit)的数据是最快的,所以将数据字节对齐后,虽然浪费了一些内存,但是存取数据却更快了。
我们来分析一下,在这里如果没有字节对齐的话,CPU如何取出float呢?这就要分三步来了:首先,取出偏移地址为4的4字节数据(这四字节里第一个字节是char,后面三个是float的),然后在便宜地址为8的地方取4个字节(这四个字节里第一个字节是float的),最后再把两部分(前面的3字节和后面的1字节)组合起来,这样需要两次读操作和一次组合。但是字节对齐后,只需要一步读操作(即在偏移地址为8的地方取4个字节),效率显然提高很多。

最近的一些技术整理(20120109)

前段时间一直没写博客,昨天更新了一篇,今天突然又来了兴致,那就再更新一篇吧(所以说啊,治疗拖延症最好的方法就是现在开始做) 这篇还是一些技术的整理,...

阅读全文

C++模板的几个应用

C++的模板其实是个挺纠结的东西,用的不好的话,编译的一堆错误够你调到崩溃,但要是用的好呢,又确实非常方便,我们来看看 一.获取数组长度 比如 ...

阅读全文

又见C++诡异问题

用C++越久,越是觉得C++太多陷阱,真是防不胜防。 我们看这样一段代码: C++ #include <stdio.h> using namespace std; ...

阅读全文

18则回应给“sizeof相关的两个C++特性”

  1. nonomori说道:

    前两天上课时刚看到这个字节对齐. 以前struct时,怎么都想不到顺序不同,占用内存也会不同..
    可能看这个图更形象点吧
    http://gallery.me.com/inonomori#100036/Screen%20shot%202010-04-09%20at%201.53.48%20AM

    [回复]

    ian 回复:

    谢谢nonomori提醒,这种问题用图表来演示确实更加直观,我也不用敲那么多字了~~

    [回复]

  2. iOver说道:

    领教了,以前考试的时候也遇到过类似的问题,后来也是通过查GOOGLE查出来的。感谢博主给我又一次温故而知新的机会。

    [回复]

    ian 回复:

    呵呵,这种问题在各位大侠们看来还是很浅显的,只是c++基础而已,不过正如iOver所说,温故知新也是有好处的^.^

    [回复]

  3. egmkang说道:

    这些东西本质上讲,不是C++的东西,是从C继承来的.
    sizeof(array)是编译时算出来的,没什么奇特的;
    并且C里面,本质上是没有数组的,所有的数组都是指针,所以传递的也是指针.

    [回复]

    Dante 回复:

    嗯,呵呵,不过字节对齐这里还是比较容易出错的,因为经常要用到指针偏移……

    [回复]

  4. c++初学者说道:

    您好,我是名c++初学者,
    您的例子:
    int array[5]= {}; sizeof(array)=?

    int Fuction(int a[])
    {
    return sizeof(a);
    }
    请问怎么在函数里获得形参传来的数组的长度?

    [回复]

    Dante 回复:

    这个数组长度是要单独作为参数传进来的,
    这也是为什么main函数的参数会有两个
    argc 和 argv的原因
    你那个函数:
    return sizeof(a);
    实际上是返回了指针的长度(就是4)。

    [回复]

    ian 回复:

    要想获取数组的长度,有这样一个方法:c++分配的数组首指针地址前16个字节处存储的就是这个数组的长度信息,可以直接读取这个内存的数据以直接获取数组长度,比如
    int A[100];
    ……
    int length = *(int*)((char*)A – 16); //length值即数组长度

    [回复]

    MadPer 回复:

    为啥我用ian的代码,最后得出来的是乱码?

    [回复]

    ian 回复:

    呃,刚才用g++测试了一下,发现真的不好用,以前我是在visual studio下这么用的,看来这个特性是编译器相关的

    [回复]

    MadPer 回复:

    用不起visual studio的泪奔呀…

    [回复]

    MadPer 回复:

    大家所谓的数组长度是指的什么?占用的空间?还是包含多少个元素?
    好吧,我承认,我是新手~~

    [回复]

    ian 回复:

    一般说数组长度,指的都是数组中元素的个数,不过我上面说的那种方法,获取到的是数组占内存的字节数

    [回复]

    MadPer 回复:

    这个,求个解.emacs的自动开新行模式,就是我输入”;”或者其他的一些符号,会自动帮我换行,”{“”}”之类的也是如此,不知道ian用过没有.
    问题是,如果我想写”a[] = {1, 2, 3};”这样的句子,在输入大括号的时候也会换行,这个怎么办?

    [回复]

    ian 回复:

    先汗一个,回复竟然不在emacs那个帖子下面,找了半天才发现在这里
    这个是因为emacs默认的c++-mode打开了auto-newline,你进c++-mode之后,菜单c++–>toggle–>auto newline 取消就好了
    或者在脚本里加入如下配置行:
    (c-toggle-auto-newline -1)

    [回复]

    MadPer 回复:

    哦~~多谢~~ :)

    [回复]

  5. Lissa说道:

    If my problem was a Death Star, this artcile is a photon torpedo.

    [回复]

发表评论