介绍
sizeof 是一个关键字、操作符,也是一个编译时运算符
作用:返回一个对象或者类型所占的内存字节数
使用方法
sizeof(type_name);//sizeof(类型)
sizeof(object);//sizeof(对象)
注意:
sizeof 操作符不能用于函数类型,不完全类型或位字段
不完全类型:指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void 类型……
sizeof(void)
不是正确形式
基本数据类型
本质:将对象转换成对象类型进行计算,同种类型的不同对象其 sizeof 值相同
常见字长:
#include <iostream>
#include <string>
using namespace std;
int main() {
cout << "sizeof(bool)=" << sizeof(bool) << endl; // 1
cout << "sizeof(char)=" << sizeof(char) << endl; // 1
cout << "sizeof(short)=" << sizeof(short int) << endl; // 2
cout << "sizeof(int)=" << sizeof(int) << endl; // 4
cout << "sizeof(long)=" << sizeof(long int) << endl; // 4
cout << "sizeof(long long)=" << sizeof(long long) << endl; // 8
cout << "sizeof(float)=" << sizeof(float) << endl; // 4
cout << "sizeof(double)=" << sizeof(double) << endl; // 8
return 0;
}
表达式
编译器根据表达式的最终结果类型确定大小,sizeof 是编译时进行计算,与运行时无关,不会对表达式进行计算
#include <iostream>
#include <string>
using namespace std;
int main() {
int i = 8;
cout << "sizeof(i)=" << sizeof(i) << endl; // 4
cout << "sizeof(i=5)=" << sizeof(i = 5) << endl; // 4
cout << i << endl;//sizeof(i = 5)不会改变i的值
cout << "sizeof(i++)=" << sizeof(i++) << endl; // 4
cout << i << endl;//sizeof(i++)不会改变i的值
return 0;
}
指针
指针变量的 sizeof 值与指针所指的对象类型无关,与指针申请的空间大小无关,所有指针变量所占内存大小均相等
32 位系统中,返回 4;64 位系统中,返回 8
#include<iostream>
using namespace std;
char fun() {
return 'c';
}
int main() {
//指向char类型变量的指针
char c;
char* ptr_char = &c;
cout << "sizeof(ptr_char) = " << sizeof(ptr_char) << endl; // 8
//指向int类型变量的指针
int i;
int* ptr_int = &i;
cout << "sizeof(ptr_int) = " << sizeof(ptr_int) << endl; // 8
//指向double类型变量的指针
double* ptr_double = new double[10];
cout << "sizeof(ptr_double) = " << sizeof(ptr_double) << endl; // 8
//二级指针
char** two_ptr_char = &ptr_char;
cout << "sizeof(二级指针) = " << sizeof(two_ptr_char) << endl; // 8
//函数指针:指向函数的指针,指针函数:返回值是指针的函数
void (*ptr_f)();
cout << "sizeof(函数指针) = " << sizeof(ptr_f) << endl; // 8
//&fun:一个函数指针
cout << "sizeof(&fun) = " << sizeof(&fun) << endl; // 8
//fun():一次函数调用
cout << "sizeof(fun()) = " << sizeof(fun()) << endl;// 1,返回返回值的类型的大小
//(*fun)():一次函数调用
cout << "sizeof((*fun)()) = " << sizeof((*fun)()) << endl;// 1,返回返回值的类型的大小
return 0;
}
数组
数组所有元素所占用的大小
#include<iostream>
using namespace std;
void foo(int a[3]) {//当数组作为函数形参时,以指针类型进行传递
cout << "sizeof(a) = " << sizeof(a) << endl; // 8,a以指针传递,故a为指针
}
int main() {
//二维数组
int A[3][5];
cout << "sizeof(A) = " << sizeof(A) << endl; // 60=3*5*4,A的数据类型为int[3][5]
cout << "sizeof(A[0]) = " << sizeof(A[0]) << endl; // 20=5*4,A[0]的数据类型为int[5]
cout << "sizeof(A[4]) = " << sizeof(A[4]) << endl; // 20=5*4,A[4]的数据类型为int[5]
//虽然A[4]下标越界,但sizeof只关心数据类型,在编译阶段已经完成,不会造成运行错误
cout << "sizeof(A[0][0]) = " << sizeof(A[0][0]) << endl; // 4=1*4,A[0][0]的数据类型为int
cout << "sizeof(A[0][1]) = " << sizeof(A[0][1]) << endl; // 4=1*4,A[0][1]的数据类型为int
//字符串
char c[] = "abcdef";
cout << "sizeof(c) = " << sizeof(c) << endl; // 7=6+1('\0')
//指针数组=指针内存大小*元素个数
char* ch[10];
cout << "sizeof(ch) = " << sizeof(ch) << endl; // 80,ch是一个数组,数组的元素是指针
cout << "sizeof(*ch) = " << sizeof(*ch) << endl; // 8,*ch是一个指针,是数组的第一个元素
cout << "sizeof(**ch) = " << sizeof(**ch) << endl; // 1,**ch是一个char变量,是数组的第一个元素(指针)指向的变量
//数组指针
int* (*d)[3][6];
cout << "sizeof(d) = " << sizeof(d) << endl; // 8,d是一个指针,指向一个二维数组,二维数组的元素是int类型的指针
cout << "sizeof(*d) = " << sizeof(*d) << endl; // 144=3*6*8,*d是一个二维数组,是d指向的二维数组
cout << "sizeof(**d) = " << sizeof(**d) << endl; // 48=6*8,**d是一个一维数组
cout << "sizeof(***d) = " << sizeof(***d) << endl; // 8,***d是一个指针
cout << "sizeof(****d) = " << sizeof(****d) << endl; // 4,****d是一个int变量
//一维数组
int nums[] = { 1,2,3 };
cout << "sizeof(nums) = " << sizeof(nums) << endl; // 12=3*4
foo(nums);//参数以指针传递
return 0;
}
字符串
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main() {
const char* strPtr = "hyy";
char strs[] = "hyy";
string str = "h";
cout << "sizeof(strPtr) = " << sizeof(strPtr) << endl; // 8,strPtr是一个指针
cout << "sizeof(strs) = " << sizeof(strs) << endl; // 4,strs是一个字符数组,包括 '\0'
cout << "sizeof(str) = " << sizeof(str) << endl; // 40,str是一个string类对象,是string类所占的实际内存
cout << "sizeof(string) = " << sizeof(string) << endl; // 40
cout << "strlen(strPtr) = " << strlen(strPtr) << endl; // 3
cout << "strlen(strs) = " << strlen(strs) << endl; // 3
cout << "str.size() = " << str.size() << endl; // 1
return 0;
}
函数
返回函数返回值类型的大小,函数并不会被调用执行
sizeof(函数名(实参表))
PS:
- 不可以对返回值类型为空的函数求值
- 不可以对函数名求值
- 对有参数的函数,须写上实参列表
#include <iostream>
using namespace std;
int intfun() {
return 1;
}
double doufun(int a, double b) {
return a + b;
}
void voidfun() { }
int main() {
// cout << "sizeof(intfun) = " << sizeof(intfun) << endl; //编译失败
cout << "sizeof(intfun()) = " << sizeof(intfun()) << endl; // 4
cout << "sizeof(doufun(1, 1.5)) = " << sizeof(doufun(1, 1.5)) << endl; // 8,不可省略实参列表
// cout << "sizeof(voidfun()) = " << sizeof(voidfun()) << endl; //编译失败
return 0;
}
结构体
内存对齐
对其目的:减少访存指令周期,提高 CPU 存储速度
一般情况下,结构体所占内存大小并非元素本身大小之和
#include <iostream>
using namespace std;
struct S1 {
char c;
int i;
};
int main() {
cout << "sizeof(S1) = " << sizeof(S1) << endl; // 8
return 0;
}
// sizeof(S1) =/= sizeof(char) + sizeof(int)
结构体或类成员变量具有不同类型时,需进行成员变量的对齐
每个特定平台上的编译器都有自己的默认“对齐系数”(对齐模数)
32 位机对齐(默认)是按4字节对齐,而 64 位机(默认)是按8字节对齐
模数在不同平台值不同,可使用#pragmapack(n)
改变
对其原则:
- 结构体内存大小应按最大元素大小对齐,若最大元素大小超过模数,应按模数大小对齐
- 若结构体的最大元素大小超过模数,结构体的起始地址是可以被模数整除的;若最大元素大小没有超过模数,那么其起始地址是可以被最大元素大小整除
- 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,若有需要编译器会在成员之间加上填充字节(internal adding),首个成员从偏移量为 0 的位置开始存
- 结构体的总大小为结构体最宽基本成员类型大小的整数倍,若有需要编译器会在最末一个成员之后加上填充字节(trailing padding)
空结构体(不含数据成员)的 sizeof 值为 1
静态成员存放在静态存储区,不占用结构体的大小
成员函数不占用结构体的大小
#include <iostream>
using namespace std;
struct st0 {
char a; // 1 + pad(7)
char* a1; // 8
int c; // 4 + pad(4)
}; // 24
//a 的长度为 1,所占的地址为 0;
//a1 的长度为 8,偏移前的首地址为 1,因为首地址不是成员大小的整数倍,故需偏移,使其首地址为 8 即可,故加上 7 个填充字节;
//c 的长度为 4,首地址为 16,结构体的大小为 20,不是最宽基本成员(8)的整数倍,故需填充 4 个字节,使得结构体总大小为 24
struct st1 {
char a; // 1 + pad(7)
double b; // 8
int c; // 4
char d; // 1 + pad(3)
}; // 24
// a 的长度为 1,所占的地址为 0;
// b 的长度为 8,偏移前的首地址为 1,因为首地址不是成员大小的整数倍,故需偏移,使其首地址为 8 即可,故加上 7 个填充字节;
// c 的长度为 4,首地址为 16
// d 的长度为 1,首地址为 20,结构体大小为 21,不是最宽基本成员(8)的整数倍,故需填充 3 个字节,使得结构体总大小为 24
struct st2 {
char a; // 1
char a1; // 1 + pad(2)
int c; // 4
st1 st; // 24
char d; // 1 + aad(7)
}; // 40
// a 的长度为 1,所占的地址为 0;
// a1 的长度为 1,所占的地址为 1,首地址是成员大小的整数倍,故不需要偏移
// c 的长度为 4,偏移前的首地址为 2,因为首地址不是成员大小的整数倍,故需偏移,使其首地址为 4 即可,故加上 2 个填充字节;
// st 的长度为 24,首地址为 8,虽然首地址不是成员大小的整数倍,但因为成员大小超过默认模数,故以模数进行对齐
// d 的长度为 1,首地址为 32,结构体大小为 33,不是模数(8)的整数倍(因为最宽基本成员类型大小超过模数,故以模数进行对齐),故需填充 7 个字节,使得结构体总大小为 40
int main() {
cout << "sizeof(st0) = " << sizeof(st0) << endl; // 24
// 获取成员在结构体的地址偏移量
cout << "offsetof(st0, a) = " << offsetof(st0, a) << endl; // 0
cout << "offsetof(st0, a1) = " << offsetof(st0, a1) << endl; // 8
cout << "offsetof(st0, c) = " << offsetof(st0, c) << endl; // 16
cout << "sizeof(st1) = " << sizeof(st1) << endl; // 24
cout << "offsetof(st1, a) = " << offsetof(st1, a) << endl; // 0
cout << "offsetof(st1, b) = " << offsetof(st1, b) << endl; // 8
cout << "offsetof(st1, c) = " << offsetof(st1, c) << endl; // 16
cout << "offsetof(st1, d) = " << offsetof(st1, d) << endl; // 20
cout << "sizeof(st2) = " << sizeof(st2) << endl; // 40
cout << "offsetof(st2, a) = " << offsetof(st2, a) << endl; // 0
cout << "offsetof(st2, a1) = " << offsetof(st2, a1) << endl; // 1
cout << "offsetof(st2, c) = " << offsetof(st2, c) << endl; // 4
cout << "offsetof(st2, st) = " << offsetof(st2, st) << endl; // 8
cout << "offsetof(st2, d) = " << offsetof(st2, d) << endl; // 32
return 0;
}
类
进行 sizeof 计算时,类与结构体类似
- 空类大小为 1
- 类的成员函数不占用结构体大小。类对象的大小由其数据成员决定
- 类和结构体一样,需要对齐
- 类若包含虚函数,编译器会在类对象中插入一个指向虚函数表的指针(多个虚函数也只有一个),以帮助实现虚函数的动态调用
- 静态成员存放在静态存储区,不占用类的大小
#include <iostream>
using namespace std;
class c0 {
char a; // 1 + pad(7)
char* a1; // 8
int c; // 4 + pad(4)
}; // 24
class c1 {
char a; // 1 + pad(7)
double b; // 8
int c; // 4
char d; // 1 + pad(3)
}; // 24
class c2 {
char a; // 1
char a1; // 1 + pad(2)
int c; // 4
c1 cla; // 24
char d; // 1 + pad(7)
}; // 40
struct c3
{
static int i; // 静态成员
int fun(); // 成员函数
int d; // 4
char ch; // 1 + pad(3)
virtual int vir1(); // 8 (64bit)
}; // 16
class A{}; // 1,空类
class B {
virtual int fun(){ // 虚函数
return 0;
}
}; // 8
class C {
static int a; // 静态成员
void fun(); // 成员函数
}; // 1
int main() {
cout << "sizeof(c0) = " << sizeof(c0) << endl; // 24
cout << "sizeof(c1) = " << sizeof(c1) << endl; // 24
cout << "sizeof(c2) = " << sizeof(c2) << endl; // 40
cout << "sizeof(c3) = " << sizeof(c3) << endl; // 16
cout << "sizeof(A) = " << sizeof(A) << endl; // 1
A a;
cout << "sizeof(a) = " << sizeof(a) << endl; // 1,空类的实例大小就是类的大小
cout << "sizeof(B) = " << sizeof(B) << endl; // 8
B b;
cout << "sizeof(b) = " << sizeof(b) << endl; // 8
cout << "sizeof(C) = " << sizeof(C) << endl; // 1
C c;
cout << "sizeof(c) = " << sizeof(c) << endl; // 1
return 0;
}
string
sizeof(string)
计算 string 变量在内存中的空间大小,与字符串长度无关
string 在内存中的布局:
vector
sizeof(vector)
计算 vector 变量在内存中的空间大小,与变量中元素的个数无关
联合体
结构体在内存组织上是顺序式的,联合体是重叠式,各成员共享一段内存,所以整个联合体的 sizeof 就是每个成员 sizeof 的最大值
#include <iostream>
using namespace std;
class c {
char a; // 1 + pad(7)
char* a1; // 8
int c; // 4 + pad(4)
}; // 24
union U {
int i; // 4
char c; // 1
class c c_c;// 24
};
int main() {
cout << "sizeof(U) = " << sizeof(U) << endl; // 24
return 0;
}