sizeof操作符-结构体与类大小

导读

  sizeof是C/C++一个难点,当在自定义类上应用sizeof操作符时,总会出现意想不到的结果,下面,我们就来探讨一下sizeof这个操作符!

目录

1. sizeof与strlen的区别

2. sizeof作用于结构体

3. 字节对齐问题

4. sizeof作用于类

正文

1. sizeof与strlen的区别

  这是老生常谈的问题了,下面举一个例子大家就明白了。  

char buf[] = "hello world!";
cout<<sizeof(buf)<<endl;
cout<<strlen(buf)<<endl;

  output: 13   12

  结论:sizeof只计算字符串真正的长度,不带字符串结束标记(‘\0‘);而strlen则计算‘\0’

  还有一点需要注意的是,sizeof是操作符,strlen是函数,两者之间存在着根本的区别。

2. sizeof作用于结构体/类

  先看下面程序输出的结果:

#include<iostream>
#include <string.h>

using namespace std;

typedef struct
{
    int a;
}A_t;

typedef struct
{
    char a;
}B_t;

typedef struct
{
    int a;
    char b;
}C_t;

typedef struct
{
    int a;
    char b;
    char c;
}D_t;

typedef struct
{
    char a;
    int b;
    char c;
}E_t;

int main()
{
    cout<<"sizeof(A_t):"<<sizeof(A_t)<<endl;
    cout<<"sizeof(B_t):"<<sizeof(B_t)<<endl;
    cout<<"sizeof(C_t):"<<sizeof(C_t)<<endl;
    cout<<"sizeof(D_t):"<<sizeof(D_t)<<endl;
    cout<<"sizeof(E_t):"<<sizeof(E_t)<<endl;
    return 0;
}

  output:

$./sizeof
sizeof(A_t):4
sizeof(B_t):1
sizeof(C_t):8
sizeof(D_t):8
sizeof(E_t):12

  为什么输出的结果和我们的猜想是不符合的呢?到底是什么在影响结构体的大小?这就是下面我们要来分析的。

  这一切的原因可以归结为字节对齐问题!

3. 字节对齐问题

  3. 1 对齐跟数据在内存中的位置有关。

  如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。

  比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。

  3.2 为什么需要字节对齐

  需要字节对齐的根本原因在于CPU访问数据的效率问题。

struct A
{
    char a1;   char a2;
    int b;
    char c;
};

  假设上面整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,

  第二次取从0x00000004-0x00000005的一个 short然后组合得到所要的数据,如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为 char,

  然后组合得到整型数据。而如果变量在自然对齐位置上,则只要一次就可以取出数据。一些系统对对齐要求非常严格,比如sparc系统,如果取未对齐的数据会发生错误,举个例:
  

char ch[8];
char *p = &ch[1];
int i = *(int *)p;

  
  运行时会报segment error,而在x86上就不会出现错误,只是效率下降。

  3.3 正确处理字节对齐

  对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:
  数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。
  联合 :按其包含的长度最大的数据类型对齐。
  结构体: 结构体中每个数据类型都要对齐。
  比如有如下一个结构体:
  

struct stu{
   char sex;
   int length;
   char name[10];
  };
  struct stu my_stu;

  //内存分布图 

 由于在x86下,GCC默认按4字节对齐,它会在sex后面跟name后面分别填充三个和两个字节使length和整个结构体对齐。于是我们sizeof(my_stu)会得到长度为20,而不是15.

4. sizeof作用于类

  C++的类与C的结构体存在着不同,但是同样会涉及到字节对齐的问题,在这方面是相同的,不同之处在于C++的类可以包含成员函数(包括虚函数),数据成员(静态成员)。因此,在sizeof作用于此时也有不同之处!

4.1 空类

  当类中什么也不定义时,使用sizeof作用于该类会不会是零呢?

class A
{
};

sizeof(A);

  答案是不会,结果是1。很吃惊吧,那么为什么是1呢?

  我们简单分析一下:假设结果是0,那么我们定义类A的两个对象a, b时,a 和 b 占用的内存大小也应该是0,那么用什么来标识这两个不同的对象呢?显然没有任何办法。因此,这个假设不成立。

  那么就算不是0,为什么是1而不是其他数值呢?例如4。不是要考虑字节对齐吗?这是因为编译器在类中增加了一个字节。字节对齐是在含有数据成员的情况下才发生的事情,因此,在空类情况下不用考虑!

4.1 虚函数内存分布

  虚函数的内存分布被陈皓分析的很透彻,可以去看看他的这篇博客!C++ 虚函数表解析

  下面再来看一个例子:

class A
{
public:
    A()
    {
        cout<<"A()"<<endl;
    }

    virtual ~A()
    {
        cout<<"~A()"<<endl;
    }
};

class C
{
public:
    C(){
        cout<<"C()"<<endl;
    }

    virtual ~C() {
        cout<<"~C()"<<endl;
    }

    virtual void getvalue()
    {
        cout<<"getvalue()"<<endl;
    }
};

  那么,sizeof(A)、sizeof(C)分别是什么呢?看懂了陈皓上面那篇博客你大概就应该知道了,带有虚函数的类需要用到虚函数表,而虚函数表只需要一个指针指向就OK了!因此,两者均为4

4.2 数据成员内存分布

  同样,陈皓两篇文章很好地分析了对象的内存布局,分别是:C++ 对象的内存布局(上)C++ 对象的内存布局(下)大家可以好好看看牛逼之人是如何分析的。

  下面仅举例子说明一下:  

class A
{
private:
    static int i;
};

class B
{
private:
    static int i;
    static double d;
    int i1;
    double d1;
};

class C
{
public:
    static void fun()
    {
    }
};

  那么sizeof(A) 和 sizeof(B)、sizeof(C)又是什么呢?答案分别是1、12、1。为什么呢?

  sizeof(A)为什么不是4呢?数据成员的类型是int整形啊,怎么就之占一个字节呢?

  这是因为,static 数据成员与成员函数都不计算入sizeof中,也就是这两种成员都是不会使得编译器在类中增加什么的。

时间: 2024-08-06 10:52:17

sizeof操作符-结构体与类大小的相关文章

Runtime的初步认识——结构体与类

Runtime的初步认识 Runtime的初步认识 Runtime介绍 类与结构体的关系 结构体解析 结构体的作用 Runtime介绍 学习一个东西至少要先知道它是个啥,你一定听说过"运行时是 Objective-C 的一个特色",这里的"运行时"就是指 runtime 了. runtime是在自 iOS 平台开放并基于 Objective-C 语言开发后的一个编程语言上的高级技术. 学习runtime的目的并不是为了开发,而是让你更好的理解 Objective-C

结构体与类

最近开始想学习下Swift,下了Swift3.0中文版PDF,间断性看了几天,发现看不进去,所以还是从实践上学习吧,直接创建工程搭代码,多问度娘. 在此记录下对结构体与类的理解,之前学OC很少碰自定义结构体,所以理解上比较模糊混淆.好吧,我不是个合格的初级开发者. 结构体与类很相似,如初始化和属性访问,定义语法区别关键字struct和class. 在日常开发中,Swift在两者选择上建议第一选择是结构体 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font:

C#结构体和类的区别

结构体和类的区别 1. 值类型与引用类型 结构是值类型:值类型在堆栈上分配地址,所有的基类型都是结构类型,例如:int 对应System.int32 结构,通过使用结构可以创建更多的值类型 类是引用类型:引用类型在堆上分配地址 堆栈的执行效率要比堆的执行效率高,可是堆栈的资源有限,不适合处理大的逻辑复杂的对象.所以结构处理作为基类型对待的小对象,而类处理某个商业逻辑 因为结构是值类型所以结构之间的赋值可以创建新的结构,而类是引用类型,类之间的赋值只是复制引用 注:   a.虽然结构与类的类型不一

C++中结构体和类的区别

在C++中,结构体是一种特殊形态的类. 结构体和类的唯一区别就是:  结构体和类具有不同的默认访问控制属性. 类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型(private) 结构体中,对于未指定任何访问控制属性的成员,其访问控制属性为公有类型(public) C++中,不使用结构体丝毫不会影响程序的表达能力.C++之所以要引入结构体,是为了保持和C程序的兼容性. 但有时仍会在C++中使用结构体,是因为,可以使用结构体将不同类型数据组成整体,方便于保存数据.(若用类来保存,因类中成

结构体和类的唯一区别就是类函数没有加说明是私有而结构体函数是公有

结构体和类的唯一区别就是              类函数没有加说明是私有                   而   结构体函数是公有

Swift结构体与类

在面向过程的编程语言(如C语言)中,结构体用得比较多,但是面向对象之后,如在C++和Objective-C中,结构体已经很少使用了.这是因为结构体能够做的事情,类完全可以取而代之.而Swift语言却非常重视结构体,把结构体作为实现面向对象的重要手段.Swift中的结构体与C++和Objective-C中的结构体有很大的差别,C++和Objective-C中的结构体只能定义一组相关的成员变量,而Swift中的结构体不仅可以定义成员变量(属性),还可以定义成员方法.因此,我们可以把结构体看做是一种轻

介绍C#结构体与类区别

1. 结构体与类定义方式 结构体定义使用struct类定义使用class 结构体: struct testDemo{ int num; void action(){ } } 类: class testDemo{ int num; void action(){ } } ================== 2. 结构体与类的成员初始化 结构体成员变量在声明时不能赋值,而类在声明变量时会自动写入构造函数完成赋值(即只能定义,不能执行). 结构体中隐式构造函数存在,不能手动写,其余构造函数在手动编写中

[转]C#中的结构体与类的区别

C#中的结构体与类的区别 经常听到有朋友在讨论C#中的结构与类有什么区别.正好这几日闲来无事,自己总结一下,希望大家指点. 1. 首先是语法定义上的区别啦,这个就不用多说了.定义类使用关键字class 定义结构使用关键字struct.在语法上其实类和结构有着很多相似的地方. 定义类的语法 1 class Person 2 { 3 private string name; 4 private int age; 5 6 public void SayHi() 7 { 8 Console.WriteL

C++结构体 和类

1 #include <iostream> 2 using namespace std; 3 4 struct father 5 { 6 /* 7 virtual bool set() = 0; 8 virtual bool get() = 0; 9 */ 10 11 virtual bool set(int a, int b) = 0; 12 virtual bool get(int a, int *b) = 0; 13 14 }; 15 struct jack :public father