2.7 指针

2.7 指针

2.7.1 什么是指针

首先在理解什么是指针之前,我们先要明白变量是如何在内存中存放的。
假设内存中按字节编址。。。

一个房间号指向一个房间,对应到计算机上就是一个地址指向一个变量,可以通过地址来找到变量。在c语言中用指针来表示内存地址(或者说指针指向了内存地址),而如果这个内存地址恰好是某个变量的地址,那么又可说这个指针指向该变量。

那么如何获取这个变量的地址呢?我们可以通过取地址运算符&。在变量前面加上&,就可以获得变量的地址
举个例子:

#include<stdio.h>
using namespace std;

int main(){
    int a=10;
    printf("a的地址=%d\na的值=%d",&a,a);
    return 0;
}
a的地址=6356732
a的值=10

在本例中,打印出了a的地址与a的值,a的地址指的是a存放的地址单元,如下图所示。
在这里插入图片描述
注意,指针实际上是一个unsigned类型的整数。

2.7.2 指针变量

指针变量用来存放指针(或者理解为变量的地址)。
指针变量的定义和一般变量的定义有所区别,它在某种数据类型后加 * 表明这是一个指针变量,且 * 在变量类型之后或变量名之前都是可以的
例如 :

int *p1;
char* p2;

另外,如果同时有好几个同类型的指针变量需要定义, * 只会结合于第一个变量名,而如果要让后面定义的变量也是指针变量,需要在后面的每个变量名之前都加上 * 。

int* a1,a2;//a1为int*,a2为int型
int* p1,*p2,*p3;//p1,p2,p3均为int*型

需要注意的是int*型是指针变量的类型,而后面的p才是 变量名,用来存储地址,如:

int *p=&a;//&a是赋值给p的,而不是*p

那么,对于一个指针变量,它存放的是地址,那么如何取得这个地址所对应的元素呢?假设定义了*int p=&a,p存放的是变量a的地址,那么如何通过p获得这个变量呢,我们可以将 * 加在p之前。
举例:

#include<stdio.h>

using namespace std;

int main(){
    int a;//定义一个int型变量a
    int *p=&a;//定义一个指针变量p存放a的地址
    a=123;//将a初始化为123
    printf("a=%d\n*p=%d\np=%d",a,*p,p);
    return 0;
}

可以看到,a和*p分别为值,而p则指向的是地址。

另外,指针变量也可以进行加减法。 如对于一个int* 型指针变量p来说,p+1指的是p所指向的int型的变量的下一个int型变量的地址(下一个指的是跨越了一整个int型,即4Byte),p+i则说明跨越到当前int型变量之后的第 i 个 int 型变量。指针变量的加减法一般用于数组中。

2.7.3 指针与数组

数组实质上是由地址连续的若干个相同类型的数据组合而成。
在C语言中数组名称也作为数组的首地址使用,如 a==&a[0]
举个例子:

#include<stdio.h>

using namespace std;

int main()
{
    int a[10]= {1,2,3,4,5,6,7,8,9,10};
    int *p=a;
    printf("%d",*p);
    return 0;
}

可以看到 ,在本例中a作为数组a的首地址赋值给指针变量p,因此输出*p其实是输出a[0]。

前面提到过,指针变量可以进行加减法。结合这个知识点,可以很容易知道a+i等价于&a[i],a+i其实就是数组a的首地址偏移 i 个int型变量的地址。同时,也可以得到
*(a+i) 与 &a[i] 等价
举个例子,使用指针变量输入、输出数组:

#include<stdio.h>

using namespace std;

int main()
{
    int a[10];
    
    for(int i=0;i<10;i++){
           scanf("%d",a+i);
       }
       
    for(int i=0;i<10;i++){
           printf("%d ",*(a+i));
       }
    
   /*  int *p=a;

    for(; p<a+10; p++)
    {
        scanf("%d",p);
    }
    p=a;
    for(; p<a+10; p++)
    {
        printf("%d ",*p);
    }
    */
    return 0;
}

另外,指针变量可以使用自增操作,因此可以枚举数组中的元素;

#include<stdio.h>

using namespace std;

int main()
{
    int a[10]= {3,4,5,6,8,5,3,1,2,7};
    for(int* p=a; p<a+10; p++)
    {
        printf("%d ",*p);
    }
    return 0;
}

输出结果:

3 4 5 6 8 5 3 1 2 7

最后,提下指针的减法

#include<stdio.h>

using namespace std;

int main()
{
    int a[10]= {3,4,5,6,8,5,3,1,2,7};
    int *p=a;//定义一个指针变量p,指向数组a的首地址
    int *q=&a[5];//定义一个指针变量q,指向a[5]的地址
    printf("%d\n",p);//打印p==&a[0]
    printf("%d\n",q);//打印q==&a[5]
    printf("%d",q-p);//求q-p
    return 0;
}

输出结果:

6356688
6356708
5

q-p的结果明明是20,为什么最后输出为5呢?q-p表示两个地址之间的举例,这个举例又以int为单位,由于一个int为4Byte,故输出20/4=5。即两个int型指针相减,等价于再求两个指针之间相差了几个int

2.7.4 使用指针变量作为函数参数

指针类型也可以作为函数参数的类型,这时视为把变量的地址传入函数。如果在函数中对这个地址中的元素进行改变,原先的数据就会确实的被改变。
举例:

#include<stdio.h>

void change(int *p)
{
    *p=52;
}
int main()
{
    int a=1;
    int* p=&a;
    change(p);
    printf("a=%d",*p);
    return 0;
}

在本例中,定义了一个 int* 指针变量p,将其赋初值为 a 的地址,change 函数将指针变量p(a 的地址)作为函数参数传入,并将p指向地址中的数据改为52,这样最后就改变了p所指向地址中的值。这种传递方式称为地址传递。
下面我们来分析一个经典的例子:使用指针作为函数参数,交换两个数。

#include<stdio.h>

int main()
{
    int a=1,b=2;
    int temp=a;
    a=b;
    b=temp;
    printf("a=%d , b=%d",a,b);
    return 0;
}

输出结果:

a=2 , b=1

一般来说,交换两个数的值要使用中间变量,即先令中间变量temp存放一个数a,再令已被转移数据的a赋值为b,最后把存有a的数据的中间变量temp赋值给b,这样就完成了两个数之间的交换。

那么如果想要把交换功能写成一个函数,应该怎么做呢?首先需要说明,下面这种做法做不到。

#include<stdio.h>

void swap(int a,int b)
{
    int temp=a;
    a=b;
    b=temp;
}

int main()
{
    int a=1,b=2;
    swap(a,b);
    printf("a=%d , b=%d",a,b);
    return 0;
}

输出结果:

a=1 , b=2

因为swap函数传入的是a,b的值,a、b均是局部变量,仅在自己函数范围内有效,故在对数据进行交换不会改变主函数中a、b的值。

接下来介绍指针的使用办法。众所周知,指针存放的是地址,那么将指针变量作为函数参数传递进来也是地址,只有在获取地址的情况下对元素进行操作,才能真正的修改变量。

/*写法三:将交换功能写作函数——正确写法
*/
#include<stdio.h>

void swap(int *p,int *q)
{
    int temp;
    temp=*p;
    *p=*q;
    *q=temp;

}

int main()
{
    int a=1,b=2;
    int *p=&a,*q=&b;
    swap(p,q);
    printf("a=%d , b=%d",a,b);
}

下面介绍两种常见的错误写法:

/*写法四:常见的错误写法一
*   其实*temp、*p、*q都可以看作int型变量,那完全可以像普通变量那样进行交换,
*   这个想法其实没有问题,问题出在temp。在定义int*型指针变量temp时,temp没
*   有被初始化,也就是temp中存放的地址是随机的,如果该地址指向系统工作区间,
*   那么就会出错,且这种概率特别大。
*/
#include<stdio.h>

void swap(int *p,int *q)
{
    int *temp;
    *temp=*p;
    *p=*q;
    *q=*temp;

}

int main()
{
    int a=1,b=2;
    int *p=&a,*q=&b;
    swap(p,q);
    printf("a=%d , b=%d",a,b);
}

/*写法四的改进:
*   找到问题之后,我们只需要给temp赋初值就行了
*/
void swap(int *p,int *q)
{
    int x;
    int *temp=&x;
    *temp=*p;
    *p=*q;
    *q=*temp;
}
/*写法四:常见的错误写法一
*   其实*temp、*p、*q都可以看作int型变量,那完全可以像普通变量那样进行交换,
*   这个想法其实没有问题,问题出在temp。在定义int*型指针变量temp时,temp没
*   有被初始化,也就是temp中存放的地址是随机的,如果该地址指向系统工作区间,
*   那么就会出错,且这种概率特别大。
*/
#include<stdio.h>

void swap(int *p,int *q)
{
    int *temp;
    *temp=*p;
    *p=*q;
    *q=*temp;

}

int main()
{
    int a=1,b=2;
    int *p=&a,*q=&b;
    swap(p,q);
    printf("a=%d , b=%d",a,b);
}

/*写法四的改进:
*   找到问题之后,我们只需要给temp赋初值就行了
*/
#include<stdio.h>

void swap(int *p,int *q)
{
    int x;
    int *temp=&x;
    *temp=*p;
    *p=*q;
    *q=*temp;

}

int main()
{
    int a=1,b=2;
    int *p=&a,*q=&b;
    swap(p,q);
    printf("a=%d , b=%d",a,b);
}

2.7.5 引用

1.引用的含义
引用是c++中一个强有力的语法,在编程时极为实用。众所周知,函数的参数是作为局部变量的,对局部变量的操作不会影响外部的变量,如果想要修改外部变量,那么只能使用指针。那么,有没有什么办法可以不使用指针,也能达到修改传入参数的目的?一个很方便的方法就是c++中的引用。引用不产生副本,而是给原变量起了个别名,即别名和旧名字都是指的同一个东西,且对引用变量的操作就是对原变量的操作。

引用的使用方法很简单,只需要在函数的参数类型后加个 & 就行了(&加在int后面或者参数名前面都可以)。
举个例子:

#include<stdio.h>

void change(int &x)
{
    x=52;
}
int main()
{
    int a=1;
    change(a);
    printf("a=%d",a);
    return 0;
}

输出结果:

a=52

在上述代码中,在change的函数参数前加了&,在传入参数时对参数的修改也会对原变量进行修改。
注意:要把引用的&跟取地址运算符&区别开来,引用并不是取地址的意思。
2.指针的引用
之前在指针第二种错误写法中,我们曾试图通过交换地址从而达到交换两个变量值的效果,但是失败了。这是因为对指针变量的修改无法作用到原指针变量上,此处可以通过引用来实现上面的效果。

#include<stdio.h>

void swap(int *&p,int *&q)
{
    int *temp;
    temp=p;
    p=q;
    q=temp;
}

int main()
{
    int a=1,b=2;
    int *p=&a,*q=&b;
    swap(p,q);
    printf("a=%d,b=%d",*p,*q);
    return 0;
}

注意,由于引用是产生变量的别名,因此常量不可使用引用。于是上面的代码不可以写作 swap(&a,&b),而必须使用指针变量p1和p2存放&a和&b,然后把指针变量作为参数传入。

参考书:《算法笔记》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值