1.函数可以声明多次,但只能定义一次
2.在32位操作系统下(x86),不管什么类型的指针,都是占用四个字节的内存空间,在64位操作系统下(x64),则占用8个字节
3.空指针:指向内存编号为零的空间,空指针指向的内存是不可访问的
4.const修饰指针
(1)const int* p=&a; 常量指针
特点:指针的指向可以改变,指针指向的值不可以改变
(2)int* const p=&a; 指针常量
特点:指针的指向不可以改变,但指针指向的值可以改变
(3)const int* const p=&a;
指针的指向和指针指向的值都不可以改变
5.sizeof数组与指针
int arr[5]={1,2,3,4,5}
int* p=arr;
cout<<sizeof(arr)<<endl;
cout<<sizeof(p)<<endl;
//结果为:
//20
//4(x64下为8)
//无论什么类型的指针都占4(x64为8)个字节
6.结构体
struct 结构体名{成员列表};
通过结构体创建变量的方式有四种:
struct 结构体名 变量名;
struct 结构体名 变量名={成员1,成员2…}
结构体名 变量名;
定义结构体时顺便创建变量
struct student{
int age;
int height;
}s4;
int main(){
struct student s1;//第一种创建变量方式
student s2;//第二种
student s3={19,130};//第三种
}
7.C++内存分区模型
- 代码区:存放函数体的二进制代码,代码区是共享的,只读的
- 全局区:存放全局变量、静态变量、常量,该区域的数据在程序结束后由操作系统释放
常量包括字符串常量和const修饰的全局变量,const修饰的局部变量
字符串常量和const修饰的全局变量(全局常量)存放在全局区
const修饰的局部变量(局部常量)存放在栈区 - 栈区:存放函数参数值及局部变量,由编译器自动分配和释放内存
注意:不要返回局部变量的地址和引用,因为该变量的内存空间已被回收 - 堆区:动态申请的存储空间,由程序员控制它的分配和释放,若未释放,则程序运行结束后由系统进行回收
//malloc和free
char* str=(char*)malloc(sizeof(char)*100);
free(str);
new 数据类型 返回的该数据类型的指针
通常在虚构函数中用delete释放
释放数组要加[]
8.引用:给变量起别名
(1)用法:数据类型 &别名=原名
int a=10;
int &p=a;
p=20;
(2)引用必须初始化,引用初始化后就不可以改变
int &b;//错误的,因为没有初始化
(3)不要返回局部变量的引用,因为引用指向的内存空间已经不归该变量所有
int& func(){
int temp=10;
return temp;
}
int main(){
int &ret=func();
cout<<ret<<endl;//第一次正确,因为编译器会做一个保存,但此时ret所指向的内存空间其实已经被回收
cout<<ret<<endl;//第二次错误
}
(4)如果函数的返回值是引用,那么函数的调用可以作为左值
int& func(int &a){
return a;
}
int main(){
int a=10;
func(a)=20;
cout<<a<<endl;//a的值变为20
}
(5)引用的本质就是一个C++指针常量
int a=10;
int &p=a;//底层被转换为int* constp=&a;
p=20;//底层被转换为*p=20;
(6)常量引用:通常用来修饰函数形参,使被引用实体的值不会在函数内改变
void func(const int&p){}
(7)对原引用变量,权限只能缩小,不能放大
const int a=10;
int &p=a;//错误,因为原变量是常量,只能读,不能修改,而这种写法对原变量是可修改的,权限不能放大
int b=10;
const int&p1=b;//正确,权限可以缩小
int &ret1=10;//错误
const int &ret2=20;//正确
(8)引用的使用场景
//(1)做形参
//避免值传递导致的调用拷贝构造函数,效率低下,且无法修改实参
//以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而
//是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤
//其是当参数或者返回值类型非常大时,效率就更低
void swap(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
int main(){
int a=10,b=20;
swap(a,b);
//a变为20,b变为10
cout<<a<<endl;
cout<<b<<endl;
}
//(2)做返回值
//注意做返回值时不要返回局部变量的引用
9.默认参数
默认参数必须在最右边
void func(int a,int b=20,int c=30){}
如果函数声明有了默认参数,那么函数实现就不能有默认参数,反之亦然,目的是防止出现二义性
void func(int a=10,int b=10);
void func(int a=20,int b=20);
//错误,出现了二义性,a,b的默认值究竟是多少?
10.占位参数
返回值类型 函数名(数据类型){}
占位参数也可以有默认值
比如可以用于区分前置递增和后置递增
void func1(int){}
void func2(int=20){}
class MyInteger{
public:
int num;
MyInteger& operator++(){//前置递增
this->num=this->num+1;
return *this;
}
MyInteger operator++(int){//因为后式递增不允许链式编程(a++)++这种,故不返回引用
MyInteger temp=*this;
this->num=this->num+1;
return temp;
}
}
11.函数重载注意事项
(1)引用作为重载条件
void func(int& a){}
void func(const int&a){}
//上面这两个是可以重载的
int a=10;
const int b=10;
func(a);//调用的是第一个函数
func(b);//调用的是第二个函数
func(10);//调用的是第二个函数
(2)函数重载碰到默认参数
void func(int a,int b=10){}
void func(int a){}
func(10);//将会出错,二义性
func(10,20);//不会出错