浅谈指针

指针学习笔记

指针是什么

指针就是地址

在计算机里面有一个核心硬件设备叫做内存,程序在运行的时候在会给在代码里面定义的每一个变量都会分配一个内存单元用来存储变量的值,每个内存单元都有一个地址类似于家里的门牌号,通过这个”门牌号”就可以找到这个变量的值。

指针变量里面存储的就是其它变量的地址,例如下图中a是一个指针变量它的地址(”门牌号“)是0002,这个地址对应的存储单元里面存储的内容是1008。根据存储地址1008与变量之间的映射关系,很容易看出指针a指向来指针变量b,通过取值运算符号*a就可以得到变量b在存储器里面存储的内容。在32位体系下,每个变量的地址只需要4个字节就可以表示所以任何指针变量只占用4个字节,即使是指向结构体变量的指针也只占用4个字节、所以使用指针变量作为函数参数可以充分节省内存空间、减少内存拷贝。

指针是C的一个重要特色 使用指针的优点一是提高程序效率,二是可从函数调用得到多个可改变的值,三是可以实现动态分配内存

指针变量

  1. 定义 存放地址的变量是指针变量,定义指针变量的一般形式为 类型名 *指针变量名,例如int * pointer_1,左端的int是在定义指针变量是必须指定的 基类型,在定义指针变量时必须指定基类型,由此可知指针或地址是包含有类型信息的。

指针变量定义的归纳:

`变量定义         类型表示            含义 `

`int i           int                 定义整型变量i`

`int *p          int *               定义p为指向整形数据类型的指针变量`

`int a[5]        int[5]              定义整型数组,它有五个元素`

`int *p[4]       int *[4]            定义指针数组p,它由4个指向整型数据类型的指针元素组成`

`int (*p)[4]     int (*)[4]          p为指向包含4个元素的一维数组的指针变量(行指针)`

`int *p(参数列表) int * ()           p为返回一个指针的函数,该指针指向整型数据类型`

`int (*p)(参数列表) int(*)()          p为指向函数的指针,该函数返回一个整型值(函数接口)`

`int **p           int **           p为一个指针变量,他指向一个指向整型数据类型的指针变量`

`void *p           void *           p为一个空类型的指针变量`
  1. 引用
    int a;int *p;p=&a 把整型变量a的地址赋给指针变量p,指针变量p的值就是变量a的地址,称为p指向a

    printf("%d",*p); 引用指针变量指向的变量,p即为变量a的值,称为引用指针变量指向的变量*

    printf("%o",p); 以八进制输出指针变量p的值,也就是a的地址(&a),称为引用指针变量的值

  2. 作为函数参数 函数的调用只可以得到一个返回值,而使用指针变量作参数,可以得到多个变化了的值(指针优势二)我们先考察以下程序(读入三个整数a,b,c并由大到小输出,用函数实现且输出顺序仍为a,b,c)

     #include<stdio.h>
     int main()
     {
     	void exchange(int *q1,int *q2,int *q3);
     	int a,b,c;
     	int *p1,*p2,*p3;
     	scanf("%d %d %d",&a,&b,&c);
     	p1=&a;p2=&b;p3=&c;
     	exchange(p1,p2,p3);
     	printf("%d %d %d",a,b,c);
     	return 0;
     }
    
     void exchange(int *q1,int *q2,int *q3)
     {
     	void swap(int *pt1,int *pt2);
     	if(*q1<*q2) swap(q1,q2);
     	if(*q1<*q3) swap(q1,q3);
     	if(*q2<*q3) swap(q2,q3);
     }
     void swap(int *pt1,int *pt2)
     {
     	int temp;
     	temp=*pt1;
     	*pt1=*pt2;
     	*pt2=temp;
     }
    

运行结果:输入:1 9 7 输出 9 7 1 上面这段简短的程序生动诠释了指针传参的优势。大家幼儿园就知道函数实参与形参的传递为值传递,只能单向传递,那上述程序是如何做到改变的主函数中变量a,b,c的值呢?我们可以这样理解程序开始时指针变量p1值为整型变量a的地址,p1指向a,p2,p3同理,再调用exchange(p1,p2,p3)exchange函数中形参指针变量q1,q2,q3为p1,p2,p3的副本,同时一起指向a,b,c,再通过swap函数改变副本q1,q2,q3的指向时p1,p2,p3的指向同时被改变,因为p1与q1可以说是同时一起指向a,若在swap中改变的是指针变量的值而不是指针变量所指向的值,那么影响是不会传递到主函数中的(读者可自行上机尝试),因为改变q1,q2,q3的值并不会改变p1,p2,p3(值传递)。这便是指针传递的妙处所在,在后面传递数组中也同样会运用到(原理相同)。

通过指针引用一维数组

  1. 数组元素的指针 变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,那么他们都有相应的地址,所谓数组元素的指针就是数组元素的地址。我们可以用一个指针变量指向一个数组元素 int a[10]={1,2,3,4,5,6,7,8,9,10}; int *p; p=a;等价于p=&a[0]因为p=a就是把a数组的首元素的地址赋给指针变量p,这个原理与后面的字符串是一致的。后三行代码还能合并为int *p=a把a[0]地址赋给p而不是*p。

  2. 我们习惯使用下标法来引用数组元素,而指针也可引用且具有优势一,下面我们来介绍通过指针引用数组元素。指针是可以进行加减运算的,比如有+,++等,p+1就是指向同一数组的下一个元素,其余同理。若p1,p2两个指针相减,那么这两个指针必须是指向统一数组的,结果为p1所指元素与p2所指元素的相对位置。地址不能相加,p1+p2是没有意义的。接着我们称(a+i)或(p+i)为指针法引用数组,代表a[i]的地址,而(a+i)或(p+i)则指a[i]的值。比较下面三段代码:

     `for(int i=0;i<10;i++) printf("%d",a[i]);`
     for(int i=0;i<10;i++) printf("%d",*(a+i));
    

    for(p=a;p<(a+10);p++) printf("%d",*p);``// 用指针指向当前的数组元素,此方法可大大提高执行效率,程序简洁,高效

请读者思考如果把第三种情况中的p++改为a++,然后输出*a可以吗?答案是否定的,因为a是一个指针类型常量,代表数组首元素的地址。需要提醒的一点使用指针法时用注意指针当前的所指的位置,比如

int *p=&a[2];*(p+1)!=a[1]

接着我们来介绍用数组名作函数参数。由上文exchange与swap函数可知当数组名作参数时,如果形参中各元素的值发生变化,实参数组元素的值随之变化。实际上,C编译都是将形参数组名作为指针变量来处理的。归纳可知,数组传参共有四种情况

int main()
{
	int a[10];
	...
	f(a,10);
}
void f(int x[],int n)
{
	//形参与实参均用数组名
}

int main()
{
	int a[10];
	...
	f(a,10);
}
void f(int *x,int n)
{
	实参用数组名,形参用指针变量名
}

int main()
{
	int a[10],*p=a;
	...
	f(p,10);
}
void f(int *x,int n)
{
	实参形参都用指针变量
}

int main()
{
	int a[10],*p=a;
	...
	f(p,10);
}
void f(int x[],int n)
{
	实参用指针变量,形参用数组名
}

6/17/2022 3:10:22 PM 先写到这,后面的有空再来写。

通过指针引用多维数组

通过指针引用字符串

指向函数的指针

返回指针值的函数

指针数组和多重指针

动态分配内存

posted @ 2022-06-17 15:16  DBDZJM  阅读(288)  评论(0)    收藏  举报