c关键字-sizeof的种种

On November 14, 2010, in 语言学习, C语言, by sponge

熟悉c的人都知道,sizeof是一个关键字而不是一个宏或者库函数什么的,他的值是在编译时确定的,如果这个不了解,可以现看看这篇文章这篇文章。 既然如此,让我们先看下面几个小例子:

sizeof(int);
sizeof(char);
sizeof(double);

上面三行sizeof的值是多少呢?这里我们假定在32位的x86系统下。我们会得到答案:4,1,8。这个没什么吧,大多数人都应该知道。那么,下面这个:

sizeof(int);
sizeof(long);

在32位x86下,这两个是多少呢?4,8?

实际上,答案是4,4。我们需要注意,long类型在32位系统下是32位的。那么,64位下结果又如何呢?8,8?其实答案是4,8。另一个需要注意的是,64位下的int是32位的

上面只是热热身,现在,让我们看sizeof的下面几种情形:

1、sizeof一个结构体。

这个我就不说啥了,具体的参考这篇文章。至于空的结构体,下面会解释。

2、sizeof数组、指针等 先看下面两个例子:

char a[100];
char b[100]="helloworld!";
char c[]="helloworld!";
char* d=b;
sizeof(a);
sizeof(b);
sizeof(c);
sizeof(d);

在32位x86系统下,以上各是多少呢?

答案是:100,100,12,4。

为什么不是100,12,12,12呢? sizeof一个数组名,返回的是数组的大小,不管你数组里面放的什么数据。所以,第一个数组大小是100,第二个数组大小是100,第三个数组大小是12(别忘记"\0")。第四个呢?第四个不是一个数组名,而是一个指针!32位下指针大小永远是4,不管你是指向一个数组还是一个int还是一个char。 好,这个问题搞清楚之后,看下面这个程序:

int func(char a[100])
{
    printf("%d\n", sizeof(a));
}
int main()
{
    char *m = "helloworld!";
    func(m);
    char n[100]="helloworld!";
    func(n);
}

这个程序会打印出什么结果呢?

答案是:4, 4。 为什么结果都是4?不是应该返回数组长度么?

这里出现又一个需要注意的地方:在作为参数传递的时候,数组名会退化为指针。也就是说,这里的func里的参数,虽然看上去是个数组名,但实际上还是个指针。 你了解了么?下面是几个练习,自己实验下^_^

char *p = NULL;
sizeof(p);
sizeof(*p);
 
int a[100];
sizeof(a);
sizeof(a[100]);
sizeof(&a);
sizeof(&a[0]);

懒得实验?答案分别是4,1,400,4,4,4。为什么?自己想想。

3、sizeof一些诡异的东西

(enum,空类,空struct) 所谓的诡异的东西,就是一些你想到的想不到的东西拿来sizeof。比如说sizeof一个enum类型是多少?一个空struct呢?一个空类呢? 这些诡异的东西在标准C中都没有作出规定,很大程度上都是编译器和系统结构相关的。 先来看一个:

int main()
{
    enum week{Mon, Tue, Wed, Thu, Fri, Sat, Sun};
    printf("%d\n", sizeof(enum week));
}

这个你会得到什么样的结果呢?28? 在gcc或者vc下运行一把,你会得到答案:4。

为什么呢? 实际上,enum具体有多大取决于编译器的实现,目前大多数的编译器都将其实现为int类型。也就是说这里的enum被当作int类型(当然,使用上不一样)。这不是一成不变的,有些编译器,如VC++允许下面这种定义:

//注意这是个cpp文件
enum Color : unsigned char
{
   red, green, blue
};
// assert(sizeof(Color) == 1);

enum先说到这里,那么一个空的结构体是多大呢? 如果你擅长C语言,你可以很快的写出下面的程序:

//this is a *.c file
struct node{
 
}Node;
int main
{
    printf("%d\n", sizeof(Node));
}

很快,你可以验证出来结果是0。 没错,这很好理解。但是,如果你用的是c++呢?

//this is a *.cpp file
struct node{
 
}Node;
 
class node2{
 
}Node2;
 
int main()
{
    printf("%d\n", sizeof(Node));
    printf("%d\n", sizeof(Node2));
}

(不怎么会写c++的我表示压力很大)以上这个c++的例子结果是多少呢?

为什么会得到1,1这个结果呢? 换句话说就是为什么sizeof一个空类和空结构体在c++下就是1呢?

这个原因要追朔到c++标准中的一句话:“no object shall have the same address in memory as any other variable”, 用中国话简单点说就是:不同的对象之间应该有不同的地址(为什么会有这样的规定?看这里)。

既然每个对象都必须有不同的地址,让我们假设上面代码中的Node的size是0,想想会出现什么样的后果?

Node a;
Node b;

a的大小是0,b的大小是0,那么,a和b的地址是不是很可能重复了?

所以,为了保证不同的对象拥有不同的地址,最简单的方法就是保证所有类型的大小都不是0

所以,为了保证这点,大多数c++编译器都会给空结构或类加上一个冗余的字节保证类型不为空。

我的解释的清楚么?如果我的表达能力不够好,看这里,解释的足够详细。 当然,还有其他很多我没有想到的诡异的东西可以拿来sizeof,如果你想到了,欢迎跟我分享。至于c++中的sizeof类,基类,派生类,足够可以作为一个专门的文章了,先不再这里说,等我学会C++的再写一下相关内容(-_-")。

最后,给一个稍微给力点的程序,大家看看最终得到的结果是什么(注意这是一个c++程序):

//this is a cpp file
typedef   struct   weekday_st
{
        enum   week     {sun=123456789,mon,tue,wed,thu,fri,sat,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak};
        enum   day{monring,   moon,   aftermoon};
}weekday_st;
 
int   main(int   argc,   char   *argv[])
{
        printf( "sizeof(weekday_st)=%d\n ",   sizeof(weekday_st));
        printf( "sizeof(weekday)=%d\n ",   sizeof(weekday_st::week));
        printf( "sizeof(day)=%d\n ",   sizeof(weekday_st::day));
        return  0;
}

参考书目:《C专家编程》 《C语言深度解剖:解开程序员面试笔试的秘密 》

anyShare分享到:
Tagged with:
 

15 Responses to “c关键字-sizeof的种种”

  1. sagi says:

    想起了淘宝那道笔试题

  2. dutor says:

    数组名退化的那段代码里面&被"转义"了。

  3. zhy says:

    int a[100];
    sizeof(a);

    这个的结果是否是400?

    ps
    想转载下你的文章,有空能再学习下,行不呵呵

  4. c++0x says:

    貌似在windows x64下long是32位。

    • sponge says:

      @c++0x 谢谢,我身边没有64位的windows来测试,不过在手册上发现了这句话:
      In 64-bit Microsoft® Windows®, this assumption of parity in data type sizes is invalid. Making all data types 64 bits in length would waste space, because most applications do not need the increased size. However, applications do need pointers to 64-bit data, and they need the ability to have 64-bit data types in selected cases. These considerations led to the selection of an abstract data model called LLP64 (or P64). In the LLP64 data model, only pointers expand to 64 bits; all other basic data types (integer and long) remain 32 bits in length.
      没错,64位的windows下的long确实是32位的,谢谢!

  5. caoeryingzi says:

    博主,我检查了一下,空的结构体和类的大小输出都是1.不是0啊。我用的是VC6.0下的纯C编程和纯C++编程。在linux下,正是博主写的这种情况。

    • sponge says:

      @caoeryingzi 这个我也不是很确定,因为手头找不到有VC的机器。。。不过VC本身就是面向C++的编译器,而空结构体的size在C的标准中是没有规定的,C++中才有,所以我觉得你说的情况应该是对的,先mark一下,等我实验后修正 呵呵 谢谢!

  6. Kain says:

    赞!非常清楚

  7. yylang1987 says:

    楼主犯了一个基本错误. sizeof 只跟编译器设置相关, 跟运行实际机器无关.

    • sponge says:

      @yylang1987 理论上,C和C++的东西都可以由编译器随便设置,操作系统和硬件一点都不重要,编译器可以随意模拟硬件层,可以将int设置成随便的字节,C标准也没有规定sizeof是多大,但是编译器的实现讲求效率,所以在不同的系统结构下,一个正常点的编译器都不会违背这个结构的规则。所以,sizeof是强依赖于结构的。

Leave a Reply

Note: Commenter is allowed to use '@User+blank' to automatically notify your reply to other commenter. e.g, if ABC is one of commenter of this post, then write '@ABC '(exclude ') will automatically send your comment to ABC. Using '@all ' to notify all previous commenters. Be sure that the value of User should exactly match with commenter's name (case sensitive).