8.3 通过指针引用数组

第八章  善于利用指针
 

目录

第八章  善于利用指针

8.3 通过指针引用数组

8.3.1 数组元素的指针

8.3.2 在引用数组元素时指针的运算

8.3.3 通过指针引用数组元素

8.3.4  用数组名作函数参数

*8.3.5 通过指针引用多维数组


​​​​​​​

 

8.3 通过指针引用数组

8.3.1 数组元素的指针

所谓数组元素的指针就是数组元素的地址。一个变量有地址,一个数组包含若干个元素。每个元素都在内存中占用存储单元,它们都有相应的地址。指针变量可以指向变量,当然也可以指向有地址的数组元素(把地址放在一个指针变量中),这就是数组元素的指针就是数组元素的地址

int a[10] = {0,1,2,3,4,5,6,7,8,9};    //定义a为包含10个整型数据的数组
int * p ;                //定义基类型为int,指针变量p;
p = &a[0];               //把a[0]元素的地址赋给了指针变量p;

以上是使指针变量p指向数组a的第0个元素。

引用数组我们可以用下标法(a[0],a[1]),也可以用指针法(即通过指向数组元素的指针找到该元素)。使用指针的好处是目标程序质量高,占用内存少,运行速度快。

在C语言中,数组名(不包括形参数组名)代表数组首元素(即序号为0的元素)的地址。如下面语句

 p = &a[0];  //p的值是a[0]的地址
 p = a;      //p的值是a[0]的地址

该两行代码等价,都是p指向数组a[0]的地址。

*注意:程序中的数组名不代表整个数组元素,仅仅只代表首元素的地址。

在定义指针变量时可以将对它初始化,如

int * p = a;
 
//等同于下面代码语句  
 
int  *p; 
p = a ;       /*此语句等同于 ————>*/       p = &a[0];

该代码的含义是将a(a[0])的首元素地址赋给指针变量p;

8.3.2 在引用数组元素时指针的运算

这一小节讨论的是关于指针变量的算术运算:什么时候需要用到指针型数据的算术运算?其含义是什么?

指针就是地址,对地址进行乘和除是没有意义的,实际上也无此必要。

在一定条件的情况下允许对指针进行加和减的运算。在什么情况下允许呢?答案是指针变量指向数组元素的时候。可以进行加和减的运算。

指针已指向一个数组的元素时,可以对指针进行以下运算:

1.p+1;
2.p-1;     
3.p++,++p; //自增运算
4.p--,--P; //自减运算
5.p2-p2;   //两个指针相减,p1和p2都是同一个数组中的元素才有意义。
  1. 如果指针变量p已指向数组中的一个元素,则p+1指向同一个数组中的下一个元素。p-1指向同一个数组中的上一个元素。注意执行p+1时并不是将p的值(地址)简单的加上1,而是加上一个数组元素所占用的字节数。如果数组元素是int型,每个元素所占4个字节。那么p+1的值就是p的值(地址)加4个字节,来表示下一个元素。这就是为什么定义指针时,为什么要指定基类型(int,char 等),指针变量加1和减1都是基类型所占的字节数。
  2. 如果p的初值为&a[0];,则p+i和a+i(数组名a是首元素的地址,也就相当于指针变量名)就是数组元素a[i]的地址。它们表示指向数组a中序号为i的元素
  3. *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i].例如*(p+5)或*(a+5)就是a[5];也就是说这三者等价*(p+ii)<==>i*(a+i)<==>a[5];

说明:[] 实际就是变址运算符,即a[i]按a+i计算地址,然后找出该地址单元的值。

      4.p2-p1,结果是p2-p1的值(两个地址之差,也就是字节长度)除以数组元素的长度(也可以理解为基类型的字节长度)。

       例如,p1指向a[1]的地址2000;p2指向a[2]的地址2004.那么  p2-p1 = (2004-2000)/4 = 1; 

注意:p2+p1是无实际意义的,两个地址是不能相加。

8.3.3 通过指针引用数组元素

例题  有一个整型数组a,有10个数,要求输出数组中的全部元素

引用数组中元素的值【1】下标法【2】数组名计算元素的地址【3】指针变量所指向的元素

【1】下标法

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

运行结果:

【2】数组名计算元素的地址,找到该元素的值

#include<stdio.h>
int main(){
    int a[10] = {0,1,2,3,4,5,6,7,8,9};
    int *p;
    p = a;
    for(int i=0;i<10;i++){
        printf("%4d",*(a+i));
    }
    printf("\n");
    return 0;
}

运行结果:

【3】用指针变量指向数组元素

#include<stdio.h>
int main(){
    int a[10] = {0,1,2,3,4,5,6,7,8,9};
    int *p;
    for(p=a;p<(a+10);p++){
        printf("%3d",*p);
    }
    printf("\n");
    return 0;
}

运行结果:

程序分析:3种方法结果都是相同的,但效率不同 。【1】下标法【2】数组名这两种引用数组元素执行效率是相同的,编译系统将a[i]转化为*(a+i)处理的,也就是先计算元素的地址。每次都要找比较费时。

                                                        【3】指针变量不必每次计算地址,像p++这种自加操作,有规律的改变地址值(p++)能够大大的提高执行效率。

 

注意:

  1. 数组名(a)是个常量,是不能改变的。因为数组名(a)代表首元素的地址,它是一个指针型常量,它的值在程序中运行期间是固定不变。所以数组名不能实现算术运算:a++,++a这些都是错误的代码。
  2. 时刻注意指针变量的当前值。
  3. 如果程序中引用数组元素a[10]{但实际数组最后一个元素是a[9],不存在a[10]这个元素},系统会把它按*(p+10)处理,即先找到(a+10)的值(是个地址),然后找出它指向的单元*(a+10)的内容。虽然编译系统不会报错。这是逻辑上的错误,这种错误比较隐蔽。因此在使用指针变量指向数组元素时,应切实保证指向数组中有效的元素。
  4. 指向数组元素的指针变量也可以带下标。p[i]。带下标的指针变量是什么含义呢?前提条件当指针变量指向数组元素时,指针变量可以带下标。因为在程序编译时,对待下标处理方法是转化为地址的,对p[i]处理为*(p+i),如果p指向一个整型数组a[0],则p[i]代表a[i]。但是必须弄清楚p当前值是什么?如果p指向a[3],那么p[2]不代表a[2]。记住p是一个指针变量,数组名a是一个地址常量。
  5. 利用指针引用数组元素,比较方便灵活,有不少技巧。 

p++;*p;   //p++使p指向下一个元素a[1],*p得到下一个元素a[1]的值。


*p++;     //由于++和*同级优先级,结合方向为自右而左,因此等同于*(p++)。先引用p的值,实现*p的运算,然后再使p自增1。


 *(p++)和*(++p)是不相同的。前者是先取*p的值,然后使p加1;后者是先p+1,然后取*p的值。


++(*p)。表示p所指向的元素加1。如果p指向a,a[0]的值是4,那么执行++(*p)后,a[0的值4+1 =5。
 

如果p当前值是指向数组a中第i个元素a[i],那么:

  1. *(p--)等价于*a[i--];
  2. *(--p)等价于*a[--i];
  3. *(++p)等价于*a[++i];
  4. *(p++)等价于*a[i++];   

将++和--运算符用于指针变量十分有效,可以使指针变量自动向前或向后移动,指向下一个或向上一个数组元素。

8.3.4  用数组名作函数参数

当数组名作为参数时,如果形参数组中各元素的值发生了改变,实参数组元素的值也随之改变。这是什么原因呢?学完指针后我们就能很好的去理解。

先看数组元素作实参时的情,如果定义了一个函数,其原型为

void swap(int x,int y);
 

假设函数的作用是将两个形参(x,y)的值进行交换,有下面的函数调用;

swap(a[1],a[2]);

用数组元素a[1]和a[2]作实参时,与用变量作实参时是一样的。属于“值传递”方式。将a[1]和a[2]的值单向传递给x,y时,当x,y的值发生改变时,a[1]和a[2]的值不发生改变。

我们再看看数组名作为函数参数时的情况。我们学习所知实参数组名代表首元素的地址。而形参是用来接收从实参传递过来的数组首元素的地址。因此,形参是一个指针变量。(只有指针变量才能存放地址)。在C语言编译器中都是将形参数组名作为一个指针变量来处理的。如下面代码

void fun(int arr[],int n);
void fun(int * arr,int n);

这两行代码等价。在函数调用时,系统会在fun函数中建立一个指针变量arr,用来存放从主调函数传递过来的实参数组首元素的地址。

当arr接送实参数组的首元素地址后,arr就指向实参数组的首元素。也即是指向a[0]。因此

*arr 等价于a[0];

arr+1指向arr{1];    *(arr+1)等价于arr[1]; 

arr+2指向arr[2];    *(arr+2)等价于arr[2]; 

变量名数组名作为函数参数的比较
实参类型变量名数组名
要求形参的类型变量名数组名或者指针变量
传递的信息变量值实参数组首元素的地址
通过函数调用能否改变实参的值不能改变实参变量的值能该改变实参数组的值

 

说明:C语言调用函数时采用虚实结合的方法都是“值传递”,当用变量名作为函数参数时传递的是变量的值;当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。

注意:实参数组名是一个指针常量(首元素的地址),但是形参数组名不是一个固定的地址,而是按地址指针变量来处理的。

*8.3.5 通过指针引用多维数组

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值