指针
指针的定义
指针即地址
0x3d4f | 0x3d50 | 0x3d51 | 0x3d52 |
---|---|---|---|
56 | 73 | 48 | 95 |
上表是一个关于地址和存储空间的举例,每个地址均对应一个存储空间,或者说,计算机中每一个存储空间都有编号,用以表明位置,称为指针。
通常我们所声明的变量,都是储存在一个或多个地址拼合在一起的一块存储空间中。
指针,存放的是一个地址值,不同类型的变量有不同类型的地址值。
声明整型指针变量
野指针
int *p ;
这样一句代码 , 就声明了一个int型的指针变量p,由于没有初始化,p此时称为一个野指针,对野指针的操作是非常危险的。
空指针
int *p = NULL;
通过这一语句,可以声明一个指向NULL的指针变量p,由于它指向的是NULL,可以认为它不指向任何地址,称为空指针(实际上一般认为指向内存地址0位,由于此地址需要极高权限操作,所以空指针一般是安全的。
取地址符
int a = 3;
int *p = &a;
通过这样一部分代码,我们定义了一个有初值的int型指针变量p,它的初值是int型变量a的地址,& 符号就是取地址符,可以获得一个变量的地址。
在p指向变量a的地址后,便可以通过(*p)
取得变量p所指向的变量的值,也就是变量a的值,同时对(*p)
的任何操作也会直接影响变量a。
联系C语言中的整型基本读入语句,scanf("%d",&a);
这即是把从控制台窗口读入的值,写入了变量a的地址中,再加上上方的语句,此时变量p的值就是变量a的地址,所以这句语句也可以这样表达scanf("%d",p);
指针的作用
指针最常见的作用就是在参数中传递变量的地址,举例如下
传值与传地址
传值
void Swap(int a, int b){
int temp = a;
a = b;
b = temp;
return;
}
int main(){
int x = 5 , y = 7;
Swap(x,y);
cout<<x<<'\t'<<y<<endl;
}
函数的功能显然是想要交换形参a与b的值,但这是无法做到的,因为函数的形参变量是传值的,即当前的Swap函数相当于只是获得了来自调用处函数的两个整型值(5和7),并在Swap函数内新声明了变量a和b保存这两个整型值,而不是获得了两个变量(x和y)。
传地址
void Swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
return;
}
int main(){
int x = 5 , y = 7;
Swap(&x,&y);
cout<<x<<'\t'<<y<<endl;
}
以上的程序片段是可以完成交换功能的,函数的参数并不是两个整型变量,而是两个整型指针变量,传入的是两个变量x,y的地址,(*a)与(*b)是直接对变量x,y的地址所存储内容的修改
举个可能不太恰当的例子,现在有两个瓶子,一个装的是橙汁,一个装的是可乐,需要人来交换他们的内容,上面传值的函数是找来的人又弄来了两个瓶子,里面有一瓶完全一样的橙汁和一瓶完全一样的可乐,并且用第三个瓶子(temp)把他们交换了,而需要交换的两个瓶子并没有发生任何变化。
但下面传地址的函数找来的人直接找到了这两个瓶子的位置,又拿了一个瓶子(temp)过来,交换了他们的内容,这次,这两个瓶子里的内容成功交换了。
尝试
根据Swap函数,构造一个函数,能够求出传入的两个参数的和,并保存在第一个参数中带回主调函数中
更多的指针
其他类型变量的指针
之前介绍的都是int *型的指针,而C/C++有众多的类型,显然我们知道指针不止此一种,一般某种类型的变量,我们只使用此类型的指针保存它的地址,反例如下
float a = 3.1415926535;
int *p = a;
此程序段编译时就会报错,如果通过强制类型转换,把代码改为
float a = 3.1415926535;
int *p = (int *)&a;
编译便不会报错,但是由于这只是简单的把变量a的地址强制转换成了(int *)类型,地址所存储的二进制码并没有变,所以*p此时的值并不是3,而是1078530011,这其中有一些不可告人的处理转换,详情可以学习整型编码与浮点数编码
数组的指针
当我们在C/C++内声明一个数组,也就是内存中占用了一段连续的地址,长度为每个单元的长度*数组长度,继续举例
int arry[100];
int *p = arry;
由上面的程序可以发现,声明的数组名也就是这一段内存的首地址,*p即可访问arry[0],由于上面说明数组的地址是连续的,所以可想而知
*(p+1)即可访问arry[1],(这里的括号不可缺少,由于*的优先级高于加法运算符+,所以需要加上括号,否则相当于(*a)+1这样的一个表达式
注意 对于不同的指针类型,+1操作所跳过的地址值不同,如对于(int )型指针 int ptr; ptr+1将指向ptr所对应的地址后第四个位置,即增加了一个int的长度,指向了数组中或内存中下一个int型变量的地址,(由于此操作不管下面四个字节是否保存的是int,都会当做int取出,所以尽量减少对内存位置区域的改变
指针的数组
与普通数组几乎同理,只是存放的内容是地址
int a , b;
int * p[10];
p[0] = &a;
p[1] = &b;
指针的指针
既然指针变量本身也是一个变量,那么便可以用一个指针指向它,
int a;
int *x = &a;
int **y = &x;
int * p[10];
int ** q = p;
如上例,x是一个指向变量a的指针,y是一个指向int型指针的指针
而下面的两个语句,由于在上面数组的指针中提到,数组名本身就是一个指向内存中数组所在位置的首地址,所以数组名本身就是一个指针,那么指针数组的数组名也就是一个指针的指针,所以不需要取地址符便可以直接赋值给一个 指针的指针变量 q
另外,关于取地址符的反向操作,对一个地址取内容的操作符 *,是根据所给地址值,返回地址处的数据,由于指针的指针是层层指向的,所以对于这样的情况,取出地址中的值也要层层取出,对于上面的程序片段
int * p[10];
int ** q = p;
使用 *q可以取出q所指向变量地址处的内容,而q指向一个(int *)的指针,所以得到的还是一个指针(p[0]),再做一个运算才能得到一个int型变量,即**q
尝试
一维数组
声明一个一维数组,使用刚刚的方法,用一个指针保存数组的首地址,并用此指针存取数组中的元素
多维数组
声明一个多维数组,试着使用指针来存取多维数组的元素
多维指针数组
太复杂了,写出这种的都该判刑!
申请内存空间
调用malloc或new会返回一段在内存中申请的空间的地址,所以需要用一个指针接收
free对应 malloc申请的空间 ,delete对应 new申请的空间 , 不可以混用
free 或 delete 后 , 要记得把接收地址的指针变量值置为NULL
C语言 :malloc() ,free()
C++:new , delete
扩展应用
链表
用基本的struct构建一个C语言风格的链表,实现链表的创建,删除,插入节点,搜索,[倒置],(排序)
struct Node{
int data;
Node* next;
};
树(二叉树)
根据二叉树的基本节点,创建一颗具备最基本的二叉树,附加递归等知识,实现二叉树的构建,删除,查找,三序遍历,叶节点计数
struct Node{
int data;
Node* leftson;
Node* rightson;
};