第七章 指针与引用
7.1 指针基本问题
1、指针与引用的差别?
非空区别。在任何情况下都不能使用指向空值的引用。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高;
合法性区别。在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空;
可修改区别。指针与引用的另一个重要的区别是指针可以被重新赋值以指向另一个不同对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。
示例:
int &reiv; //错误,声明了一个引用,但引用不能为空,必须同时初始化。
int *p; *p = 5; //错误,整数指针p并没有指向一个实际的地址。在这种情况下就给它赋值是错误的,因为赋得值不知道该放哪里去,从而造成错误。
const double di; //错误,const常量赋值时,必须同时初始化。
7.2 传递动态内存
1、两个数交换
a、使用指针形式交换
<span style="font-size:14px;">void swap(int *p, int *q) { int temp; temp = *p; *p = *q; *q = temp; }</span>b、使用引用传递
<span style="font-size:14px;">void swap(int &p, int &q) { int temp; temp = p; p = q; q = temp; } </span>
<span style="font-size:14px;">#include <iostream> void GetMemory(char *p, int num) { p = (char *)malloc(sizeof(char) *num); }; int main() { char *str = NULL; GetMemory(str,100); strcpy(str,"hello"); return 0; }</span>
解决方法:
a、如果要用指针参数去申请内存,我们可以采用指向指针的指针,传递str地址给函数
void GetMemory(char **p, int num);//二级指针 GetMemory(&str,100);
char *GetMemory(char *p, int num) { p = (char *)malloc(sizeof(char) *num);
return p; };
str = GetMemory (str,100);
整型传值
<span style="font-size:14px;">#include<stdio.h></span>
<span style="font-size:14px;">void GetMemory2(int *z) { *z=5; }; int main() { int v; GetMemory2(&v); cout << v << endl; return 0; }</span>
*z
是地址里的值,是v的副本;通过直接修改地址里的值,不需要有返回值,也把v给修改了,因为v所指向地址的值发生了改变。
3、char c[] 和 char *c
这个函数有什么问题?该如何修改?
char *strA() { char str[] = "hello world"; return str; }
如果想获得正确的函数,改成下面这样就可以:
char *strA() { char *str = "hello world"; return str; }
char str[] = "hello world";
是分配一个局部数组。局部数组是局部变量,它所对应的是内存中的栈。局部变量的生命周期结束后该变量不存在了。
char *str = "hello world";
是指向了常量区的字符串,位于静态存储区,它在程序生命期内恒定不变,所以字符串还在。无论什么时候调用 strA,它返回的始终是同一个“只读”的内存块。
另外想要修改,也可以这样:
char *strA() { static char str[] = "hello world"; return str; }
答案:
因为这个函数返回的是局部变量的地址,当调用这个函数后,这个局部变量str就释放了,所以返回的结果是不确定的且不安全,随时都有被收回的可能。
7.3 函数指针
1、
void(*f)() 函数指针(有括号),也叫指向函数的指针 void* f() 函数返回指针 int * const const指针 const位于*右侧,指针本身为常量 const int * 指向const的指针 const位于*左侧,指针指向为常量 float(**def)[10] 二级指针,指向一个一维数组指针,数组元素为floatdouble*(*gh)[10] gh是一个指针,它指向一个一维数组,数组元素都是double* Long(*fun)(int) 函数指针 Int (*(*F)(int,int))(int) F是一个函数的指针,指向的函数类型是有两个int参数并且返回一个函数指针的函数
int(*p)[n] 代表它是指针,指向整个数组
如要将二维数组赋给一指针,应这样赋值:
int a[3][4]; int (*p)[4]; 该语句是定义一个数组指针,指向含4个元素的一维数组。 p=a; 将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] p++; 该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
2、指针数组定义 int *p[n]; 是一个数组里面装着指针
[]优先级高,先与p结合成为一个数组,再由
int*
说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样*p=a;
这里*p
表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3]; int a[3][4]; for(i=0;i<3;i++) p[i]=a[i];
int *p[3]
表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
<strong>3、理解二维数组的真正含义且与*</strong> 比如要表示数组中i行j列一个元素: *(p[i]+j) *(*(p+i)+j) (*(p+i))[j] p[i][j]
+ <strong>4、二维数组与&</strong> int a[] = {1,2,3,4,5}; int *ptr = (int*)(&a+1); printf("%d %d", *(a+1), *(ptr-1)); //说明: *(a+1) 直接就为2 简单 &a+1 由于本身a就为数组名也就是指针,加上& 相当于双指针 也就相当于**(a+1) 所以加1 就是数组整体加一行,ptr指向a的第6个元素,所以*(ptr-1)为5
7.6 指针和句柄
1、this指针
this指针记录每个对象的内存地址,之后通过运算符->访问该对象成员
当你进入一个房子后,你可以看到桌子、椅子、地板等,但是房子你是看不到全貌的,对于一个类的实例来说,你可以看到它的成员函数,成员变量,但是实例本身你是看不到的,this指针是这样的一个指针,时时刻刻指向这个实例本身。
1)this只在成员函数中使用,全局函数、静态函数都不能使用this
2)this在成员函数开始之前创造,在成员的结束后清除
3)this不占用对象的空间
4)this可能存放在堆、栈、也可能是寄存器
5)this在成员函数中定义,所以获取一个对象之后,也不能通过对象使用this指针,所以我们没法知道一个对象this指针的位置,只有在成员函数中我们才知道,利用&this就知道了,并且可以使用它。