sizeof()

一、sizeof概念

sizeof()是一个操作符(operator),不是函数。

它的功能是返回一个对象或者类型名的长度,类型为size_t,单位为字节。

size_t是一个依赖编译器的值,定义在头文件stddef.h,一般定义为 typedef unsigned int size_t;

二、sizeof有三种语法:

(1)sizeof(object);//sizeof(对象);

(2)sizeof(type_name);//sizeof(类型);

(3)sizeof object;//sizeof 对象。

三、sizeof与strlen()比较

sizeof是操作符,strlen()是函数。

strlen()计算字符数组的字符数,以'\0'为结束判断,不计算为'\0'的数组元素。

而sizeof计算数据(包括数组、变量、类型、结构体等)所占内存空间,用字节数表示,包含'\0'所占空间。

eg1:

char  str1[] = “Hello”;

sizeof (str1 ) = 6    (自动加了'\0')  

strlen (str1 ) = 5    (字符串的长度)  

char  str2[5] = {'H','e','l','l','o'};

sizeof (str2 ) = 5    (字符数组的大小)

strlen (str2)  = 未知 (该字符串缺少结束符'\0')

char  str3[6] = {'H','e','l','l','o','\0'};

sizeof (str3)  = 6    (字符数组的大小)

strlen (str3)  = 5    (该字符串的长度为5)

  sizeof:计算字符数组大小(包括’\0’)

 strlen:计算长度,到’\0’为止

eg2:

char arr[10] = "Hello"; 

strlen(arr) = 5; //有效字符串长度为5.

sizeof(arr) = 10;//数组长度为10。

 
四、基本数据类型的sizeof

基本数据类型指short、int、long、float、double这样的简单内置数据类型,由于它们都是和系统相关的,所以在不同的系统下取值可能不同。一般的,在32位编译环境中:

sizeof(char) = 1;

sizeof(int) = 4;

sizeof(long) = 4;

sizeof(float) = 4;

sizeof(double) = 8.

五、指针类型的sizeof

指针用于存放地址,它等于计算机内部地址总线的宽度,所以32位计算机中,一个指针变量的返回值是4字节(与指针变量的类型无关)。可以预计,在64位系统中指针变量的sizeof等于8。

sizeof(p) = 4;

六、数组的sizeof

数组的sizeof值等于数组所占用的内存字节数,如: 

char a1[] = "abc"; 

int a2[3]; 

sizeof( a1 ); // 结果为4,字符串末尾还存在一个NULL终止符 

sizeof( a2 ); // 结果为3*4=12(3*sizeof(int)) 

数组元素个数的求法:

sizeof(a1)/sizeof(*a1);

当数组作为函数参数时将从数组类型蜕变为指针类型:

void Function1( char p[],int num ){ 

 sizeof (p) = 4 ;//(数组在作为函数参数时均化为指针,p此时为char*类型)

 }

七、联合体的sizeof

联合体在内存结构上是重叠式的,各成员共享一段内存,所以整个联合体的sizeof就是最大成员的sizeof值。


union U{
    int i;
    char c;
     double  d;
};//sizeof(U) = sizeof(double) = 8.
八、结构体的sizeof

结构体空间大小的计算遵循的原则是字节对齐。

类或者结构体的sizeof大小必须是类中最大数据类型的整数倍。

CPU访问对齐的数据的效率是最高的,因此通常编译浪费一些空间来使得我们的数据是对齐的。

总体上遵循两个原则:

1)整体空间是占用空间最大的成员(的类型)所占字节数的整倍数

   2)数据对齐原则----内存按结构成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整倍数,如果不够则补齐,以此向后类推。举例如下:

struct s1{
char a;

short b;
int c;
char d; 
};//sizeof(s1) = 12;

struct s2{
char a;
char b;
int c;
short d;
};//sizeof(s2)= 12.

s1中char占1个字节,short占2个字节,int占4个字节,因此结构体的自身对齐字节大小为4字节。于是a和b要组成4字节,以便与c的4字节对齐。其存储方式具体如下图:

其中空白方格无数据,是浪费的内存空间,共占用8字节内存。

s2的存储结构如下:

 这里有个陷阱,对于结构体中的结构体成员,不要认为它的对齐方式就是他的大小,看下面的例子:

struct s1{

char a[9];

};//sizeof(s1) = 9;

struct s2{

double d;

};//sizeof(s2) = 8;

struct s3{

s1 s;

char a;

};//sizeof(s3) = 10;

struct s4{

s2 s;

char a; 

};//sizeof(s4) = 16

  s1的对齐方式是1(char),s2是8(double),所以在s3和s4中才有这样的差异。

还有一个影响sizeof的重要参量--编译器的pack指令。它是用来调整结构体对齐方式的,不同编译器名称和用法略有不同,VC6中通过#pragma pack实现,也可以直接修改/Zp编译开关。#pragma pack的基本用法为:#pragma pack( n ),n为字节对齐数,其取值为1、2、4、8、16,默认是8,如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准,即是说,结构体成员的偏移量应该取二者的最小值,公式如下:

offsetof( item ) = min( n, sizeof( item ) )

eg:

#pragma pack(1)// 必须在结构体定义之前使用

struct s4{

char c;

int i;

};sizeof(s4) = 5;

#pragma pack(1),对齐值变为1,所以sizeof = 5。此时内存紧凑,不会出现内存浪费,但效率降低了。效率之所以降低,是因为:如果存在更大字节数的变量时(比1大),比如int类型,需要进行多次读周期才能将一个int数据拼凑起来。

九、类的sizeof(类似于结构体的sizeof)

1)多重继承

class A{};

class B{};

class C : public A, public B{};

class D : virtual public A{};

class E : virtual public A, virtual public B{};

sizeof(A) = 1,sizeof(B) = 1(空类大小为1,编译器安插一个char给空类,用来标记它的每一个对象)

sizeof ( C ) = 1      (继承或多重继承后空类大小还是1)

sizeof(D) = 4 (虚继承时编译器为该类安插一个指向父类的指针,指针大小为4)

sizeof(E) = 8  (指向父类A的指针与父类B的指针,加起来大小为8)

2)函数与虚函数

编译器为每个有虚函数的类都建立一个虚函数表(其大小不计算在类中),并为这个类安插一个指向虚函数表的指针,即每个有虚函数的类其大小至少为一个指针的大小4

class A {

public:

   int a;

   void Function();

};

class B {

public:

   int a;

   virtual void Function();

};

class C : public B{

public:

   char b;

};

class D : public B{

public:

   virtual void Function2();

     /*

     Int a;  继承来的

     Vptr *p; 虚函数指针

*/

};

class E{

public:

   static void Function();

};

class F{

static int a;

}

sizeof (A) = 4   (内含一个int,普通函数不占大小)

sizeof (B) = 8   (一个int ,一个虚函数表指针)

sizeof (C) =12   (一个int ,一个虚函数表指针,一个char ,再加上数据对齐)

sizeof (D) = 8   (一个int ,一个虚函数表指针,多个虚函数是放在一个表里的,所以虚函数表指针只要一个就行了)

sizeof (E) = 1   (static 函数不占大小,空类大小为1)

sizeof(F) = 1    (static数据成员不属于class所有)

    static成员变量是存储在静态区当中的,它是一个共享的量,因此,在为这个类创建一个实例对象的时候,是无需再为static成员变量分配空间的,所以,这个类的实例对象所需要分配的空间是要排除static成员变量的,于是,当sizeof计算类的大小的时候会忽略static成员变量的大小。

3)父类的私有数据

虽然在子类中不可用,但是是可见的,因此私有的数据还是要占子类的大小

class A{

private:

   int a;

};

class B:public A{};

sizof(B) = 4;    (内含一个不可用的父类的int)

十、含有位域结构体的sizeof

    有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
        位域的使用和结构成员的使用相同,其一般形式为: 位域变量名 位域名:位域允许用各种格式输出。
  例如: 
struct bs
{
 int a:8;
 int b:2;
 int c:6;
}; 

  位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如: 

struct bs
{
 int a:8;
 int b:2;
 int c:6;
}data; 

  说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:
  1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: 
struct bs
{
 unsigned a:4
 unsigned :0 /*空域*/
 unsigned b:4 /*从下一单元开始存放*/
 unsigned c:4

   在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
  2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
  3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: 
struct k
{
 int a:1
 int :2 /*该2位不能使用*/
 int b:3
 int c:2
}; 
  从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。
参考文章:

http://blog.csdn.net/freefalcon/article/details/54839

http://blog.chinaunix.net/uid-26868581-id-3328170.html

http://blog.sina.com.cn/s/blog_715de2f50100pgs3.html

http://blog.chinaunix.net/uid-20726927-id-2455478.html
————————————————
版权声明:本文为CSDN博主「浪迹天涯_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wo17fang/article/details/51143237

  • 20
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值