C语言数据结构(3)——指针类型

指针是C语言的主要特色应用,在C语言中,指针作为一种基本数据类型,可与数组、函数、结构体等相结合应用于各种场合。在学习指针时我们要做到两点:永远要清楚每个指针指向了哪里;永远要清楚指针的指向位置是什么。

指针和指针变量

在学习指针前我们应该搞清楚变量的意义,每个变量都具有两个物理意义,一个是它本身的内容,另一个是变量的地址。 我们可以把计算机的内存看作一条长街上的一排房屋,每座房子都可以容纳数据,并通过一个房号来标识。这时房子里的数据就是变量本身的内容,房号就是变量的地址。更为准确的描述应该是:
变量的内容是变量在所分配的存储空间中存放的数据。
变量的地址是变量所分配存储空间的首地址。

然后我们需要弄清楚直接存取和间接存取。程序中对变量进行存取操作,就是对某个地址的若干字节存储单元进行操作。一般情况下,在程序中只需要指出变量名,无需知道每个变量在内存中的具体地址,每个变量与具体地址的联系由C编译系统来完成。程序执行时对变量进行存取操作,实际上就是某个地址的存储单元进行操作。这种直接按变量的地址存取变量值的方式称为“直接存取”。 例如,在程序中引用整型变量名a,系统就会自动根据a的地址值直接访问变量a的4字节内容。
若定义一种特殊的变量,这种变量专门用来存放内存地址,称为指针变量,如图,假设我们定义了一个这样的变量pa,它也有自己的地址2004;若要将变量a的地址(1001)存放到变量pa中,这时要访问变量a所代表的存储单元,可以先找到pa的内存地址(2004),从中取出a的地址(1001),然后再去访问以1001为首地址的存储单元。这种通过变量pa间接取得到变量a的地址,然后再存取a的值的方式称为“间接存取”。
在这里插入图片描述

搞清楚上面两个概念后我们就可以更轻松的学习指针了,什么是指针?——指针就是“地址”,变量的地址称为指针。指针作为一种数据类型,也有指针常量和指针变量之分,其变量也和其他类型的变量意义,需要先定义后才可以进行运算或操作。指针变量的值就是应是某个变量的地址。
如果指针变量中已具有地址值,就可以形象的说指针变量指向某个变量,同时将指针变量所指向的这个变量称为目标变量。例如:指针变量pa通过pa=&a与a变量a建立了指向关系,我们称指针变量pa指向变量a,变量a是指针变量pa的目标变量。

:指针的类型和指针所指向的类型是两个概念。指针的类型是指针自身的类型,而指针所指向的类型是指针所指向的变量的类型。例如:

int a=5,b;
float x;
int *pi;
float *pf;

其中:pi指针的类型:int *,pf指针的类型:float *,pi所指向的类型:int,pf所指向的类型:float。

指针变量的定义

指针变量的一般定义形式:

数据类型 * 指针变量名
其中:“ * ”表示其后的变量名为指针类型;“数据类型”是定义指针变量所指向的目标变量的数据类型,也可称为指针变量的基类型。

指针变量的初始化

指针变量也可以像其他类型变量一样,在定义的时候赋初值,一般形式为:
数据类型 * 指针变量名=初始地址值

#include <stdio.h>
void main()
{
    
    
	float x,*p1=NULL,*p2=&x;
	int y,*p3=&y;
	char name[30],*cp=name;
	...
}

如上所示,使p1成为空指针,p2、p3以及cp分别存放x的地址、y的地址和name数组的首地址。由此可以说,p2指向x,p3指向y,cp指向name[0]。
:1.当把一个变量的地址作为初值赋给指针时,该变量必须先给以定义,且该变量的数据类型必须与指针的数据类型一致

int n;
int *p=&n;

或者用如下等价定义

int n,*p=&n;

2.也可把一个已初始化的指针值作为初值赋予另一指针

float x,*p=&x,*q=p;

p和q都具有相同的地址值,都指向同一变量x。
3.也可通过初始化定义某种类型的空指针

int *p=0;/*值0是唯一能够直接付给指针变量的整型数*/
int *p=NULL;

以上两行代码作用相同,均是定义了一个空指针。

指针的基本运算

1.取地址运算
&要求运算变量是变量或数组元素,它返回其指向变量或数组元素的地址。一般形式为:
&变量名或数组元素名

int a,*p;
pa=&a;

实现把变量的地址赋给指针pa,此时指针pa指向整型变量a。
2.间接存取运算
间接存取运算符“ * ”通常称为“间接访问运算”,是一个单目运算符;其右操作数必须是一个指针值,返回值是其指定地址的值。一般形式为:
*指针变量或目标变量的地址

int a,b,*p;
pa=&a;
b=*pa;

其中运算符“ * ”以访问pa为地址的存储区域,而pa中存放的是变量a的地址,因此*pa访问的地址是变量a的开始地址,它就是a所占的存储区域,所有上面的赋值表达式等价于b=a。

可见“&”运算符和“*”运算符互为逆运算。注意区分以下不同表达式的不同含义:
pa——指针变量
*pa——指针pa的目标变量
&pa——指针变量pa占用存储区域地址
3.赋值运算
(1)把一个变量的地址赋给一个同类型的指针,如:

int  a, *pa;
pa=&a; 		/* 使pa指向变量a */

(2)把一个指针的值赋给另一同类型的指针,如:

char  c, *s1=&c, *s2;
s2=s1;      /* 结果s1和s2指向同一变量c */

(3)将地址常量如数组名赋给同类型的指针,如:

char  *str,ch[80];
str=ch;      /* 使str得到字符数组ch的首地址,即str指向数组ch */

(4)同类型指针算术运算的结果,如果还是地址量的话,可以赋值给同类型的指针。例如:

int *p1,*p2,a[20];
p1=a;  p2=p1+5;  p1=p2-3

赋初值与赋值运算的区别: 赋初值是定义变量的同时赋给变量一个值,不是间接存取运算。间接存取运算的结果是一个整型变量,不可以接收地址值。

4.指针的算数运算
(1)把指针加上或减去一个整数

int a[10],*pa=a,*p;
pa+=5;/*pa指向变量a[5]*/
pb=pa-3;/*pa指向变量a[2]*/

(2)两个具有相同类型的指针相减

int a[10],*pa=&a[1],*pb=&a[8];
int dist;
dist=pb-pa;/*diat为7,表明pa与pb两个指针所指向的数组元素之间的距离*/

指针加减运算要点:
① 两个指针变量不能做加法运算。
② 只有当指针变量指向数组时,并且只有当运算结果仍指向同一数组中的元素时,指针的加减运算才有意义。
③ 指针加减运算的结果不以字节为单位,而是以数据类型的大小(即sizeof(类型))为单位。
④ 只有当两个指针变量指向同一数组时,进行指针相减才有实际意义。
⑤ *(p1+n)与(*p1)+n是两个不同的概念。
5.指针的关系运算
⑴ 指向同一数组的两个指针可以进行关系运算
⑵ 指针与一个整型数据比较是没有意义的,不同类型指针变量之间的比较是非法的。
⑶ NULL可以与任何类型指针进行==、!=的运算,用于判断指针是否为空指针。

指针和数组

C语言中,指针和数组的关系极为密切。在介绍数组时我们已经知道了数组元素在计算机内存中的存储方式已经数组的地址是怎么分配的,并且只有指向同一数组的指针之间的算数运算和关系运算才有意义,所以数组和指针结合在C语言中应用也是很多的。

指针与一维数组

1.建立一个指向某个一维数组的指针,可以先定义,然后对指针赋值。例如:

int a[5],*pa,*p;
pa=a;/*  pa=&a[0];  */

因为数组名a是该数组的首地址,也即a[0]的地址,所以指针pa指向该数组首地址。此时*pa的值就是a[0]的值。pa+1则指向下一个元素。特别情况下,也可以在指针定义后直接让指针指向某一个数组元素,如:p=&a[5];
2.通过指针引用数组元素
通常引用一个数组元素有三种方法:
(1)下标法,如a[i]形式;
(2)数组名地址法,由于数组名是数组的首地址,根据地址的计算法则,则a+i就表示了以数组名a为起始地址的顺数第i个元素,即a[i]的地址,那么 *(a+i)即为a[i]
(3)指针法,有两种形式
①指针地址法。既然有pa=a,则pa+i就表示以pa为起始地址的顺数第i个元素,即a[i]的地址,那么 *(pa+i)即为a[i]
②指针下标法。由于pa=a, *(pa+i)相当于a[i],所以C语言中允许直接用pa[i]的形式来表示以pa指示的位置为起点顺数第i个同类型的数据。

综上所述:对同一数据类型指针pa和数组a来说,一旦二者建立了pa=a的联系(即指针pa指向数组a的首地址),则下述对数组元素a[i]的表示就是等价的:
a[i]、* (a+i)、* (pa+i)、pa[i]

使用指针法访问数组元素时要注意:
(1)指针变量的值可以改变,但作为数组名的指针常量值是不可以改变的。如a是数组名,p是指针,b是变量,则试图改变a的值都是错误的,而指针是地址变量,其值可以改变。
(2)利用指针变量访问数组元素,要考虑数组越界问题
(3)在数组元素的下标表示法中如果采用指针变量,其下标可以出现负值。 如若有指针p和数组b,当p=&b[2]时,b[1]元素如果要用指针变量p的下标表示法为p[-1],指针法表示为*(p-1)
(4)在指针变量运算中要特别注意单目运算符的右结合性。如y=* p++等价于y=* (p++)、y=* ++p等价于y=*(++p)

指针与二维数组

二维数组与一维数组类似,建立指针和数组的关系后可以用指针来表示数组的元素。
现若有数组a[3][4],则
指示行元素的方式有:
a[0]可以用*(a+0)即* a表示
a[i]可以用* (a+i)表示
指示列元素的方式有:
&a[1][3]可用a[1]+3或者* (a+1)+3表示
&a[i][j]可用a[i]+j或者* (a+i)+j表示

二维数组元素的表示法有以下几种:
数组下标法:a[i][j]
指针表示法:* (* (a+i)+j)
行数下标表示法:* (a[i]+j)
列数下标表示法:* (a+i)[j]

总结

指针是C语言中重要的一部分,是C语言的一大特色。学好指针要理清变量的两个物理意义,变量的地址和变量的内容,指针变量也是一样也有其变量地址和变量内容,不过指针变量的变量内容是其他类型变量的地址,在引用指针变量时就是间接的存取指针变量所指向的变量内容。在使用指针变量是要注意“ * ”标识符,在不同的情况下是不同的含意,* p就代表指针p的目标变量,&p代表指针p的地址。注意指针和数组结合的应用方式,数组用指针法表示的方法。

猜你喜欢

转载自blog.csdn.net/Tao_9/article/details/129836560