一、前言

编译环境是vs2010(32位)。

  1. <span style="font-size:18px;">#include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. using namespace std;
  5. typedef struct
  6. {
  7. int a;
  8. char b;
  9. }A_t;
  10. typedef struct
  11. {
  12. int a;
  13. char b;
  14. char c;
  15. }B_t;
  16. typedef struct
  17. {
  18. char a;
  19. int b;
  20. char c;
  21. }C_t;
  22. void main()
  23. {
  24. char*a=0;
  25. cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;//4
  26. cout<<sizeof(<span style="color:#ff0000;">*a</span>)<<endl;//1--这个能理解
  27. cout<<sizeof(<span style="color:#ff0000;">A_t</span>)<<endl;//8
  28. cout<<sizeof(<span style="color:#ff0000;">B_t</span>)<<endl;//8
  29. cout<<sizeof<span style="color:#ff0000;">(C_t</span>)<<endl;//12
  30. }</span>

为什么是这样的结果啊?

二、语法

sizeof有三种语法形式,如下:

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

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

3) sizeof object; // sizeof 对象;

三. 指针变量的sizeof 

既然是来存放地址的,那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中,一个指针变量的返回值必定是4(以字节为单位),在64位系统中指针变量的sizeof结果为8。

  1. <span style="font-size:18px;">#include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. using namespace std;
  5. int main()
  6. {
  7. char *a=0;
  8. char* pc = "abc";
  9. int* pi;
  10. string* ps;
  11. char** ppc = &pc;
  12. <span style="color:#ff0000;">void (*pf)();// 函数指针</span>
  13. cout<<sizeof(<span style="color:#ff0000;">char</span>)<<endl; //1
  14. cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;//4
  15. cout<<sizeof(<span style="color:#ff0000;">*a</span>)<<endl;//1
  16. cout<<sizeof(<span style="color:#ff0000;">pc</span>)<<endl; //4(指针)
  17. cout<<sizeof(<span style="color:#ff0000;">pi</span>)<<endl;//4(指针)
  18. cout<<sizeof(<span style="color:#ff0000;">ps</span>)<<endl; //4(string型指针)
  19. cout<<sizeof(<span style="color:#ff0000;">ppc</span>)<<endl; //4(指针)
  20. cout<<sizeof(<span style="color:#ff0000;">pf</span>)<<endl;//4
  21. }</span>

指针变量的sizeof值与指针所指的对象没有任何关系,正是由于所有的指针变量所占内存

大小相等,所以MFC消息处理函数使用两个参数WPARAM、LPARAM就能传递各种复杂的消息结构(使用指向结构体的指针)。

四.、数组的sizeof

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

  1. <span style="font-size:18px;">#include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. using namespace std;
  5. int main()
  6. {
  7. char b1[]="123";
  8. int b2[3];
  9. <span style="color:#ff0000;">int c1=sizeof(b1)/sizeof(char);
  10. int c2=sizeof(b1)/sizeof(b1[0]);
  11. int c3=sizeof(b2)/sizeof(int);</span>
  12. int c4=sizeof(b2)/sizeof(b2[0]);
  13. cout<<sizeof(b1)<<' '<<c1<<' '<<c2<<endl;//4 4 4
  14. cout<<sizeof(b2)<<' '<<c3<<' '<<c4<<endl;//12(3*4 依赖int) 3 3
  15. }</span>

1.数组长度

char a1[] = "abc";

int a2[3];

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

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

2.数组元素个数

int c1 = sizeof( a1 ) / sizeof( char ); // 总长度/单个元素的长度

int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 总长度/第一个元素的长度

3.数组“传址”(数组为函数参数)

我们可以思考一下,下面的c3,c4值应该是多少呢?

  1. <span style="font-size:18px;">void foo3(char a3[3])
  2. {
  3. int c3 = sizeof( a3 ); // c3 ==
  4. }
  5. void foo4(char a4[])
  6. {
  7. int c4 = sizeof( a4 ); // c4 ==
  8. } </span>

也许当你试图回答c4的值时已经意识到c3答错了,是的,c3!=3。这里函数参数a3已不再是数组类型,而是蜕变成指针,相当于char* a3,为什么?仔细想想就不难明白,我们调用函数foo1时,程序会在栈上分配一个大小为3的数组吗?不会!数组是“传址”的,调用者只需将实参的地址传递过去,所以a3自然为指针类型(char*),c3的值也就为4。

五. 结构体的sizeof

结构体相对而言最容易碰到而且最容易出错。让我们先看一个结构体:

struct S1
{
char c;
int i;
};

编译得到结果为8!

我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所占的内存字节数,好吧,那就让我们来看看S1的内存分配情况:

S1 s1 = {  a , 0xFFFFFFFF };

定义上面的变量后,加上断点,运行程序,观察s1所在的内存,你发现了什么?

以我的Vs为例,s1的地址为0x0012FF78,其数据内容如下:

0012FF78: 61 CC CC CC FF FF FF FF

发现了什么?怎么中间夹杂了3个字节的CC?看看MSDN上的说明:

When applied to a structure type or variable, sizeof returns the actual size,

which may include padding bytes inserted for alignment.

原来如此,这就是传说中的字节对齐啊!一个重要的话题出现了。

1.怎么判断内存对齐规则,sizeof的结果怎么来的,牢记如下3条规则(在没有#pragma pack宏的情况下):

(1)数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

(2)结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a 里存有struct b,b里有char,int,double等元素,那么b应该从8的整数倍开始存储)。

(3)收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

  • 类型

    对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)

    Char

    偏移量必须为sizeof(char)即1的倍数

    int

    偏移量必须为sizeof(int)即4的倍数

    float

    偏移量必须为sizeof(float)即4的倍数

    double

    偏移量必须为sizeof(double)即8的倍数

    Short

    偏移量必须为sizeof(short)即2的倍数

  1. <span style="font-size:18px;">#include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. using namespace std;
  5. typedef struct bb
  6. {
  7. int id;             //[0]....[3]
  8. double weight;      //[8].....[15]      原则1
  9. float height;      //[16]..[19],总长要为8的整数倍,补齐[20]...[23]     原则3
  10. }BB;
  11. typedef struct aa
  12. {
  13. char name[2];  //[0] [1]
  14. int id;     //[4]....[7]   原则1
  15. double score;// [8]...[15]
  16. short t;    //[16]...[17]
  17. BB b;   //[24]...[47]  原则2、3
  18. }AA;
  19. int main()
  20. {
  21. cout<<sizeof(BB)<<endl; //为24
  22. cout<<sizeof(AA)<<endl; //为48
  23. return 0;
  24. }</span>

2.带#pragma pack()

在代码前加上一句#pragma pack(1),bb就是4+8+4=16。Aa就是2+4+8+2+16=32.

六、联合体的sizeof

结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存,所以整个联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型,这里,复合类型成员是被作为整体考虑的。

所以,下面例子中,U的sizeof值等于sizeof(s)。

  1. <span style="font-size:18px;">union U
  2. {
  3. int i;
  4. char c;
  5. AA s;
  6. };</span>

七、类的sizeof

1、空类的sizeof是1。空类是指没有成员的类,类中的函数不占空间,除非是虚函数。

如:

  1. <span style="font-size:18px;"> class A
  2. {
  3. public:
  4. A(){}
  5. ~A(){}
  6. void fun(){}
  7. };</span>

sizeof(A)是1.

注:

  1. <span style="font-size:18px;"> class A1
  2. {
  3. public:
  4. A1(){}
  5. ~A1(){}
  6. void fun(){}
  7. char a[0];
  8. };</span>

sizeof(A1)也是1.(VC6.0下编译)

2、若类中包含成员,则类对象的大小只包括其中非静态成员经过对齐所占的空间,对齐方式和结构体相同。如:

  1. <span style="font-size:18px;">class A
  2. {
  3. public:
  4. int b;
  5. float c;
  6. char d;
  7. };</span>

sizeof(A)是12.

  1. <span style="font-size:18px;">class A
  2. {
  3. public:
  4. static int a;
  5. int b;
  6. float c;
  7. char d;
  8. };</span>

sizeof(A)是12.

3、若类中包含虚函数,则无论有几个虚函数,sizeof类都等于sizeof(数据成员)的和+sizeof(V表指针,为4),如:

  1. <span style="font-size:18px;">class Base
  2. {
  3. public:
  4. Base(){cout<<"Base-ctor"<<endl;}
  5. ~Base(){cout<<"Base-dtor"<<endl;}
  6. int a;
  7. virtual void f(int) {cout<<"Base::f(int)"<<endl;}
  8. virtual void f(double){cout<<"Base::f(double)"<<endl;}//共有两个虚拟函数(virtual)
  9. };</span>

sizeof(Base)为8.

4、对于子类,它的sizeof是它父类成员(无论成员是public或private),再加上它自己的成员,对齐后的sizeof,如:

  1. <span style="font-size:18px;">class A2
  2. {
  3. public:
  4. int a;
  5. private:
  6. char b;
  7. };
  8. class A3:public A2
  9. {
  10. public:
  11. char b;
  12. short a;          }</span>

sizeof(A3)是12.

5、对于子类和父类中都有虚函数的情况,子类的sizeof是它父类成员(无论成员是public或private),再加上它自己的成员,对齐后的sizeof,再加4(虚表指针)。如:

  1. <span style="font-size:18px;">class Base
  2. {
  3. public:
  4. Base(){cout<<"Base-ctor"<<endl;}
  5. ~Base(){cout<<"Base-dtor"<<endl;}
  6. int a;
  7. virtual void f(int) {cout<<"Base::f(int)"<<endl;}
  8. virtual void f(double){cout<<"Base::f(double)"<<endl;}
  9. };
  10. class Derived:public Base
  11. {
  12. public:
  13. Derived(){cout<<"Derived-ctor"<<endl;}
  14. int b;
  15. virtual void g(int){cout<<"Derived::g(int)"<<endl;}
  16. };</span>

sizeof(Derived)是12.

6、对于虚继承的子类,其sizeof的值是其父类成员,加上它自己的成员,以及它自己一个指向父类的指针(大小为4),对齐后的sizeof。如:

  1. <span style="font-size:18px;">#include   <iostream.h>
  2. class   a
  3. {
  4. private:
  5. int   x;
  6. };
  7. class   b:   virtual   public   a
  8. {
  9. private:
  10. int   y;
  11. };
  12. class   c:   virtual   public   a
  13. {
  14. private:
  15. int   z;
  16. };
  17. class d:public   b,public   c
  18. {
  19. private:
  20. int   m;
  21. };
  22. int main(int argc,char* argv[])
  23. {
  24. cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;
  25. cout<<sizeof(<span style="color:#ff0000;">b</span>)<<endl;
  26. cout<<sizeof(<span style="color:#ff0000;">c</span>)<<endl;
  27. cout<<sizeof(<span style="color:#ff0000;">d</span>)<<endl;
  28. return   0;
  29. }   </span>

在VC6.0下调试结果为

4

12

12

24

sizeof(b)和sizeof(c)相同,都是4+4+4=12。

sizeof(d)是sizeof(b)(为12)+sizeof(c)(为12)-b和c相同的部分(a的成员,大小是4)+d自己的成员(大小为4)=24

7、对于既有虚继承又有虚函数的子类,其sizeof的值是其父类成员(计算虚表指针大小+4),加上它自己的成员(计算虚表指针大小+4),以及它自己一个指向父类的指针(大小为4),对齐后的sizeof。

  1. <span style="font-size:18px;">class Base
  2. {
  3. public:
  4. Base(){cout<<"Base-ctor"<<endl;}
  5. ~Base(){cout<<"Base-dtor"<<endl;}
  6. virtual void f(int) {cout<<"Base::f(int)"<<endl;}
  7. virtual void f(double){cout<<"Base::f(double)"<<endl;}
  8. };
  9. class Derived:virtual public Base
  10. {
  11. public:
  12. Derived(){cout<<"Derived-ctor"<<endl;}
  13. virtual void g(int){cout<<"Derived::g(int)"<<endl;}
  14. };</span>

sizeof(Base)=4

sizeof(Derived)=12 (父类虚表指针大小4+自己虚表指针大小4+子类指向父类的一个指针大小4=12)

七、C结构体之位域(位段)的sizeof

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

(一)位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:

struct 位域结构名

{

位域列表

};

其中位域列表的形式为:

类型说明符 位域名:位域长度

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

  1. <span style="font-size:18px;">struct bs
  2. {
  3.   int a:8;
  4.   int b:2;
  5.   int c:6;
  6. }data; </span>

说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:

1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:

  1. <span style="font-size:18px;">struct bs
  2. {
  3. unsigned a:4
  4. unsigned b:5 /*从下一单元开始存放*/
  5. unsigned c:4
  6. }</span>

2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度。

3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

  1. <span style="font-size:18px;">struct k
  2. {
  3. int a:1
  4. int :2 /*无位域名,该2位不能使用*/
  5. int b:3
  6. int c:2
  7. }; </span>

(二)位域的使用

  1. <span style="font-size:18px;">#include <iostream>
  2. #include <memory.h>
  3. using namespace std;
  4. struct A
  5. {
  6. int a:5;
  7. int b:3;
  8. };
  9. int main(void)
  10. {
  11. char str[100] = "0134324324afsadfsdlfjlsdjfl";
  12. struct A d;
  13. memcpy(&d, str, sizeof(A));
  14. cout << d.a << endl;
  15. cout << d.b << endl;
  16. return 0;
  17. }</span>

复制代码

在32位x86机器上输出:

$ ./langxun.exe

-16

1

解析:在默认情况下,为了方便对结构体内元素的访问和管理,当结构体内的元素长度都小于处理器的位数的时候,便以结构体里面最长的元素为对其单位,即结构体的长度一定是最长的数据元素的整数倍;如果有结构体内存长度大于处理器位数的元素,那么就以处理器的位数为对齐单元。由于是32位处理器,而且结构体中a和b元素类型均为int(也是4个字节),所以结构体的A占用内存为4个字节。

上例程序中定义了位域结构A,两个个位域为a(占用5位),b(占用3位),所以a和b总共占用了结构A一个字节(低位的一个字节)。

当程序运行到14行时,d内存分配情况:

高位 00110100 00110011   00110001    00110000 低位(ASCII码)

'4'       '3'       '1'          '0'

其中d.a和d.b占用d低位一个字节(00110000),d.a : 10000, d.b : 001

d.a内存中二进制表示为10000,由于d.a为有符号的整型变量,输出时要对符号位进行扩展,所以结果为-16(二进制为11111111111111111111111111110000)

d.b内存中二进制表示为001,由于d.b为有符号的整型变量,输出时要对符号位进行扩展,所以结果为1(二进制为00000000000000000000000000000001)

(三)位域的对齐

  如果结构体中含有位域(bit-field),那么VC中准则是:

  1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

  2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

  3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;

  系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。

转自:http://blog.csdn.net/u014186096/article/details/48290013#

全面总结sizeof的用法(定义、语法、指针变量、数组、结构体、类、联合体、位域位段)的更多相关文章

  1. [C]语法, 知识点总结(二. 结构体, 类别名, static, const)

    结构体 定义: struct Student{ // 定义结构体Student, stu是创建的对象         char a[17]; // 结构体里面可以有多种不同类型的变量         ...

  2. C语言定义一个指针变量

    10.2.1 定义一个指针变量 对指针变量的定义包括三个内容: (1) 指针类型说明,即定义变量为一个指针变量: (2) 指针变量名: (3) 变量值(指针)所指向的变量的数据类型. 其一般形式为: ...

  3. Android For JNI(五)——C语言多级指针,结构体,联合体,枚举,自定义类型

    Android For JNI(五)--C语言多级指针,结构体,联合体,枚举,自定义类型 我们的C已经渐渐的步入正轨了,基础过去之后,就是我们的NDK和JNI实战了 一.多级指针 指针的概念我们在前面 ...

  4. C语言基础知识点整理(函数/变量/常量/指针/数组/结构体)

    函数 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ...

  5. 字符串输出输入函数,const修饰符,内存分区,动态内存管理,指针和函数,结构体

    1.字符串输出输入函数 读入字符串的方法: 1) scanf 特点:不能接收空格 2) gets 特点:可以接受含有空格的字符串 ,不安全 3) fgets(); 特点:可以帮我们自动根据数组的长度截 ...

  6. C语言开发函数库时利用不透明指针对外隐藏结构体细节

    1 模块化设计要求库接口隐藏实现细节 作为一个函数库来说,尽力降低和其调用方的耦合.是最主要的设计标准. C语言,作为经典"程序=数据结构+算法"的践行者,在实现函数库的时候,必定 ...

  7. oracle函数、包、变量的定义和使用、重点”结构体和数组”

    函数 实例1:输入雇员的姓名,返回该雇员的年薪 create function fun1(spName varchar2) ,); begin +nvl(comm,) into yearSal fro ...

  8. C语言结构体指针(指向结构体的指针)详解

    C语言结构体指针详解 一.前言 一个指向结构体的变量的指针表示的是这个结构体变量占内存中的起始位置,同样它也可以指向结构体变量数组. *a).b 等价于 a->b. "."一 ...

  9. 【C++】结构体/结构体数组/结构体指针/结构体嵌套/函数参数/const

    一.结构体声明 struct Student { //成员列表 string name; int age; int score; }; //s3;定义时直接声明 int main() { struct ...

  10. 关于oc中出现的typedef的用法/定义函数指针

    typedef int (^calculateBlock)(int a,int b); 这里面typedef的作用只是给 calculateBlock取了一个 别名,说明以后可以直接使用. calcu ...

随机推荐

  1. Apache配置文件服务器

    配置Apache文件服务器: apache配置文件服务器httpd.conf中这两个模块必须不被注释LoadModule autoindex_module modules/mod_autoindex. ...

  2. Window 端口查询

    1. Window环境下查询端口使用情况 方法1: 使用netstat [-参数]|findstr [端口号]来直接查询某个端口的具体使用情况 示例: netstat -ano|findstr &qu ...

  3. C++ 实现Range类,用于常规遍历

    PYTHON的Range类非常好用,所以用C++来简单实现下:  // 实现Range类,用于遍历 // #include <string> class Range { public: / ...

  4. Git学习笔记(一)

    1.git clone https://github.com/miguelgrinberg/flasky.git cd flasky git checkout 1a 2.git reset --har ...

  5. asp.net 手工调用 WS(Get)方法:

    asp.net 手工调用 WS(Get)方法: 通过手工HttpWebRequest,HttpWebResponse来模拟调用.核心代码:string strurl="http://loca ...

  6. SOA Integration Repository Error:Service Provider Access is not available.

    在Oracle EBS Integration Repository中,打开一个Webservice,报了一个警告. 英文: Warning Service Provider Access is no ...

  7. 安装部署完office web apps 后,无法浏览Word

    安装部署完office web apps 后,在sharepoint 2010浏览器中浏览Word提示:“由于出现意外错误,Word Web App 无法打开此 文档 进行查看. 要查看此 文档,请在 ...

  8. Android开发:程序目录结构详解

    HelloWorld程序的目录结构概述 我们可以在文件夹中看到,HelloWorld程序的目录主要包括:src文件夹.gen文件夹.Android文件夹.assets.res文件夹. AndroidM ...

  9. mysqldump的使用

    mysqldump按导入:mysqldump -u用户名 -p密码 -h主机 数据库 < 路径eg:mysql -uroot -p1234 db1 < c:\a.txtmysqldump导 ...

  10. Unable to install breakpoint in

    Unable to install breakpoint inXXXX due to missing line number attributes.modify compiler options to ...