变量名实质上是一段连续存储空间的别名,是一个标号(门牌号)
通过变量来申请并命名内存空间.
通过变量的名字可以使用存储空间.
变量名,本身是一段内存的引用,即别名(alias). 引用可以看作一个已定义变量的别名。
引用的语法:Type& name = var;
int a=10; //c编译器分配4个字节内存, a内存空间的别名
int &b=a; //b就是a的引用
3.1.3 规则
1 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
2 声明的时候必须初始化,一经声明,不可变更。
3 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
4 &符号前有数据类型时,是引用。其它皆为取地址。
const int a;
int const b;
//第⼀个第⼆个意思⼀样 代表⼀个常整形数
const int *c;
//第三个 c是⼀个指向常整形数的指针(所指向的内存数据不能被修改,但是本⾝可以修改)
int * const d;
//第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
const int * const e ;
//第五个 e⼀个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
const 常量是由编译器处理的,提供类型检查和作用域检查
#define 宏定义由预处理器处理,单纯的文本替换
引用作为函数参数
普通引用在声明时必须用其它的变量进行初始化,引用作为函数参数声 明时不进行初始化
引用作为函数的返回值(引用当左值)
I. 当函数返回值为引用时,
若返回栈变量: 不能成为其它引用的初始值(不能作为左值使用)
若返回静态变量或全局变量 可以成为其他引用的初始值(可作为右值使用,也可作为左值使用)
如果返回值为引用可以当左值
如果返回值为普通变量不可以当左值。
普通引用有自己的存储空间吗?
1)引用在C++中的内部实现是一个常指针
Type& name <===> Type* const name
2)C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占 用的空间大小与指针相同。
3)从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。
这是C++为了实用性而做出的细节隐藏。
从右向左看,&或*哪个离变量近, 哪个便起主要作用。int*&a=p_a; 把int*看成类型
int b=12;
int &a=b;
int* p_a=&b;
int* &a=p_a;
const引用
const 引用有较多使用。它可以防止对象的值被随意修改。因而具有一 些特性。
(1)const 对象的引用必须是 const 的,将普通引用绑定到 const 对象是不 合法的。这个原因比较简单。既然对象是 const 的,表示不能被修改,引用当然 也不 能修改,必须使用 const 引用。实际上,
const int a=1;
int &b=a;
这种写法是不合法 的,编译不过。
(2)const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初 始化。这个是 const 引用与普通引用最大的区别。
const int &a=2; 是合法的。
double x=3.14;
const int &b=a;
也是合法的。
//int &m = 41; //error , 普通引⽤ 引⽤⼀个字⾯量 请问字⾯量有没有内存地址
const int &m = 43; //c++编译器 会 分配内存空间
// int temp = 43
// const int &m = temp;
#include <iostream>
using namespace std;
int main(void)
{
//1> ⽤变量 初始化 常引⽤
int x1 = 30;
const int &y1=x1; //⽤x1变量去初始化 常引⽤
//2>⽤字⾯量 初始化 常量引⽤
const int a = 40; //c++编译器把a放在符号表中
//int &m = 41; //error , 普通引⽤ 引⽤⼀个字⾯量 请问字⾯量有没有内存地址
26
const int &m = 43; //c++编译器 会 分配内存空间
// int temp = 43
// const int &m = temp;
return 0;
}
const引用的原理
const 引用的目的是,禁止通过修改引用值来改变被引用的对象。
结论:
1)const int & e 相当于 const int * const e
2)普通引用 相当于 int *const e
3)当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值
分配空间,并将引用名作为这段空间的别名
4)使用字面量对const引用初始化后,将生成一个只读变量
内存四区:
1引用的基本语法:
#if 1
#include<iostream>
using namespace std;
//1 引用没有定义, 是一种关系型声明。声明它和原有某一变量(实体)的关
//系。故 而类型与原类型保持一致, 且不分配内存。与被引用的变量有相同的地
//址。
//2 声明的时候必须初始化, 一经声明, 不可变更。
//3 可对引用, 再次引用。多次引用的结果, 是某一变量具有多个别名。
//4 & 符号前有数据类型时, 是引用。其它皆为取地址。
void test01() {int a = 10;int &b = a;cout << a << endl; //10b = 20;cout << a << endl; //20cout << b << endl; //20a = 30;{int *p = &a; *p = 12;cout << *p << endl; //12cout << a << endl; //12}cout << a << endl; //12cout << b << endl; //12}
void test02() {int a = 10;int *p = &a;*p = 12;cout << *p << endl; //12cout << a << endl; //12
}void test03() {const int a = 10;int *p = (int *)&a;*p = 12;cout << *p << endl; //12cout << a << endl; //10
}void test04() {int a = 10;int &b = a;int *p = &a;*p = 30;p = &b;*p = 20;int &re = a;re = 50;cout << a << endl; //50cout << b << endl; //50cout << re << endl; //50int ff = 10;re = ff;cout << a << endl; //10cout << b << endl; //10cout << re << endl; //10}void test05() {int a=1, b=0;int &r = a;r = b; //错误,不可更改原有的引⽤关系//float &rr = b; //错误,引⽤类型不匹配 cout<<&a<<&r<<endl; //变量与引⽤具有相>同的地址。int &ra = r; //可对引⽤更次引⽤,表⽰ a 变量有两个别名,分别是 r 和 ra
}
void test06() {int a = 10;int &b = a;int c = 20;b = c;b = 50;cout << a << endl; //50cout << b << endl; //50cout << c << endl; //20 b并不是c的引用}void test07() {int a = 10;int &b = a;b = 100;cout << a << endl; //100cout << b << endl; //100int c = 20;b = c;b = 200;cout << a << endl; //200cout << b << endl; //200cout << c << endl; //20 c的值未更改 b并不是c的别名
}
int& test08() {int a;return a; //返回的是a的别名,函数调用完释放a,那么a的引用将不知道指向何处,所以test08()值会乱码
}
int& test09() {int a=0;return a; //返回的是a的别名,函数调用完释放a,那么a的引用将不知道指向何处,所以test08()值会乱码const int &a1 = 100;
}
int main() {//test01();//test02();//test03();//test04();//test05();//test06();//test07();int a = 100;test08() = a;//test08() = a //修改局部变量的值 错误cout << test08() << endl; //-858993460cout << test09() << endl; //0 //不懂int a1= 100;test09() = a1;cout << test09() << endl; //0cout << a1 << endl; //100return 0;
}
#endif
2引用当函数参数
#if 1
#include<iostream>
using namespace std;
void change_value1(int c) { //int c=a; c是局部变量需要复制 重新开辟栈空间c = 100;
}
void change_value2(int *c) {//int *c=a; 指针c存放a的地址。改变*c就是改变a的值//不需要额外复制*c = 200;
}
void change_value3(int &c) { //int &c=a; 形参c是实参a的引用,不需要额外复制c = 300;
}void test01() {int a = 0;change_value1(a);cout << "change_value1:" << a << endl; //0a = 0;change_value2(&a);cout << "change_value2:" << a << endl; //200a = 0;change_value3(a);cout << "change_value3:" << a << endl; //300
}struct student {int id;char name[64];
};
void PrintStudent1(student s) { //不建议使用 值拷贝cout << s.id << " " << s.name << endl;
}
void PrintStudent2(student *s) {cout << s->id << " " << s->name << endl;
}
void PrintStudent3(student &s) {cout << s.id << " " << s.name << endl;
}
void test02() {student s1 = { 10,"sdsd" };PrintStudent1(s1); //不建议使用PrintStudent2(&s1);PrintStudent3(s1);
}
int main() {//test01();test02();return 0;
}
#endif
3引用的本质
#if 1
#include<iostream>
using namespace std;//当研究引用时,可以将引用当做一个常指针研究
//当使用引用编程时,就把引用理解为变量的别名就OK了
struct typeA {int &a;
};
struct typeB {int *a;
};
//引用的所占用的大小 跟指针相等
void test01() {cout << "sizeof(struct typeA):" << sizeof(typeA) << endl; //4 32位系统cout << "sizeof(struct typeB):" << sizeof(typeB) << endl; //4 32位系统
}
struct student {int id;char name[64];
};
//引用声明时必须初始化 常量声明时也必须初始化 引用可能是常量
//引用可能是常指针 int * const p;
void modify1(int *const a) { //int *const a=main::&a; 可以改变a的值但不能改变a指向*a = 300;//a++; 错误
}
void modify2(int &a) { //int &a=main::a; //当我们将引用作为函数参数传递的时候,编译器会替我们将实参,取地址给引用a = 200; //对一个引用操作 赋值时,编译器替我们隐藏*操作
}
void modifyStudent(int *const a) { //int *const a 可以改变a的值但不能改变a指向*a = 300;//a++; 错误
}void test02() {int a = 10;int &re = a;int *const p = &a;
}
int main(){test01();return 0;
}
#endif
4常量指针
#if 1
#include<iostream>
using namespace std;
int main() {int const a = 0; //在常量区int *const p1 = NULL; //在常量区//p1++; ERROR*p1 = 30;int const *p2 = NULL; //在常量区p2++;//*p2 = 40; ERRORreturn 0;
}
#endif
5引用作为函数返回值
#if 1
#include<iostream>
using namespace std;int getA1() {int tempValue = 10;return tempValue;
}
void getA2(int *a) {*a = 20;
}int &getA3() {int a = 10;return a; //int &temp=a;将temp传到外部 返回的是别名
}
int &getA4() {static int a = 10;return a;
}
char *getmen(int num) {char *p = NULL;p = (char *)malloc(num);return p; //值拷贝
}int getmen2(char **pp, int num) {char *p = NULL;p = (char *)malloc(num);*pp = p;return 0;
}void test01() {int a = 0;a = getA1(); //将返回值10拷贝给了a。要是有多个结构体会很浪费空间char *pp = NULL;pp = getmen(10);int main_a = 0;main_a = getA3(); //main_a=temp; 值拷贝cout << main_a << endl; //10cout << main_a << endl; //10//引用作为返回值,不要返回局部变量的引用。否则可能会出错 getA3函数被回收(没有权限访问,之前的变量覆盖)//引用如果当函数返回值的话,函数可以当左值int &main_a_re = getA3();cout << main_a_re << endl; //10cout << main_a_re << endl; //-858993460int &main_a_re1 = getA4(); cout << main_a_re1 << endl; //10cout << main_a_re1 << endl; //10cout << main_a_re1 << endl; //10getA4() = 1000;
}int main() {test01();return 0;
}
#endif
6引用指针
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;struct teacher {int id;char name[64];
};
int get_mem(struct teacher** tpp) {teacher *tp = NULL;tp = (teacher*)malloc(sizeof(teacher));if (tp == NULL) {return 0;}tp->id = 100;strcpy(tp->name, "li4");*tpp = tp;return 0;
}
void Free_teacher(struct teacher **tpp) {if (tpp == NULL) {return;}struct teacher *tp = *tpp;if (tp != NULL) {free(tp);*tpp = NULL;}
}
void test01() {struct teacher *tp = NULL;get_mem(&tp);cout << tp->id << " " << tp->name << endl;Free_teacher(&tp);}
int get_mem2(struct teacher * &tp) { //struct teacher *为整体相当于inttp = (struct teacher *)malloc(sizeof(struct teacher));if (tp == NULL) {return -1;}tp->id = 1000;strcpy(tp->name, "wangwu");return 0;}
void Free_teacher2(struct teacher * &tp) {if (tp != NULL) {free(tp);tp = NULL;}}
void test02() {struct teacher *tp = NULL;get_mem2(tp);cout << tp->id << " " << tp->name << endl;Free_teacher2(tp);
}
int main() {test01();test02();return 0;
}
#endif
7const引用
#if 1
#include<iostream>
using namespace std;
void test01() {//如果对一个常量进行引用,则必须是一个const引用//相反 如果一个普通变量,用一个const引用是可以的const int a = 10; //安全性较高//int &b = a; //错误 将 "int &" 类型的引用绑定到 "const int" 类型的初始值设定项时,//限定符被丢弃 03C++对C的拓展const int &re = a;int b = 20; //安全性较低const int &re2 = b;// ret2 = 200; 错误b = 30;cout << b << endl; //30cout << re2 << endl; //30 const引用的目的是,禁止通过修改引用值来改变被引用的对象。//但可以通过改变被引用的对象改变引用值}void test02() {//int &ref = 10; //错误 引用了不合法的内存 不可以const int &ref = 10; //加入const后,编译器处理方式为:int tmp=10;const int &ref=tmp;对临时变量的引用//ref =10; //错误int *p = (int*)&ref;*p = 1000;cout << ref << endl; //1000
}//引用使用场景:通常用来修饰形参
void showValue(int &val) {val += 1000; //如果只是想显示内容 而不进行修改就使用const进行修饰cout << "value:" << val << endl;
}void showValue(const int &val) {//val += 1000; //编译错误cout << "value:" << val << endl;
}void test03() {int a = 10;showValue(a); //1010
}
int main() {test01();cout << "--------" << endl;test02();cout << "------------" << endl;test03();return 0;
}
#endif