导读

  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 ;
}

  output:

$./sizeof
sizeof(A_t):
sizeof(B_t):
sizeof(C_t):
sizeof(D_t):
sizeof(E_t):

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

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

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[];
char *p = &ch[];
int i = *(int *)p;

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

  3.3 正确处理字节对齐

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

struct stu{
   char sex;
   int length;
   char name[];
  };
  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中,也就是这两种成员都是不会使得编译器在类中增加什么的。

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

  1. 全面总结sizeof的用法(定义、语法、指针变量、数组、结构体、类、联合体、位域位段)

    一.前言 编译环境是vs2010(32位). <span style="font-size:18px;">#include<iostream> #inclu ...

  2. (五)羽夏看C语言——结构体与类

    写在前面   由于此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇 ...

  3. .NET性能优化-使用结构体替代类

    前言 我们知道在C#和Java明显的一个区别就是C#可以自定义值类型,也就是今天的主角struct,我们有了更加方便的class为什么微软还加入了struct呢?这其实就是今天要谈到的一个优化性能的T ...

  4. C#中结构体和类的区别

    结构体和类同样能够定义字段,方法和构造函数,都能实例化对象,这样看来结构体和类的功能好像是一样的了,但是他们在数据的存储上是不一样的 C#结构体和类的区别问题:这两种数据类型的本质区别主要是各自指向的 ...

  5. 10 结构体和类 - —— 《Swift3.0 从入门到出家》

    Swift中的面向对象5个要素:枚举.结构体.类.协议.扩展 面向对象研究的是对象,完成一件事情需要多个对象参与,是生活的映射 Swift中结构体和类非常相似,也就是结构体能完成类的所有功能.结构体是 ...

  6. 结构体struct,类class

    1.struct,值类型,结构体会自动生成初始化方法,class是引用类型 struct Person { var name : String var age : Int func simpleDes ...

  7. Swift结构体与类

    在面向过程的编程语言(如C语言)中,结构体用得比较多,但是面向对象之后,如在C++和Objective-C中,结构体已经很少使用了.这是因为结构体能够做的事情,类完全可以取而代之.而Swift语言却非 ...

  8. C C++ 中结构体与类

    先来说说C和C++中结构体的不同 a) C语言中的结构体不能为空,否则会报错 1>d:\myproject\visual studio 2013\projects\myc++\main.c(71 ...

  9. 浅析C#中的结构体和类

    类和结构是 .NET Framework 中的常规类型系统的两种基本构造. 两者在本质上都属于数据结构.封装着一组总体作为一个逻辑单位的数据和行为. 数据和行为是该类或结构的"成员" ...

随机推荐

  1. openerp binary filed import export

    1: user  xmlrpc 2: use csv file to export import but want to change the csv model field_size_limit

  2. Mvc controller单元测试 Mock Url对象

    被测试Action 包含有Url对象的代码: data = new data { title = ds.Name, icon = "folder", attr = new { id ...

  3. asp.net mvc 强类型视图中传入List 数据到控制器

    问题来源: 在和一位技术老师聊天时,老师问我一个mvc 表单提交的问题,问道:怎样在表单提交的时候,将 带有 List 属性的对象传入控制器? 这时,我有点呆了,以前一直都好像是 单一属性的表单提交, ...

  4. tomcat架构分析-索引

    出处:http://gearever.iteye.com tomcat架构分析 (概览) tomcat架构分析 (容器类) tomcat架构分析 (valve机制) tomcat架构分析 (valve ...

  5. Fiddle的应用

    在Composer中输入测试的网址: 可能会发生一下情况,这意味着需要在左侧面板中选择一项,来看回应:   选择一个请求后,选择TextView,看原生的回应(Response)内容

  6. bzoj3541: Spoj59 Bytelandian Information Agency

    Description        BIA机构内部使用一个包含N台计算机的网络.每台计算机被标号为1..N,并且1号机是服务器.计算机被一些单向传输线连接着,每条数据线连接两台计算机.服务器可以向任 ...

  7. 简明解释算法中的大O符号

    伯乐在线导读:2009年1月28日Arec Barrwin在StackOverflow上提问,“有没有关于大O符号(Big O notation)的简单解释?尽量别用那么正式的定义,用尽可能简单的数学 ...

  8. Node.js回调概念

    什么是回调? 回调是一个异步等效的功能.在完成特定任务回调函数被调用. Node大量使用了回调.Node的所有的API都支持回调这样的一种方式. 例如,一个函数读取一个文件可能开始读取文件,并使得下一 ...

  9. BZOJ 1487 无归岛

    Description Neverland是个神奇的地方,它由一些岛屿环形排列组成,每个岛上都生活着之中与众不同的物种.但是这些物种都有一个共同的生活习性:对于同一个岛上的任意两个生物,他们有且仅有一 ...

  10. 检查网口流量与前10名流量大IP

    此脚本包含的功能有: 1.实时监控任意网卡的流量 2.统计10秒内平均流量 3.统计每个端口在10秒内的平均流量,基于客户端和服务端端口统计.可以看出哪些端口占流量比较大,对于web服务器,一般是80 ...