C++ sizeof的内存计算(1)

文章来自NoAlGo博客原创:C++ sizeof的内存计算(1)

内存控制是程序设计过程中非常关键的一环,C/C++中使用sizeof计算数据占用的内存大小是一个常见的手段,但是这个问题涉及到很多基础的编程细节,能够很好地反映一个程序员的基本功,成为了笔试面试常见的问题之一。
这里总结了一些常见的问题,鉴于篇幅问题,分成两部分进行,这里主要介绍比较基础的第一部分。

  1. C++ sizeof的内存计算(1)
  2. C++ sizeof的内存计算(2)

一 sizeof定义

在C++中sizeof的使用方法看上去像是一个函数,但实际上它在C++中被定义为一个关键字,同时也是一个单目运算符。sizeof运算的操作数可以是类型或者具体的变量,甚至可以是函数,当使用具体变量做操作数时括号可以省略。

sizeof在代码编译期间完成计算,编译器会把得到的结果插入到调用的地方,因此当使用表达式作为操作数时,它会返回表达式的计算类型的大小,但不会对表达式求值。同理,当使用函数调用作为参数时,它会返回函数返回类型的大小,但是不会执行函数体。

sizeof不能求得void类型的长度,但是可以计算void类型指针的大小,所有类型的指针的大小均为4个字节。

具体参考以下代码的具体输出。

void f(){};
int g(){ return 0; }
void test(){
	int a = 1;
	printf("sizeof(a)=%d\n", sizeof(a));	 //sizeof(a)=4
	printf("sizeof a=%d\n", sizeof a);		 //sizeof a=4
	//printf("sizeof(int)=%d\n", sizeof int);//错误,类型要加括号
	printf("sizeof(int)=%d\n", sizeof(int)); //sizeof(int)=4

	//rintf("sizeof(f)=%d\n", sizeof(f));     //错误,不能对函数名适用
	//printf("sizeof(f())=%d\n", sizeof(f()));//错误,不能对void类型适用
	printf("sizeof(g())=%d\n", sizeof(g()));  //sizeof(g())=4

	printf("sizeof(a++)=%d\n", sizeof(a++));  //sizeof(a++)=4
	printf("a = %d\n", a);					  //a = 1
}

二 数组

计算机中存放数组的是一段连续的内存,使用sizeof可以求得数组所占用的内存字节数。但是,sizeof只能求得静态分配的内存的数组的长度, 不能求得动态分配的内存的大小。对于动态分配内存的数组,其实质上是一个指针,进行sizeof运算时会得到该指针的大小,即4。

另外,我们同样可以使用sizeof求得数组中每个元素的大小,于是简单地使用除法可以求得数组的长度。

使用sizeof结合memset函数可以对数组进行初始化,注意memset初始化是以字节为单位进行填充的。如例子中对于数组a的初始化是失败 的,本意是想每个元素初始为5,结果是每个整数的4个字节都被初始化成5了,然后导致每个元素变成16进制的0×05050505(即整数的4个字节都是 5)。

void testArray(){
	int a[10];
	memset(a, 5, sizeof(a));   //尝试初始化为5(失败)
	printf("a[0]=%d\n", a[0]); //a[0]=84215045
	printf("%d\n", 0x05050505);//84215045,说明确实是逐字节填充

	char b[10];
	memset(b, 5, sizeof(b));  //尝试初始化为5(成功)
	printf("b[0]=%d\n", b[0]);//b[0]=5

	printf("sizeof(a)=%d\n", sizeof(a));			 //sizeof(a)=40
	printf("sizeof(a[0])=%d\n", sizeof(a[0]));		 //sizeof(a[0])=4
	printf("length(a)=%d\n", sizeof(a)/sizeof(a[0]));//length(a)=10

	int *c = new int[3];
	printf("sizeof(c)=%d\n", sizeof(c)); //sizeof(c)=4
}

这里可以先看下面的样例代码。

在数组定义的地方,使用sizeof求得的是数组的实际大小;但作为参数传进函数时,求得却是另外一个值。

原因是,C++数组作为函数参数在参数传递时,函数中的数组会为退化成为指针。数组参数是传地址调用,系统不会在函数调用栈内再给该数组分类一段空 间,而是把原来数组的指针传递进去,于是该变量变成一个单纯的指针。其大小同时变为固定的4个字节,于是不能用 sizeof(a)/sizeof(a[0])求取数组的长度,这也是一般传递数组参数时会把数组的长度一起传递的原因。

讲到这里需要进一步解释下数组名跟数组指针的区别。二者外形神似,均可以对数组内容进行访问,但是二者还是有一定的区别。

在《C和指针》一书第二版的142页中讲到,数组名的值是一个指针常量,是数组首元素的地址,只有在两种场合下,数组名并不是用指针常量来表示,就 是当数组名作为sizeof操作符和单目操作符&的操作数时。sizeof返回整个数组的长度,而不是指向数组的指针的长度。取一个数组名的地址 所产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。

数组指针是指向该数组首元素地址的一个指针变量,只是一个普通的变量,可以赋为任何的值。

在数组定义的地方使用sizeof可以求得数组的长度,但是数组在作为参数传递进函数时会退化称为数组指针,于是再使用sizeof只能得到指针的大小4。

void foo1(int a[3]){
	printf("In foo1: sizeof(a)=%d\n", sizeof(a)); //In foo1: sizeof(a)=4
}
void foo2(int *a){
	printf("In foo2: sizeof(a)=%d\n", sizeof(a)); //In foo2: sizeof(a)=4
}
void test(){
	int a[3] = {1, 2, 3};
	printf("In test: sizeof(a)=%d\n", sizeof(a)); //In test: sizeof(a)=12
	foo1(a);
	foo2(a);
}

另外,还有一个跟数组指针类似的概念,这里顺带讲解一下。指针数组,表示一个数组,它的内容是一个个的指针。数组指针,表示一个指向数组的指针。参看如下代码比较二者定义方式的区别及其具体的使用方法。

这里又涉及到刚刚讲到的数组名与数组名取地址的区别。一般情况下数组名的值是一个指针常量,是数组首元素的地址。对于一维整型数组,其为整型变量的 指针;对于二维整型数组,其为一维数组的指针。而数组名取地址所产生的是一个指向数组的指针。具体参考如下代码注释中标注的语法正确性。

void test(){
	int arr1[4];
	int arr2[4][4];

	int *(a[4]); //定义指针数组,括号可以省略
	a[0] = &arr1[0];
	*a[0] = 1;
	printf("arr1[0]=%d\n", *a[0]); //arr1[0]=1

	int (*b)[4];//定义数组指针,为长度4的一维int型数组的指针
	//b = arr1; //错误,arr1是int型变量的指针
	b = &arr1;	//正确,&arr1是长度4一维int型数组的指针
	b = arr2;	//正确,arr2是长度4一维int数组的指针
	//b = &arr2;//错误,&arr2是4x4二维int型数组的指针

	b = &arr1;
	(*b)[0] = 2; //小括号不可省略,因为中括号优先级>星号
	printf("arr1[0]=%d\n", (*b)[0]);//arr1[0]=2
}

另外,sizeof不能对不完整的数组求长度。如下代码所示,sizeof(A)试图求数组A的大小,但这里的声明extern int A[]只是告诉编译器A是一个整型数组,并没有明确指出其中包含多少个元素,编译器无法确定求得sizeof的值,编译出错。

而由于B数组的大小已经明确给出了,所以编译时编译器可以顺利求得它的大小,编译正常。

<p>&nbsp;</p>//File1.cpp
int A[5]={1, 2, 3, 4, 5};
int B[5]={5, 4, 3, 2, 1};

//File2.cpp
extern int A[];
extern int B[5];
printf("sizeof(A)=%d\n", sizeof(A)); //编译错误
printf("sizeof(B)=%d\n", sizeof(B)); //编译通过

最后用一个经典的题目做为这一部分的结尾:

void test(){
	double* (*a)[3][6];

	printf("sizeof(a)=%d\n", sizeof(a));
	printf("sizeof(*a)=%d\n", sizeof(*a));
	printf("sizeof(**a)=%d\n", sizeof(**a));
	printf("sizeof(***a)=%d\n", sizeof(***a));
	printf("sizeof(****a)=%d\n", sizeof(****a));

	//以上结果输出为:
	//sizeof(a)=4
	//sizeof(*a)=72
	//sizeof(**a)=24
	//sizeof(***a)=4
	//sizeof(****a)=8
}

解释如下,由小括号的优先级知,a是指针,那么它指向什么呢?由后面的定义知,它指向一个3×6的二维数组,但是这个二维数组的元素不是一般的 double,而是double*,即double类型的指针。于是,整体定义为,a是一个指向double*[3][6]类型数组的指针。

a是指针,所以sizeof(a)=4。

a是指向double*[3][6]类型的指针,*a就表示一个double*[3][6]的二维数组,所以sizeof(*a)=3*6*sizeof(double*)=72。

*a表示一个double*[3][6]的二维数组,**a就表示一个double*[6]的一维数组,所以sizeof(**a)=6*sizeof(double*)=24。

**a表示一个double*[6]的一维数组,***a就表示其中的元素double*,所以sizeof(***a)=4。

***a表示其中的元素double*,****a就表示一个double,所以sizeof(****a)=sizeof(double)=8。

时间: 2024-12-10 17:11:56

C++ sizeof的内存计算(1)的相关文章

C++ sizeof的内存计算(2)

文章来自NoAlGo博客原创 :C++ sizeof的内存计算(2) 内存控制是程序设计过程中非常关键的一环,C/C++中使用sizeof计算数据占用的内存大小是一个常见的手段,但是这个问题涉及到很多基础的编程细节,能够很好地反映一个程序员的基本功,成为了笔试面试常见的问题之一.这里总结了一些常见的问题,鉴于篇幅问题,分成两部分进行,这里主要介绍稍微进阶的第二部分. C++ sizeof的内存计算(1) C++ sizeof的内存计算(2) 三 复合类型 对于基本的数据类型,如char.int.

内存计算

如果说云计算这个新瓶装的是虚拟化+ SOA/网格计算+SaaS(软件即服务)的老酒,那么内存计算则重点是释放了计算这一部分的能量. 但是对内存计算经常有一些误解: 1.大容量内存很贵 2. 内存计算不会持久化:实际上几乎所有的内存计算中间件都提供多种内存备份.持久存储备份以及基于磁盘的swap空间溢出的策略. 3.内存计算要取代数据仓库:内存计算的目的是要改善那些需要OLTP和OLAP混合处理的可操作数据集(Operational Dataset)的计算,而非历史数据集(Historical d

zw版_Halcon图像数据交换和全内存计算.TXT

Halcon由于效率和其他原因,内部图像采用了很多自有格式,提高运行速度,但在数据交换方面非常麻烦. 特别是基于com.net控件模式的二次开发,无论是c,vb,还是delphi,目前都没有一种理想的解决方案. 目前,一般采用的以下两种方式进行转换: hobject桥接模式,内存拷贝,通道分离 外部硬盘文件交换 hobject桥接模式,参见:zw版[转发·台湾nvp系列Delphi例程]HALCON HImage与Bitmap格式转换 http://www.cnblogs.com/ziwang/

“非易失性内存”嫁接“内存计算”——高速安全的大数据时代来临

“非易失性内存”嫁接“内存计算” ——高速安全的大数据时代来临 题记 数据库奠基人Jim Gray:“磁带已经死了,磁盘已经落伍,闪存成为存储,内存才是王道”.“不管磁盘是否消融,闪存都是将来的一个趋势.” 石油一直直接影响着世累经济的发展速度和发展水平,现在,信息将发挥同样的作用.<经济学人>表示:“数据和信息日益成为商业的新能源,是一种与资本.劳动力并列的新经济元素”. 数据保护 大数据时代的机遇和挑战 大数据“风华正茂” 大数据时代,每两天的数据量就达到2ZB,相当于20世纪前人类文明所

这些组件分别处理Spark Core提供内存计算框架

Spark不仅支持Scala编写应用程序,而且支持Java和Python等语言进行编写,特别是Scala是一种高效.可拓展的语言,能够用简洁的代码处理较为复杂的处理工作. l通用性强 Spark生态圈即BDAS(伯克利数据分析栈)包含了Spark Core.Spark SQL.Spark Streaming.MLLib和GraphX等组件,这些组件分别处理Spark Core提供内存计算框架.SparkStreaming的实时处理应用.Spark SQL的即席查询.MLlib或MLbase的机器

弹性分布式数据集:一个支持容错的集群内存计算的抽象

注:本文章是翻译自:Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing 概要     我们提出了弹性分布式数据集(Resilient Distributed Datasets,简称RDDs)的概念,这是一个分布式内存的抽象,允许编程在大规模集群 上编写出以内存计算为基础的程序,并且该模型支持容错.RDD概念的提出主要启发于这样一种现象:有两种类型的应用程序,使用现

NOIP真题做题经历——MLE之内存计算

noip中,试题会有程序运行内存上限(MLE),这个内存上限指的是什么,怎样才能知道自己程序的运行内存? 下面就让我这个今天上午刚刚MLE的鸡来分析一下. 内存上限就是指程序运行时消耗的内存.如果你声明的是静态的(数组.普通变量等),那内存计算就是单位数*每个单位所用的字节数之和.(动态的东西,我们这里不做研究) #define ll long long ll a[400000] ; 定义 我们定义了一个ll类型的a数组.一个ll占8个字节,那么这个数组就占了400000 * 8 = 32000

c++编程思想(三)--c++中c 续,重点sizeof和内存对齐

之前理论性的太多,下面就是代码及理论结合了 1.sizeof()是一个独立运算符,并不是函数,可以让我们知道任何变量字节数,可以顺带学一下struct,union,内存对齐 内存对齐:为了机器指令快速指向地址值,编译器内部实际上会内存对齐,怎么理解了,以struct为例 先讲一下各个变量类型内存大小 所以struct理论上是:1+2+4+4+4+8+8 = 31,但是实际是 实际大小是32(1+2+1+4)+(4+4)+8+8 然后再把int和short位置调换 实际大小是40   (1+3)+

MYSQL使用内存计算

MySQL内存占用计算: global buffer+all thread buffer global buffer= innodb_buffer_pool_size 缓存表数据和索引数据,加速查询,并且使查询结果集保存在内以供重用,一般设置到主机内存的70%以下 +innodb_additional_mem_pool_size 存放数据字典信息以及一些内部数据结构,如果数据库的对象过多的话,该值可能需要调整,一般不做调整 +innodb_log_buffer_size 事务日志所使用的缓冲区,