指针(二)指针与数组、指针与函数

一、指针与数组

1. 用指针处理数组元素

  • 指针加减运算的特点使得指针特别适合存储在一段连续内存空间中的同类数据,而数组恰好是具有一定关系的若干同类型变量的集合体。

  • 数组元素的存储在物理上也是连续的,数组名就是数组存储的首地址。

int arr[5];//声明一个int型的数组
cout<<*arr;//数组名arr就是数组的的首地址
//等价于
cout<<arr[0];

cout<<*(arr+3);
//等价于
cout<<arr[3];

细节:

把数组作为函数的形参,等价于指向数组元素类型的指针作为形参。

void f(int p[]);
void f(int p[3]);
void f(int *p);
//这三种写法出现在形参列表中都是等价的

因为一个函数要接受一个数组,它接受的是数组的首地址,而数组的首地址就是一个指针型。

拓展 :标准库函数begin和end函数

为了让指针的使用更简单,更安全,C++11标准引入了两个名为begin和end的函数

这两个函数定义在iterator头文件中。

begin函数返回数组a首元素的指针。

end函数返回数组a为**尾元素下一个位置**的指针。

#include<iostream>
#include<iterator>
using namespace std;
int main()
{
int a[5]={1,2,3,4,5};
int *beg=begin(a);
int *last=end(a);
}

2. 指针数组

  • 如果一个数组的每个元素都是指针变量,这个数组就是指针数组。
  • 指针数组的每一个元素都必须是同一类型的指针。

声明一维数组的语法形式为:

数据类型 * 数组名 [下标表达式];

注意:

  • 下表表达式指出数组元素的个数
  • 类型名确定每个元素指针的类型
  • 数组名是指针数组的名称,同时也是这个数组的首地址
  • 必须先赋值,后引用
int *p[3];//声明含有三个指针指向int型的指针数组
int a=10;
p[0]=&a;
cout<<*p[0];
//等价于
cout<<**p;
//输出结果都为:10
//p是指针数组的首地址,相当于二级指针。

实例:利用指针数组输出单位矩阵

#include<iostream>
using namespace std;
int main()
{
	int line1[]={1,0,0};
	int line2[]={0,1,0};
	int line3[]={0,0,1};
    //定义数组,三行矩阵
    
    int *pline[3]={line1,line2,line3};//定义整形指针数组并初始化
    for(int i=0;i<3;i++)       //对指针数组元素循环
    {
        for(int j=0;j<3;j++)   //对矩阵每一行循环
            cout<<p[i][j]<<" ";
        //p[i][j]等价于 *(pline[i]+j)
        //还等价于 *(*(pline+i)+j)
    cout<<endl;
     } 
}
//运行结果:
1 0 0
0 1 0
0 0 1

3. 指针访问二维数组

二维数组在内存种是以行优先的方式按照一维顺序关系存放的,因此对于二维数组,可以将其理解为一维数组的一堆数组,数组名是它的首地址,这个数组的元素个数就是二维数组的行数,每个元素指向一个一维数组。

int arr[3][3]={1,2,3,4,5,6,7,8,9};
//声明一个二维数组

这个二维数组就是由三个一维数组构成。

二维数组命arr代表这三个一维数组的首地址,代表a[0]的地址,为二级指针。

arr[0] 则可以看作第一个一维数组的数组命,代表这个一维数组的首地址也即 a[0][0] 的地址,为一级指针。

同理 arr[1] 则可以看作第一个一维数组的数组命,代表这个一维数组的首地址也即 a[1] [0] 的地址。

注意:指针数组与二维数组的区别

指针数组与二维数组的区别

尽管指针数组与二维数组存在本质的差异,但二者具有相同的访问形式,可以把二维数组当作指针数组来访问。

实例:

int mian()
{
int arr[3][3]={{1,2,3},{4,5,6},{7,8,9}};
//依次访问二维数组并输出
for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
        cout<<*(*(arr+i)+j);
//把arr[3][3]看成由三个一维数组组合而成
//*(arr+i)表示指向第i个一维数组
//(*(arr+i)+j)表示指向第i个一维数组中的第j个元素
}

同理,可以对三维数组为例自行分析。

二、指针与函数

1. 指针作为函数参数

以指针作为函数参数的作用

  • 使实参与形参指针指向共同的内存空间,以达到参数双向传递的目的,即通过在被调函数中直接处理主调函数中的数据而将函数的处理结果返回其调用者。
  • 减少函数调用时数据传递的开销。
  • 通过指向函数的指针传递函数代码的首地址。

习惯:

如果在函数体中不需要通过指针改变指针所指向对象的内容,应在参数表中将其声明指向常量的指针,这样使得常量对象被取地址后也可以作为该函数的参数。

const T *ptr;//T为数据类型。

实例:完成两个数的交换(用指针作参数)

void swap(int *x,int *y)//接收的是两个地址
{
    int temp;
    temp=*x; 
    *x=*y;
    *y=temp;
}
int main()
{
    int *p1,*p2,a=100,b=10;
    p1=&a;p2=&b;   
    //完成两个数的交换
    swap(p1,p2);//跟简单的写法 swap(&a,&b);
	//输出检查是否完成交换
    cout<<a<<" "<<b<<endl;
    cout<<*p1<<" "<<*p2<<endl;
}

2. 指针性函数

  • 除void类型的函数外,函数在调用结束之后都要有返回值,指针也可以是函数的返回值。

  • 当一个函数的返回值是指针类型时,这个函数就是指针型函数。

  • 使用指针型函数的最主要在函数结束时把大量的数据从被调函数返回到主调函数中。

指针型函数的一般形式:

数据类型 * 函数名 (参数表)

{ 函数体 }

//只是简单的写个求两数和函数,说明用法。
int*  f(int x,int y)
{
    int p=x+y;
    return &p;
}
int main()
{
    int num1,num2;
    cin>>num1>>num2;
    cout<<*f(num1,num2);//返回的f(1)为指针,
}

3. 指向函数的指针

  • 执行程序的代码也想数据一样被调入内存并占据一定的空间。
  • 每一个函数都有函数名,实际上这个函数名就表示函数的代码在内存的起始地址。
  • 函数指针就是专门用来存放函数代码首地址的变量。

声明一个函数指针时,也需要说明函数的返回值,形式参数列表,其一般语法为:

数据类型 (*函数指针名)(形参表)

函数指针在使用之前也要进行赋值,使指针指向一个已经存在的函数代码的起始地址。

一般语法为:

函数指针名=函数名;

注意:

  • 等号右边的函数名所指出的必须是一个已经声明过的、和函数指针具有相同返回类型和相同形参表的函数
//只是简单的写个函数,说明用法。
int f(int x)//定义一个f函数
{    return x;   }
int main()
{
    int num;
    cin>>num;
    int (*p)(int);
    //声明一个形参为int,返回值类型为int的函数指针
    p=f;//将函数f的起始地址赋给p
    //测试
    cout<<p(num)<<endl;//
    cout<<f(num)<<endl;
}

拓展:

由于对函数指针的定义在形式上比较复杂,如果在程序中出现多个这样的定义,多次重复这样的定义会相当繁琐,一个很好的解决方法是使用 typedef 。

typedef int (*function)(double);
//声明“function为一个double形参,返回值类型int的函数的指针”类型的别名。
//使用。
function f;
//f就是该类型为function的函数指针。

关于 typedef 的用法以后我会专门做一期来讲。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空丶star

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值