为什么存在结构体内存对齐
1.平台原因
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出异常。
2.性能原因
数据结构尤其是(尤其是栈)应该尽可能的在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存仅需要一次内存访问。
1、结构体内存对齐规则
1.第一个成员在与结构体偏移量为0的地址处。
2.其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数=编译器默认的一个对齐数与该成员大小的较小值。
VS中默认的对齐数为8.
3.结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
#12
struct s1
{
char c1;
int i;
char c2;
};
#8
struct s2
{
char c1;
char c2;
int i;
};
#16
struct s3
{
double d;
char c;
int i;
};
#32
struct s4
{
char c1;
struct s3 S3;
double d;
};
总结:结构体的内存对齐是拿空间来换取时间的做法。设计结构体的时候,既要满足对齐,又要节省空间,只需让占用内存小的成员尽量集中在一起。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
2、大小端
什么是大小端?
大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。
为什么会有大端和小端?
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8个bit。而在C语言中除了8bit的char之外,还有16bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器大于一个字节,那么必然存在如何将多个字节安排的问题。因此就导致了大端和小端存储模式。
百度2015年系统工程师笔试题:
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。`
解题思路:指针类型转换,最后取char*指针的值,判断int低地址的数据是否为1。
#include<iostream>
using namespace std;
int check_sys()
{
int i = 1;
return (*(char *)&i);
}
int main()
{
int ret= check_sys();
if (ret == 1)
{
cout << "小端" << endl;
}
else
{
cout << "大端" << endl;
}
return 0;
}
3、this指针
先看一段代码
#include<iostream>
using namespace std;
class student
{
public:
void display()
{
cout << age << endl;
}
void setAge(int age)
{
age = age;
cout << age << endl;
}
private:
int age;
};
int main()
{
student s;
s.setAge(17);
s.display();
return 0;
}
代码输出:
17
-858993460
我们可以看到s对象display()的输出是随机值。原因是student类没有对成员变量age和setAge()方法中的age作区分,而display()方法传递的是成员变量中未初始化的age,所以随机值。
为了解决该问题,只需使用this指针对age进行区分即可。
#include<iostream>
using namespace std;
class student
{
public:
void display()
{
cout << age << endl;
}
void setAge(int age)
{
this->age = age;
cout << age << endl;
}
private:
int age;
};
int main()
{
student s;
s.setAge(17);
s.display();
return 0;
}
代码输出:
17
17
接下来详细了解this指针
什么是this指针?
C++编译器给每个“非静态的成员函数”增加了一个隐藏的指针参数,让该指针指向当前的对象(函数运行时调用该函数的对象),在函数体中所有的成员变量的操作,都是通过该指针取访问。只不过所有操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的特性
1.this指针的类型:类类型* const
2.只能在成员函数内部使用
3.this指针本质其实是一个成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针
4.this指针是成员函数第一个隐含的指针形参,一般情况下由编译器通过ecx寄存器自动传递,不需要用户传递
4、构造函数
什么是构造函数?
构造函数是类中特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的声明周期内只调用一次。
构造函数的特征
1.函数与类名相同
2.无返回值
3.对象实例化时编译器调用对应的构造函数
4.构造函数可以重载
5.如果没有显示定义构造函数,则C++编译器会自动生成一个无参的构造函数,一旦用户显示定义编译器将不再生成
6.无参构造函数(可以为编译器默认生成)和全缺省构造函数都称为默认构造函数,并且默认构造函数只能有一个
7.C++把数据类型分为内置数据类型和自定义数据类型。内置类型就是语法已经定义好的类型:如int,char等。自定义类型就是我们使用struct,class等自己定义的类型。我们不写,编译器默认生成的构造函数,对于内置类型不做初始化处理,对于自定义类型编译器会调用它的默认构造函数。因此编译器默认生成的构造函数是有用的。
初始化列表
以一个冒号开始,接着是以逗号分隔的数据成员列表,每个“成员变量”后边跟着一个放到花括号中的初始值或者表达式。
class Date
{
public:
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
};
注意
每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
类中包含以下成员,必须放在初始化列表位置进行初始化
引用成员变量
const成员变量
自定义类型成员(该类没有默认构造函数)
尽量使用初始化列表初始化,因为不管是否使用初始化列表,对于自定义类型成员变量,一定会先试用初始化列表初始化
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
5、析构函数
什么是析构函数?
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而在对象销毁时会自动调用析构函数,完成类的一些资源清理工作。
析构函数的特性
1.析构函数是在类名前边加入~
2.无参数无返回值
3.一个类有且只有一个析构函数。若未显示定义,系统会自动生成默认的析构函数
4.对象生命周期结束时,C++编译器会自动调用析构函数
5.对于编译器自动生成的析构函数,对内置数据类型不做处理,会对自定义数据类型成员调用它的析构函数