C语言综合1 ---sizeof 大全

  前言:教学时,发现经常有一些重要的C语言知识要点,在底层开发应用广泛,但从没有一本教材详细讲解,并且有让人信服的论证.因此在教学里我把这一些要求全部总结在一起,通过学生不断反馈,形成这几本文章.

-----------------------------------------------------------------------------------------------------------------------------------------------------------

内容提要

l        sizeof操作综述

l        sizeof()各种样例

n        基本类型

n        不同CPU/操作系统的变型

n        静态/动态空间

n        指针

n        复合数据类型

n        位域

n        C++对象

l        sizeof()练习 

 

sizeof操作综述

 

sizeofC关键字,sizeof()是单目表达式,其基本用法是求一个数据类型或表达式所占存储空间的字节数.由于C可以定义很复杂的数据结构,sizeof的求值也会有各种复杂的计算.因此很多面试题通常用人工计算sizeof()的结果,以检验开发者对C语言各个方面的掌握程度.

首先要确认一点,sizeof()的结果依赖于CPU的字长和操作系统本身的设置.如常用的32CPU的字长就是32bit,一般Windows操作系统下,这样整数(int)和长整数(long)均为32bit4字节长,short型为2字节长.但是dos,Turbo Csizeof(int)=sizeof(short)=2.(待求证)

64位的CPU,字长为64bit,但是基本类型int很多时候为保持软件兼容性,仍然是4byte.只在Windows下的_int64Linux long long 类型才是64bit,8个字节.

因此,sizeof()结果,必须要首先明确CPU的类型,有时还要确定操作系统.如果是嵌入式软件开发人员的测试,没有指出环境可以认为是有错的.

下列各种类型,如果没有特别指明,都是32 bit CPU ,Windows操作系统.

 

sizeof()样例

基本类型

sizeof()主要能对两类对象进行操作,

l        sizeof(数据类型)

数据类型可以是基本数据类型,char,int,也可以是复合或自定义的数据类型,如结构等.

l        sizeof(表达式)

表达式,首先要对表达式求值.然后再计算结果的数据类型的宽度.如果是常量表达式,还需要牵涉到常量会被默认转换类型的问题.

l        sizeof 表达式

用于表达式,sizeof操作可以写成这样 sizeof  var_name

 int aa; printf(“%d/n”,sizeof aa);

   

   32CPU,假设Windows(Linux也一样),将会有如下基本数据类型取值

     sizeof(char) = 1 ; sizeof(unsigned char)= 1

     sizeof(short)= 2 ; sizeof(unsigned short)= 2

     sizeof(int)= 4 ; sizeof(unsigned int)= 4

     sizeof(long)= 4 ; sizeof(unsigned long)= 4

     sizeof(void *)= 4 ; //所有的指针都是4Byte宽度.

sizeof(float) =4; sizeof(double) = 8;

 

 

表达式的求类型宽度是以表达式结果类型为准

 int aa; sizeof(aa) = 4;

 

如果是常量表达式,则有一个默认类型转换问题

  所有整数常量默认转换为 整数, 所以 sizeof(20) = 4

  所有小数默认为转换为 double,所以 sizeof(20.1) = 8

 

不同体系结构的sizeof取值

对于不同字长的CPU和操作系统,sizeof()对同一类型的取值有不同结果.特别是在嵌入式开发领域,经常要面对不同环境.因此在不同环境下同一类型宽度取值,必须有所了解.

除了上节的32CPU的提示以外.以下在几种特殊环境的各种类型的取值

 

l        8位单片机

开发环境:Keil μVision3 v3.53

CPU:     基于8051Atmel AT89S52

单片机无法直接对类型进行sizeof操作,因此采用第二种操作,即对变量进行sizeof操作

  因为手头没有单片机开发板,以下是是委托我的学生龙金东做的测试,在他的环境的实测结果.

    sizeof (char) = 1

sizeof (short) =2

       sizeof (int)  = 2

sizeof (long) = 4     

       sizeof(float) = 4

       sizeof(double) = 4

    sizeof(char *) = 3 //实测结果

 

l        64CPU

C 编程语言并没有提供一种机制来添加新的基本数据类型,因此在新的CPU,C语言只能修改相应基本数据类型(即修改long之类数据宽度).或者添加新的基本类型(如增加long long类型)

. C/C++仅仅定义了这些基本数据类型之间的关系,并没有定义严格定义它们的字长。在不同的平台上,根据编译器不同的实现,各种基本类型(integer,Long pointer)有不同字长宽度.

各个产商的编译器根据自己的需要选择不同字长模式.现在已知的编译模型是

64位平台上的字长模型有 LP64ILP64LLP64

32位平台上的字长模型有ILP32LP32

字长模型里LLong,IInteger,PPointer. LP64表示在这个模式下,Long Pointer宽度为64bit,integer32bit,基余模式依次型推.

 

 

LP32

ILP32

LP64

LLP64

ILP64

char

8

8

8

8

8

short

16

16

16

16

16

int

16

32

32

32

64

long

32

32

64

32

64

long long(int 64)

n/a

n/a

n/a

64

n/a

指针(pointer)

32

32

64

64

64

 

64 bit Unix/Linux  采用LP64模型,int仍为32bit,longlong long 64.

Win32 采用ILP32模型,int,long ,pointer 均为32bit

Win64 采用LLP64 模型, int,long 32bit , int64,pointer64bit

 

  不同模型下,均有如下规则:

   sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) = sizeof(size_t)

 

LP64,LLP64的函数返回值均为64bit

 

 

静态/动态空间

对于静态数组,sizeof取值的其静态空间的长度

 char ary[100] ; sizeof(ary) =100*sizeof(char)

 

对动态分配的空间,结果值实际上是指针变量,因此指针变量在32CPU下是4

 char * p=malloc(100); sizeof(p)=4

 

字符串的情况有一些特殊.在初始化时,除了字符串本身内容以外,还要包括最后一个0字符的空间

 char ary[]=”hello”;sizeof(ary)=6 ;

char ary[]=”hello”表示静态分配相应空间,hello 5个字符+最后结束符

 

  char * ary=”hello”; 这个ary是一个指向一个常量地址的空间的指针,因此实际上是一个地址值,所以sizeof(ary) = 4;

 

指针

指针的重点并不在于sizeof()本身,而在于识别出复杂的指针类型.

复杂的指针定义里,函数指针,指向数组的指针和指针数组的定义大同小异.很容易错误识别.这样也造成sizeof()的错误.

 当然,从软件工程角度来说,任何复杂,难懂的指针定义是严重不可取的,你有N种清晰,简单的方法达到同样的效果.所以这一些复杂指针,那他们只存在于面试题之中吧,不要在你程序中出现.不仅会让其它人很难理解和维护程序,甚至也让你本人过一段时间都看不懂你的自己写的程序了.

  

首先一个指针的宽度一样,无论它指向哪种类型,32CPU.

   sizeof(void *)=sizeof(int *)=sizeof(char *)=sizeof(short *) = sizeof(long *)= 4;

 

l        函数指针

当然一个函数指针也是一样,函数指针变量定义的特点是指针变量和*要合并在一个括号,这个括号前是函数返回值,后面是参数定义(一定是带一对括号) 

char (* func1)(int ,char *); 表示func1是一个函数指针,指向的函数定义 char func(int ,char *),这样 sizeof(func1)= 4;

函数指针定义这样写主要为不和返回值为指针的函数声明冲突.上述定义没有括号含义就变了,表示声明一个返回值为char *的函数func1

char * func1(int ,char *);

当然,如果你强行对这个func1sizeof(),它的值也是 4,因为函数名相当于一个地址常量.

 

l        数组指针

指向一个数组的指针的格式与函数指针定义很相似,数组指针名和*要合在一个括号里,括号前是数组类型.括号后是用[]表示数组维数

 

char (* p)[10]; 表示是指向一个一维字符数组的指针,这个数组的维数是10,所以sizeof仍然是4,因为它仍然是一个指针变量.(sizeof(p)=4)

 

用括号也是为了防止数组指针定义与指针数组的定义冲突.象把上述定义的括号去掉,含义完全变了.表示一个一维数组,数组项是char *,维数是 10,

char * p[10];

这个按照数组的计算法则,sizeof(p)= sizeof(char *)*10=40;

 

l        混和定义

混和定义就是把指针数组,函数指针,数组指针全部合在一起定义,这样面试题你完全可以拒绝作答,完全是茴香豆的茴字的几种写法的问题.当然你想挑战一下自已,你可试着算一下下面的定义

char (*(*x())[])()

x右边是()是函数,右边是*是函数指针,再到右边[],返回的是数组,再看左边是指针,指针数组。再看右边()函数,左边是char,返回值。

总的来说是一个返回值是返回char型的指针函数数组的函数指针。

 

char (*(*x[3])())[5]

定义一个包含3个返回数组指针的函数指针的数组

 

char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173]

上面sizeof(x)是多少?

 

复合数据类型

复合数据类型一般是指结构或联合.

l        结构的求sizeof基本原则是各成员总尺寸之和.联合的sizeof值是最大成员的尺寸

如下结构

struct aa{  int a;  short b;  short c; }; sizeof(struct aa)=8;

 

如下联合

union bb{ int a; short b,char c;}; sizeof(union bb)=4;

      因为最大联合成员尺寸是int a,4

 

l        空结构(即没有任何成员的结构)sizeof=1,因为编译器要保存保证结构体的每一个实例在内存中都有独一无二的地址。,即便是一个空结构.编译器也是自动分配一个字节空间上来

     struct cc{}; sizeof(struct cc)=1;

 

     但是结构或联合牵涉字节对齐的问题.这样会造成不同宽度的字节对齐造成不同结果.关于什么叫字节对齐和采用字节对齐的原因,请参见相关章节.

以下均是在32CPU,基于4对齐的结果.

l        复合结构字节对齐有如下原则:字节对齐取宽度最大成员,如成员包含int,long则对齐采用4,如果最大数据宽度是short,则字节对齐采用2,如果只包含char,则字节对齐采用1.

 

   如下定义: struct aa{ int a;char b;short c;} ; sizeof(struct aa) = 8;

     最大长度成员是int ,采用4对齐,a占用四, short  b+char c= 3不足4,编译器会在b,c之间补齐一个字符,补成4个字节,因此最终结果是sizeof(int)+sizeof(char)+pading+sizeof(short) =8

将上述定义换一下成员位置

struct B{char b ;int a;short c;}; sizeof(struct B)=12;

最大长度成员是int ,采用4对齐,char b占一个位置,后续的int a 如果紧贴前面定义,将不能对齐,所以补3个字节,而后面的short c 占两个字节,也补成4个字节对齐

    

 

struct C{ char a[20]; short b; }; sizeof(C ) =22;注意这里最大尺寸是short,因此采用2对齐,所以总尺寸是 22.

 

位域

 位域是C语言定义一种特殊用法,即可以把某一种数据类型中的几位取出来使用,通用常用底层编程.并出现在结构定义当中.

位域的优点是定义清晰,操作某一位方便.在底层设置寄存器或者在网络包设置数据位时方便和清晰易懂.但一般在编程已经不太推荐使用,因为从某一个数中定义几位位域,牵涉到数字字节序的问题.同样位置的定义,要在不同字节序下定义二次.

 

以下是一些位域的定义,有两种定义方法

l        struct bs
{int a:7;int b:2;int c:1;int d};

    上述是连续定义同一类型位域

 

l        struct bs2

   { int a:7,

       b:2,

c:1;

int d;

   };

在同一数据里分别定义多个位域,每个用,隔开.

 

 

 

 

位域的sizeof()结果值有几种计算方法

l        所有同一类位域定义之和没有超过这种类型最大尺寸,则分配时,要分配整个数据类型的尺寸.

  struct b1{ int a;2 ;int c}; 虽然a只分配了2个位,但分配空间还是分配一个完整的int,这样sizeof(struct b1)=8;

 

Struct b2{int a:1;int b:2:int c:4;int a}; 这里a,b,c虽然是分独立声明三个位域,但是是同一类型,并且总和并没有超过32,因此这三个合并成一个int 分配,这样

  sizeof(struct b2)=8;

l        如果位域之和超过数据宽度总和,则自动多分配一个数据空间

struct b3{ int a:31;int b:4;int c} ;

 sizeof(b3) = 12; a+b的宽度超过32bit,编译器会多分配一个空间来容纳多余的位.

 

要提一句,后面几个情况是属于非常少见的情况,一般应用根本不推荐这样复杂的位域.一般即便是不用完一个数据空间,也要完整用的完,即形式始下

struct b4{ int a:3,resver:29; int c};

 

而且在位域的在内存的分布,似乎不是标准C的定义内容,因此即便在同一操作系统,不同编译器有可以能产生不同结果

如下列定义中,用VC++ 6.0DevCPP计算结果不一样(待求证,有做过测试通知我一声)

struct aa{

int a:1;

int b:1;

};

VC++ 6.0a,b和空间合并到一个int中,因此结果是4,DevCpp是把a,b分成两个int,结果是8,(看资料得出,待求证)

C++对象

严格的讲,C++对象做sizeof操作是危险的,因为C++并没有规定类在底层是如何实现的.sizeof取决于类在内部是如何实现的.

但大部分C++对象的实现还是一定规律.

类可以看成是结构的扩展.因此计算方法跟结构有点象.

l        如果对象没有虚函数,则对象的sizeof等于所有数据成员尺寸之和.

这跟普通成员函数没有太多关系,这些成员函数是放在全局代码区里的.

l        如果对象没有虚函数,并且有多重继承,则对象的sizeof等于所有基类的数据成员之和.

这样下列类的sizeof取值如下

 class aa {

   int a;

   int b;

   void test();

}; sizeof(aa) = 8=sizeof(a)+sizeof(b);

下列类csizeof取值情况

class a{int a;}; class b {int b;}; class c :public b,c{int c;};sizeof( c)的值是sizeof(a)+sizeof(b)+sizeof(int).

 

l        如果包含了虚函数,每个类的虚函数的指针都会被加入一个树形链表.链表的层次取决于继承关系.则每个包含虚函数的类,第一个位置是一般是指向这个链表对应类的入口.这样包含虚函数的类尺寸,就要包含这个指针空间.

class aa{

   int a;

   int b;

   virtual void test();

}; sizeof(aa)=12;

l        注意,无论一个类里有多少个虚函数,均只有一个vftable指针.因为虚函数指针只是保留在vftable列表里,类定义只是保留一个入口地址

 

 

 

l        类静态成员,是所有对象共享的,即静态成员本身不占用对象空间,而是占用系统全局的数据区.因此静态成员所占尺寸不列入类的总尺寸

   class aa{

   static int a;

   int b;

   void test();

}; sizeof(aa)=4; //只有b占用了类空间

 

l        注意,类定义是结构的扩展,因此结构字节对齐规则对类同样有效,因此下列类的定义仍然是8

class bb{

int a;

char b;

}; sizeof(bb)= 8;

 

 

sizeof() 练习题

 

a)      sizeof(int)

b)     sizeof(short)

c)      sizoef(long)

d)     sizeof(char)

e)      char buf[1024];sizeof(buf)

f)      char *buf=”test123”; sizeof(buf);

g)     struct student{int no; int age;}; sizeof(student);

h)     struct stu2{int no; char age;}; sizeof(stu2);

i)       union stu3{int no;int age;}; sizeof(union stu3);

j)       struct stu4{

int id;

int no:1,

   age:2;

};

sizeof(struct stu4);

k)     struct C{ char a[21]; short b; };sizeof(struct C);

l)       struct C{ char a[21]; int b; };sizeof(struct C);

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值