程序员面试宝典(第四版)——读书笔记-3、第七章:指针与引用

第七章 指针与引用


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>

2、字符串知识

<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>

运行这个程序,程序会崩溃。因为GetMemory并不能传递动态内存,Test函数中str一直都是NULL。


解决方法:

a、如果要用指针参数去申请内存,我们可以采用指向指针的指针,传递str地址给函数

void GetMemory(char **p, int num);//二级指针
GetMemory(&str,100);

b、通过返回值来传递动态内存,这种方法更简单

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>

GetMemory2函数把v的地址传进来,*z是地址里的值,是v的副本;通过直接修改地址里的值,不需要有返回值,也把v给修改了,因为v所指向地址的值发生了改变。


3、char c[]   和 char  *c


这个函数有什么问题?该如何修改?

char *strA()
{
    char str[] = "hello world";
    return str;
}

解析:这个str里存在的地址是函数strA栈里“hello world”的首地址。函数调用完成,栈帧恢复调用strA之前的状态,临时空间被重置,堆栈“回缩”,strA栈帧不再属于应该访问的范围。这段程序可以正确输出结果,但是这种访问方法违背了函数的栈帧机制。
但是只要另外一个函数调用的话,你就会发现,这种方式的不合理及危险性。


如果想获得正确的函数,改成下面这样就可以:

char *strA()
{
    char *str = "hello world";
    return str;
}

char c[ ] = " hello world ";    与 char  *c   =  " hello world ";

char str[] = "hello world";是分配一个局部数组。局部数组是局部变量,它所对应的是内存中的栈。局部变量的生命周期结束后该变量不存在了。
char *str = "hello world";是指向了常量区的字符串,位于静态存储区,它在程序生命期内恒定不变,所以字符串还在。无论什么时候调用 strA,它返回的始终是同一个“只读”的内存块。


另外想要修改,也可以这样:

char *strA()
{
    static char str[] = "hello world";
    return str;
}

通过static开辟一段静态存贮空间。


答案:
因为这个函数返回的是局部变量的地址,当调用这个函数后,这个局部变量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参数并且返回一个函数指针的函数


7.4 指针数组和数组指针

1、 数组指针 (也成为行指针) int(*p)[n] 代表它是指针,指向整个数组
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,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]
所以要分别赋值。
这样两者的区别就豁然开朗了, 数组指针只是一个指针变量 ,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。 指针数组是多个指针变量 ,以数组形式存在内存当中,占有多个指针的存储空间。


还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。


<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就知道了,并且可以使用它。




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值