sizeof运算符返回一条表达式(表达式结果类型的大小)或一个类型名字所占的字节数。

32位系统指针是4字节,64位系统指针是8字节


结构体

没有成员的结构体占用的空间是多少个字节?

 答案是:1个字节。

 这就是实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类或空结构体(C++中结构体也可看为类)隐含的加一个字节,这样空类或空结构体在实例化后在内存得到了独一无二的地址,所以空类所占的内存大小是1个字节。

sizeof求结构体大小的问题

https://www.bilibili.com/video/BV1be411x7ah?share_source=copy_web&vd_source=17cdeb76122f8dc86347b37ac29fd89d 看懂了

image-20220726142031151

image-20220726142349090

struct stu1  
{
int i; // 对齐单位是:4,[0]~[3]
char c; // 对齐单位是:1,[4]~[5]
int j; // 对齐单位是:4,[8]~[11]
}; //总长度应为对齐单位(4)的倍数:12

4 + 4 + 4 = 12

struct stu1 {
int *i; // 对齐单位是:8,[0]~[7]
int j; // 对齐单位是:4,[8]~[11]
short c; // 对齐单位是:2,[12]~[13]
};//总长度应为对齐单位(4)的倍数:16

12->16


struct   s
{
int x: 3;
int y: 4;
int z: 5; // 对齐单位是:4,[0]~[3]
double a; // 对齐单位是:8,[8]~[15]
} //总长度应为对齐单位(8)的倍数:16

C语言的语法:位域。需要看清楚该结构体中是位操作,三个变量共占用一个int类型的大小,int类型占用4个字节,double占用8个字节。为了实现内存对齐,int类型需要填充4个字节的长度。前面补4位,4+4+8=16

答案16字节


结构体嵌套

typedef struct student4{
int id; //对齐单位是:4,[0]~[3]
double weight; //对齐单位是:8,[8]~[15]
float height; //[16]~[19]
}Stu4; //总长度应为对齐单位(8)的倍数:24

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

//结构体内嵌结构体
struct student5{
int id; //对齐单位是:4,[0]~[3]
double score; //对齐单位是:8,[8]~[15]
short grade; //[16]~[17]
Stu4 aa; //对齐单位是:8,[24]~[47]
char name[2]; //[48]~[49]
}Stu5; //总长度应为对齐单位(8)的倍数:56

image-20220726145422755

2->4 + 1->4 + 4 + 4 = 16

struct stu5  
{
char i; //对齐单位是:1,[0]~[1]
struct
{
char c; //对齐单位是:1,[0]~[1]
int j; //对齐单位是:4,[4]~[8]
} ss; //对齐单位是:4,长度8 [4]~[11]
char a; //对齐单位是:1,[12]~[13]
char b;
char d;
char e;
char f; //对齐单位是:1,[16]~[17]
}

1->4 + 1->4 + 4 + 5 = 17-> 20


联合体/共用体

https://codeantenna.com/a/V2wGOueu0U

长度为联合中元类型(如数组,取其类型的数据长度)最大的变量长度(对齐单位)的整数倍,且要大于等于其最大成员所占的存储空间。大于等于各种成员总长度,且为对齐单位的整数倍。

//联合体
union foo{
int a; //对齐单位:4,总长度:4
double d; //对齐单位:8,总长度:8
}Foo1; //对齐单位:8,最大存储长度8,因此联合体的长度应为对齐单位的倍数:8
//联合体
union foo{
char s[10]; //对齐单位:1,总长度:10
int a; //对齐单位:4,总长度:4
}Foo1; //对齐单位:8,最大存储长度10,因此联合体的长度应为对齐单位4的倍数:12
//联合体
union foo{
char s[10]; //对齐单位:1,总长度:10
int a; //对齐单位:4,总长度:4
double d; //对齐单位:8,总长度:8
}Foo1; //对齐单位:8,最大存储长度10,因此联合体的长度应为对齐单位8的倍数:16

2、联合体内嵌结构体

typedef struct student4{
int id; //对齐单位是:4,[0]~[3]
double weight; //对齐单位是:8,[8]~[15]
float height; //[16]~[19]
}Stu4; //总长度应为对齐单位(8)的倍数:24
//联合体包含结构体
typedef union foo2{
Stu4 bb; //对齐单位:8,总长度:24
char s[10]; //对齐单位:1,总长度:10
int a; //对齐单位:4,总长度:4
double d; //对齐单位:8,总长度:8
}Foo2; //对齐单位:8,长度应为对齐单位的倍数:24

​ 3、结构体内嵌联合体

//结构体包含联合体
struct student6{
char num[10]; //对齐单位:1,[0]~[9]
int no[10]; //对齐单位:4,[12]~[51]
Foo2 cc; //对齐单位:8,[56]~[79]
char a; //对齐单位:1,[80]
double a1; //对齐单位:8,[88]~[95]
int a2; //对齐单位:4,[96]~[99]
}Stu6; //对齐单位:8,长度应为对齐单位的倍数:104

枚举enum

​ 1、enum只是定义了一个常量集合,里面没有“元素”,而枚举类型是当做int来存储的,所以枚举类型的sizeof值都为4。

enum Day

{

saturday,

sunday = 0,

monday,

tuesday,

wednesday,

thursday,

friday

} workday; //变量workday的类型为枚举型enum DAY


char数组

sizeof不是函数,仅仅是一个操作符

  char char1[] = {'a', 'b'};
char char2[9] = {'c', 'd', 'e', 'f'};
char *char3 = (char *)"hello world";
string char4 = "this is a string.this is a string.this is a string.";

cout << "char1[] = {'a','b'} -> sizeof:" << sizeof(char1) << endl;
cout << "char2[9] = {'c','d','e','f'} -> sizeof:" << sizeof(char2) << endl;
cout << "char3 \"hello world\" -> sizeof:" << sizeof(char3) << endl;
cout << "char4 string \"this is a string.\"-> sizeof:" << sizeof(char4)
<< endl;
cout << endl;
cout << "char1[10] = {'a','b'} -> strlen:" << strlen(char1) << endl;
cout << "char2[9] = {'c','d','e','f'} -> strlen:" << strlen(char2) << endl;
cout << "char3 \"hello world\" -> strlen:" << strlen(char3) << endl;
// cout<< "char4 -> strlen:"<<strlen(char4)<<endl; //编译不通过
cout << endl;
cout << "char4 string \"this is a string.\"-> length:" << char4.length()
<< endl;

cout << "char4 string \"this is a string.\"-> size:" << char4.size() << endl;



char1[] = {'a','b'} -> sizeof:2
char2[9] = {'c','d','e','f'} -> sizeof:9
char3 "hello world" -> sizeof:8 // 指针!
char4 string "this is a string."-> sizeof:32 // string 多长都是32

char1[10] = {'a','b'} -> strlen:2
char2[9] = {'c','d','e','f'} -> strlen:4
char3 "hello world" -> strlen:11

char4 string "this is a string."-> length:51
char4 string "this is a string."-> size:51

对于字符数组:sizeof得到的是字符数组的容量,strlen是的到的字符的长度
对于字符指针:sizeof得到的是指针的字节数8,strlen得到的是指针指向的字符的个数

对于string:sizeof得到的变量的字符大小32,而length和size得到的是变量中字符串的长度。


char buffer[] = "Hello";
int m = strlen(buffer); /*m = 5*/
int n = sizeof(buffer); /*n = 6*/

以遇到的第一个’\0’为结束标志;n = 6,这是因为字符串以’\0’为结束标志,’\0’也需要占用一个字节,所以sizeof测得的结果为6。

所以char s[8] = “newcoder”是错的 需要s[9]

char str[100] = "abcdefg";
cout << sizeof(str) << endl; // 100 (str的容量为100)
cout << sizeof(*str) << endl; // 1 (*str是一个字符数据)
cout << strlen(str) << endl; // 7 (字符串str的长度)

char str1[] = "abcdefg"; //
cout << sizeof(str1) << endl; // 8
cout << strlen(str1) << endl; // 7

char str2[] = "abcde0fg"; // (里面是字符'0',不等于'\0')
cout << sizeof(str2) << endl; // 9
cout << strlen(str2) << endl; // 8
char str3[] = "abcde\0fg";
cout << sizeof(str3) << endl; // 9
cout << strlen(str3) << endl; // 5

char str4[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
cout << sizeof(str4) << endl; // 7
cout << strlen(str4) << endl; // 7 (数值不确定)

char str6[] = {'a', 'b', 'c', 'd', 'e', '0', 'f', 'g'};
cout << sizeof(str6) << endl; // 8
cout << strlen(str6) << endl; // 21 (数值不确定)

char str7[] = {'a', 'b', 'c', 'd', 'e', '\0', 'f', 'g'};
cout << sizeof(str7) << endl; // 8
cout << strlen(str7) << endl; // 5

char str8[] = {'a', 'b', 'c', 'd', 'e', 0, 'f', 'g'};
cout << sizeof(str8) << endl; // 8
cout << strlen(str8) << endl; // 5

char str9[] = "";
cout << sizeof(str9) << endl; // 1
cout << strlen(str9) << endl; // 0
char a[10][9];
cout << sizeof(a) << endl; // 90

//dji 08.07
#include <bits/stdc++.h>
using namespace std;
template <class T>
int a(T) noexcept { return sizeof(T); }

template <class T>
int b(T &&) noexcept { return sizeof(T); }

int f(char a[])
{
return sizeof(a);
}

int main(int argc, char *argv[])
{
int i = 123;
int aa[100] = {i};
int *p = aa;
cout << a(i) << endl // 4
<< a(aa) << endl // 8
<< a(p) << endl; // 8
cout << b(i) << endl // 4
<< b(aa) << endl // 400
<< b(p) << endl; // 8
cout << sizeof(i) << endl // 4
<< sizeof(aa) << endl // 400
<< sizeof(p) << endl; // 8
char aaa[15];
cout << sizeof(aaa) << endl // 15
<< f(aaa) << endl; // 8
system("pause");
return 0;
}


类对象

计算需要考虑成员变量大小,内存对齐,是否有虚函数,是否有虚继承等

  1. 空类
#include <iostream>
using namespace std;

class A {

};
class B : public A{

};
class C : public B{

};

int main() {
A a;
B b;
C c;
cout<<"size of a:"<<sizeof(a)<<endl; // 1
cout<<"size of b:"<<sizeof(b)<<endl; // 1
cout<<"size of c:"<<sizeof(c)<<endl; // 1
return 0;
}
  1. 仅有常规函数、无成员变量类

    扩展1:如果在该类型中添加一个构造函数和析构函数,再求sizeof,得到的结果是多少?
    答案:还是1。调用构造函数和析构函数只需要知道函数的地址即可,而这些地址只与类型相关,而与类型的实例无关, 编译器也不会因为这两个函数而在实例内添加任何额外的信息。
    注:不管添加的是构造函数还是析构函数还是其它任何类型的函数,都是这个结果。

#include <iostream>
using namespace std;

class A {
public:
A(int x=0) {
cout<<"A"<<x<<endl;
}
void printA() {
cout<<"Hello A";
}
};
class B :public A{
public:
B(int x=0) {
cout<<"B"<<x<<endl;
}
void printB() {
cout<<"Hello B";
}
};

class C : public B{
public:
C() {
cout<<"C"<<endl;
}
void printC() {
cout<<"Hello C";
}
};

int main() {
A a;
B b;
C c;
cout<<"size of a:"<<sizeof(a)<<endl; // 1
cout<<"size of b:"<<sizeof(b)<<endl; // 1
cout<<"size of c:"<<sizeof(c)<<endl; // 1
return 0;
}

仅包含一般成员函数(即没有虚函数),不含成员变量时,运行结果和(一)是一样的,系统也只是为对象创建了1个字节的占位符。因此,我们可以得出结论是,一般成员函数不会对类的大小造成影响。

  1. 含有一般成员变量类

    普通的继承就是基类的大小+派生类自身的大小。

#include <iostream>
using namespace std;

class A {
public:
A(int x=0) {
cout<<"A"<<x<<endl;
}
void printA() {
cout<<"Hello A";
}
private:
char Data1[3];
int Data2;
};
class B :public A{
public:
B(int x=0) {
cout<<"B"<<x<<endl;
}
void printB() {
cout<<"Hello B";
}
private:
char Data1[3];
int Data2;
};

class C : public B{
public:
C(int x=0) {
cout<<"C"<<x<<endl;
}
void printC() {
cout<<"Hello C";
}
private:
char Data1[3];
int Data2;
};

int main() {
A a;
B b;
C c;
cout<<"size of a:"<<sizeof(a)<<endl; // 8
cout<<"size of b:"<<sizeof(b)<<endl; // 16
cout<<"size of c:"<<sizeof(c)<<endl; // 24
return 0;
}

依次继承的三个类中含有相同数量,相同类型的一般成员变量(不含静态成员变量)。此种情况下,类对象大小=基类对象大小+自身成员大小。A当中三个字符变量3个字节,一个整形变量4个字节,考虑内存对齐因素(默认为4),A类对象大小为8。B类对象大小为A类对象大小基础上再加8,C类对象大小在B类对象大小基础上再加8。

  1. 含静态成员变量的类
#include <iostream>
using namespace std;

class A {
public:
A(int x=0) {
cout<<"A"<<x<<endl;
}
void printA() {
cout<<"Hello A";
}
private:
char Data1[3];
int Data2;
static int Data3;
};
class B: public A {
public:
B(int x=0) {
cout<<"B"<<x<<endl;
}
void printB() {
cout<<"Hello B";
}
private:
char Data1[3];
int Data2;
static int Data3;
};

class C : public B{
public:
C(int x=0) {
cout<<"C"<<x<<endl;
}
void printC() {
cout<<"Hello C";
}
private:
char Data1[3];
int Data2;
static int Data3;
};

int main() {
A a;
B b;
C c;
cout<<"size of a:"<<sizeof(a)<<endl; // 8
cout<<"size of b:"<<sizeof(b)<<endl; // 16
cout<<"size of c:"<<sizeof(c)<<endl; // 24
return 0;
}

可以看到,类对象大小没有因为增加了静态成员而变化。因为静态成员是属于类成员共有的,不单独属于任何一个对象,对静态成员的存储不会选择在某个对象空间,而是存在于堆当中,因此不会对对象的大小造成影响。

  1. 虚函数
using namespace std;
class A {
public:
virtual void fun(){};
};
class B {
public:
virtual void fun(){};
virtual void fun1(){};
virtual void fun2(){};
};
int main() {
A a;
B b;
cout << sizeof(a) << endl; // 8
cout << sizeof(A) << endl; // 8
cout << sizeof(B) << endl; // 8
system("pause");
return 0;
}
class A {
public:
char b;
double a;
short c;
};
class B {
public:
char a;
virtual void fun() {}
short b;
};
int main() {
cout << sizeof(A) << endl; // 24
cout << sizeof(B) << endl; // 8 + 1->2 + 2 = 12->16
system("pause");
return 0;
}

为了效率问题,编译器(gcc 和 微软)一般会把虚指针放在类的内存空间的最前面的位置,不管虚函数声明的位置。考虑对齐,大小都是 4 +1+1+2 = 8.

class A {
virtual void fun() {}
};
class B : public A {
public:
virtual void fun2() {}
};
class C : virtual public A { // 虚基类
public:
virtual void fun3() {}
};
int main() {
cout << sizeof(A) << endl; // 8
cout << sizeof(B) << endl; // 8
cout << sizeof(C) << endl; // 8
system("pause");
return 0;
}

派生类继承了基类的虚指针,所以大小为4。

其他

vector<int> ivec;
cout << sizeof(ivec) << endl;
3个指针 = 24