C++的常量、指针、引用
一、C语言的常量是可以通过地址进行修改的;而C++的常量是不可修改的,且在定义的时候必须初始化!
可引用常量的地址,但只是临时地址:
const int a = 10 ;
int *b = (int*)&a ; //开辟临时地址给a,实际并不能访问a的地址,它在符号表中,不可修改
宏是在预编译阶段(预处理)处理的,而常量是在编译阶段处理的。而且宏没有作用域的说法,从它定义的位置开始到代码结束都可以使用该宏常量。
const修饰的指针,通常如下三种:
常量指针(有点难):
int const a = 10,int * const p = NULL,都在常量区符号表中?
数组int array[10],10个int数据在栈中,而array常量指针在常量区符号表中,不可修改,但内容是可以修改的。
引用int &r = a,r是常指针在常量区,指向a变量,r不可改变,而使用时就是*r,就取的是a的值了。
二、C++中引用相当于给变量取别名,且定义引用变量时必须初始化:
int a = 10;
int *p = &a; //此处&表示取a地址赋给指针p
int &re = a; //此处&表示引用类型的变量,re就是a的别名,可以对re赋值达到修改a的目的
int &re2 = re; //可以对引用再次引用,其实re2本质也是a别名
三、通过指针和引用两种方式改变变量的值(如下图)
有变量a,如果函数的参数为指针int *p,那么传参时应该传的是地址&a;
如果函数的参数为引用int &r,那么传参时应该直接传a;
为什么把函数参数类型改为int b,就是实现不了对a的值的修改呢?因为此时传进去的a为形参,作用域只在函数内部,与实际a的地址并不一样,所以不能实现对a值的修改。
四、指针、引用作为函数参数
如果需要传进的参数是一个结构体之类的变量,数据会很多,此时直接传参会存在值拷贝的动作,如果使用指针或引用就可以避免,如下图:
用指针时,取结构体属性(成员)使用'->'符号;
用引用时,取结构体属性(成员)使用'.'符号。
五、引用的本质
注:在研究引用时,可按如下理解,但是编程时只需要把引用当做变量的别名即可!
常量要初始化,引用也要初始化,从这个角度可推测引用可能是一个常量,也就是说是常指针,有引用int &a,其本质形式为int *const a;
常指针int *const a,即可以通过*a = 10的方式赋值,但指针的指向不能修改,必须指向某一固定地址。
在引用作为函数参数时,如int &a,直接传参数a,实际编译器隐藏了取地址操作给引用int &a = &a,‘&’被隐藏;
函数内部对一个引用操作赋值的时候,编译器隐藏了‘*’操作,实际a = 200,即为*a = 200。
六、引用作为函数的返回值
直接上个C++代码的例子吧:
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 #include<stdlib.h> 4 5 using namespace std; 6 /* 7 malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域 8 以void*类型返回分配的内存区域地址,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。 9 void* 类型表示未确定类型的指针。C,C++规定,void* 类型可以通过类型转换强制转换为任何其它类型的指针。 10 */ 11 12 char * getmem(int num) { 13 char *p = NULL; 14 p = (char*)malloc(num);//malloc()函数,动态分配内存 15 return p;//返回地址 16 } 17 18 //两个指针的目的是把一个指针传出来 19 int getmem2(char **pp, int num) { 20 char *p = NULL; 21 p = (char*)malloc(num); 22 *pp = p;//指向指针的指针,二级指针 23 return 0; 24 } 25 26 int getA1() { 27 int a = 10; 28 return a; 29 } 30 31 int getA2(int *a) { 32 return 0; 33 } 34 35 //引用作为返回值时,不要返回局部变量的引用 36 int& getA3() { 37 int a = 10; 38 return a;//隐式操作int &temp = a; temp 为a的别名 39 } 40 41 int & getA4() { 42 static int a = 10;//静态变量,全局领域开辟的空间,只有程序结束才会释放 43 return a; 44 } 45 46 int main(void) { 47 int a = 0; 48 char *pp = NULL; 49 50 a = getA1();//此时是一个值拷贝过程 51 pp = getmem(10);//也是值拷贝,拷贝的是地址 52 cout << "-----------------" << endl; 53 int main_a = 0; 54 55 main_a = getA3();//main_a = temp//数值拷贝 56 cout << "main_a=" << main_a << endl; 57 cout << "-----------------" << endl; 58 #if 0 59 int &main_a_re = getA3();//再次对temp去别名,本质上还是局部变量a的别名 60 cout << "main_a_re=" << main_a_re << endl;//第一次调用还未压栈 61 cout << "main_a_re=" << main_a_re << endl;//再调用时main_a_re,因为a是局部变量,压栈后内存被释放,这个别名就出现异常 62 #endif 63 int &main_a_re = getA4(); 64 cout << "main_a_re=" << main_a_re << endl; 65 cout << "main_a_re=" << main_a_re << endl; 66 //引用作为函数返回值的话,函数可以当左值 67 getA4() = 1000; 68 69 system("pause"); 70 return 0; 71 }
七、指针引用
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 #include<stdlib.h> 4 5 using namespace std; 6 struct teacher { 7 int id; 8 char name[64]; 9 }; 10 11 //二级指针**tpp这种形式就是最外层指针的内容是一个指针 12 int get_mem(struct teacher** tpp) { 13 struct teacher *tp = NULL; 14 tp = (struct teacher*)malloc(sizeof(struct teacher)); 15 if (tp == NULL) { 16 return -1; 17 } 18 tp->id = 100; 19 strcpy(tp->name, "li4"); 20 *tpp = tp;//指针指向指针,就是新的指针tpp是用来保存第一层指针tp的 21 return 0; 22 } 23 24 void free_mem(struct teacher **tpp) { 25 if (tpp == NULL) { 26 return; 27 } 28 29 struct teacher *tp = *tpp;//一级指针*tpp赋给临时变量tp 30 if (tp != NULL) { 31 free(tp);//释放最里层的一级指针,内容为结构体 32 *tpp = NULL; 33 } 34 } 35 36 int get_mem2(struct teacher *&re) { 37 re = (struct teacher*)malloc(sizeof(struct teacher)); 38 if (re == NULL) { 39 return -1; 40 } 41 re->id = 200; 42 strcpy(re->name, "wang5"); 43 return 0; 44 } 45 46 void free_mem2(struct teacher *&re) { 47 if (re != NULL) { 48 free(re); 49 re = NULL; 50 } 51 } 52 53 int main(void) { 54 struct teacher *tp = NULL; 55 56 get_mem(&tp);//不论是几级的指针,传进的参数都是地址,所以用&tp 57 cout << "id=" << tp->id << ",name=" << tp->name << endl; 58 free_mem(&tp);//释放最外层的指针 59 cout << "------------------" << endl; 60 get_mem2(tp);//用引用时,tp就是定义中的&tp 61 cout << "id=" << tp->id << ",name=" << tp->name << endl; 62 free_mem2(tp); 63 system("pause"); 64 return 0; 65 }
八、const引用
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<iostream> 3 #include<stdlib.h> 4 5 using namespace std; 6 7 int main(void) { 8 const int a = 10;//如果对常量进行引用,那么必须是一个const引用 9 const int &re = a; 10 11 int b = 20; 12 const int &re2 = b;//相反如果一个普通变量,用一个const引用接收是可以的 13 14 system("pause"); 15 return 0; 16 }