目录
一、什么是指针
1.地址
计算机的存储设备可以存储二进制信息,系统将每八个二进制位编为一个基本存储单元,称为字节(Byte)。如果在程序中定义了一个变量在对程序进行编译时,编译器就会给这个变量分配内存单元。
内存区的每一个字节有一个编号,这就是”地址”。编译系统根据程序中定义的变量类型,分配一定长度的空间:
注意:程序运行时,计算机操作系统会给程序分配一整块连续的内存空间,而编译器使用的“地址”是这一块内存空间中的“相对地址”
2.数据在内存中的存储
int main()
{short i,j,k;
i=1,j=2,k=3
printf("%d,%d,%d",i,j,k);
}
3.数据的读写方式——直接访问
变量的三要素:名字、类型与值
每个变量都通过变量名与相应的存储单元相连系,具体分配哪些单元给变量,由C编译系统完成变量名到对应内存单元地址的变换
变量分配存储空间的大小由类型决定
变量的值则是指相应存储单元的内容数据的读写
x=25;
y=4*x+12;
实际编译后是根据变量的地在程序中通过变量名读写数据,:址读写内存单元的内容,这种方式称为直接访问
4.数据的读写方式——间接访问
将一个变量的地址存储到新的变量中,通过新变量找到原变量的存储单元,再读写数据,称为间接访问。例如:int i=1;p=&i;
二、变量的指针和指向变量的指针变量
1.指针变量
●指针
·是一个变量的地址
●指针变量
·用来存储变量地址(指针)的变量
●指向
·通过变量的地址(指针)可以找到变量的内存单元
·存储变量地址的变量(指针变量)指向该变量
2.指针变量的定义
一般形式:[存储类别] 数据类型*指针变量名;
例 *p1,*p2;
在定义指针变量时要注意
(1)指针变量前面的“*"表示该变量为指针型变量。指针变量名则
不包含“*”。
(2)int *p1,*p2;与 int *p1,p2;意义不同
(3)指针变量定义后,其值不确定,应用前必须先赋值(指向)
p1=&x;
(4)在定义指针变量时必须指定基类型:一个变量的指针的含义包括两个方面,一是以存储单元编号表示的纯地址(如编号为2000的字节)一是它指向的存储单元的数据类型(如int,char,float等)。指针变量只能指向其基类型变量。
(5)指针变量和整型变量不能通用
point = &i;
(语法正确)
point=2002;
(语法错误)
(6)指针变量并不固定指向一个变量,可指向同类型的不同变量
int a,b;
point=&a ;
point= &b ;
3.怎样引用指针变量
(1)指针变量的三种引用方式
①给指针变量赋值
int a,*p,*q;p=&a;
②引用指针变量指向的变量。
printf("%d",*p);*p=1;
③引用指针变量的值
printf(“%o",p);//合法,但没有意义
q=p;
注意
要熟练掌握两个有关的运算符
(1)&取地址运算符。&a是变量a的地址
(2)*指针运算符(或称“间接访问”运算符),*p代表指针变量p指向的对象
注意:*与&具有相同的优先级,结合方向从右到左。这样,&*p即&(*p)是对变量*p取地址,它与&a等价;p与&(*p)等价,a与*(&a)等价
4 .指针变量做函数的参数
函数的调用可以(而且只可以)得到一个结果:(即函数返回值),而使用指针变量作参数,可以得到多个变化了的值。
如果想通过函数调用得到n个要改变的值,可以这样做:
①在主调函数中设n个变量,用n个指针变量指向它们;
②设计一个函数,有n个指针形参。在这个函数中改变这n个形参的引用数据;
③在主调函数中调用这个函数,在调用时将这n个指针变量作实参,将它们的值,也就是相关变量的地址传给该函数的形参,
④在执行该函数的过程中,通过形参指针变量,改变它们所指向的n个变量的值;
⑤主调函数中就可以使用这些改变了值的变量。
三、通过指针引用数组
1.数组元素的指针
设有定义:int a[10]={1,3,5,7,9};
·数组a在内存分配10个连续的int元素,每个元素相当于一个int变量,都有自己的地址,即数组元素的指针。
·指向数组元素的指针变量的定义方法与指向基本类型变量的指针的定义方法相同
int *p;
p=&a[2];(把数组元素a[2]的地址赋给指针变量p)
·C语言规定:数组名是一个常量指针,它的值为该数组的首地址
语句p=a;可以把数组的首地址赋给指针变量p
等价于p=&a[0];
·在定义指针变量的同时可赋初值
int *p=&a[0];
或 int *p=a;
2.在引用数组元素时指针的运算
(1)指针的算术运算
①指针加减一个整数
一个指针可以加、减一个整数n,其结果与指针所指对象的数据类型有关。指针变量的值(地址)增加或减少n*sizeof(指针类型)
例:
int a=2,b=4,c=6;int p=&a;p=*p+2;
②同类型两指针相减:
表示两个指针之间包含的该类型元素个数如上例中:表达式&c-&a的值为2
③指针算术运算的作用
对指向一般数据的指针,加减运算无实际意义。加减运算常用于数组的处理,因为数组元素的存储单元连续。例如:short al10],*p=a,*x
x=p+3;实际上是p加上3*2个字节赋给x,x指向数组的第三个分量a[3]
对于不同基类型的指针,指针变量“加上“或“减去”一个整数n所移动的字节数是不同的。例如:a[10],*p=a,*x;float
x=p+3;实际上是p加上3*4个字节赋给x,但x依然指向数组的第三个分量a[3]
同一数组中,不同元素的两个指针相减,等于元素下标的差。
例如:p=&a[3],g=&a[7] 则q-p的值是4
(2)指针的关系运算
和基本类型变量一样,指针能进行关系运算例如:p>q,p<q,p==q,p!=q
指针的关系运算在指向数组的指针中广泛的运用,假设 p、a是指向同一数组的两个指针,执行p>q的运算,其含义为若表达式结果为真(非0值),则说明p所指元素在q所指元素之后。或者说q所指元素离数组第一个元素更近些
指针进行关系运算之前,指针必须初始化,另外,只有基类型相同的指针才能进行比较。
(3)指针变量的自增自减运算
指针变量自增、自减运算具有上述运算的特点,但有前置后置、先用后用的考虑,务请小心。
例如:
int a[10], *p=a,求X
x=p++;/*x第一个元素分量,p指向第二个元素*1x=++p;/*x、p均指向数组的第二个分量*/
*p++相当于*(p++)
*(p++)与(*p)++含义不同,前者表示地址自增,后者表示当前所指向的数据自增。
3.通过指针引用数组元素
(1)在定义了指向数组元素的指针后,可以通过指针引用数组元素
例如:int a[10],*p=a;
*p=5;表示对p当前所指的数组元素赋以一个值5。即a[0]=5
p+1指向数组的下一元素(而不是将p值简单地加1)。p+1意味着使p的原值(地址)加d个字节(a为一个数组元素所占的字节数)。
若上例中p的初值为&a[0],则:
·p+i和a+i就是a[i]的地址,或者说它们指向a数组的第i个元素。*(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。指向数组的指针变量也可以带下标,如p[i]与*(p+i)、a[i]等价
(2)指向数组元素的指针变量与数组名的关系
设有定义:int a[10],*p;
·a为常量、p为变量,赋值之后二者值相等
·取值时,a与p可以互换
·即a与p均可做算数运算、指针运算和下标运算
通过指针引用数组元素要注意以下问题
(1)可以通过改变指针变量的值指向不同的元素如果不用p变化的方法而用数组名a变化的方法则不可以
for(p=a;a<(p+10);a++)
printf("%d",*a);
因为数组名a代表数组首元素的地址,它是一个指针型常量,它的值在程序运行期间是固定不变的。既然a是常量,所以a++是无法实现的。
(2)要注意指针变量的当前值。
指针变量是一个变量,并不固定指向同一个地址,有可能随着程序的运行改变它的指向。
(3)指针容易引起越界
虽然定义数组时指定它包含10个元素,并用指针从上例可以看出,路变量p指向某一数组元素,但是实际上指针变量p可以指向数组以后的存储单元,结果不可预期,应避免出现这样的情况。
(4)指针变量可以做变址运算
指向数组元素的指针变量也可以带下标,如p[i]。
p[i]被处理成*(p+i),如果p是指向一个整型数组元素a[0],则p[i]代表a[i]。
但是必须清楚p的当前值:如果当前p指向a[3],则p[2]并不代表a[2],而是a[3+2],即a[5]
(5)利用指针引用数组元素,比较方便灵活,有不少技巧。
设有定义:int a[10],*p=a;分析下面几种情况
p++ //使p指向下一元素a[1]
*p;//得到下一个元素a[1]的值
*p++ /*由于++和*同优先级,结合方向自右而左,因此它等价于*(p++)。先引用p的值:实现*p的运算,然后再使p自增1*/
*(p++);//先取*p值,然后使p加1
*(++p);//先使p加1,再取*p
++(*p);/*表示p所指向的元素值加1,如果p=a,则相当于++a[0],若a[0]的值为3则a[0]的值为4。注意:是元素a[0]的值加1,而不是指针p的值加1*/
如果p当前指向a数组中第i个元素a[i],则:
*(p--);//相当于a[i--],先对p进行“*”运算,再使p自减
*(++p);//相当于a[++i],先使p自加,再进行“*”运算
*(--p)//相当于a[--i],先使p自减,再进行“*”运算