C语言指针.docx

上传人:b****5 文档编号:7423517 上传时间:2023-01-23 格式:DOCX 页数:38 大小:43.86KB
下载 相关 举报
C语言指针.docx_第1页
第1页 / 共38页
C语言指针.docx_第2页
第2页 / 共38页
C语言指针.docx_第3页
第3页 / 共38页
C语言指针.docx_第4页
第4页 / 共38页
C语言指针.docx_第5页
第5页 / 共38页
点击查看更多>>
下载资源
资源描述

C语言指针.docx

《C语言指针.docx》由会员分享,可在线阅读,更多相关《C语言指针.docx(38页珍藏版)》请在冰豆网上搜索。

C语言指针.docx

C语言指针

第八章 指针

本章主要内容:

指针与地址                   1课时

指针变量的使用               1课时

指针与数组                   2课时

指针在函数中的使用           2课时

指针和字符串                 1课时

指针数组与多级指针           1课时

指向函数的指针               1课时

重点、难点:

指针概念、指针与数组、函数、字符串的关系

上机试验:

4课时

 

第一节指针与地址

内存储器为程序的存放和执行提供场所,而内存储器中存储单元的组织是按照线性结构来实现。

为了能够正确的访问这些单元,必须为每个单元进行编号,每个存储单元的编号称为地址。

程序运行之后,系统根据存储对象的类型在内存中分配相应的存储空间。

在TurboC系统中,为char类型对象分配1byte,为int类型对象分配2byte,为float类型对象分配4byte,为double类型对象分配8byte。

除了char类型之外,其它类型的对象都要分配多个字节。

由于为它们所分配的空间都是连续的,因此用连续分配的第一个单元的地址作为该对象的内存地址。

例8.1         int a;

      float b;

      char c;

      double d;

假设它们的空间分配如图8.1,他们的地址分别为:

3000,3002,3020,2040。

 

 

 

 

 

 

 

 

 

 

 

 

为了将存储单元的地址与变量之间的关系有一个清晰的认识,我们来看一个形象的例子:

假设办公楼2楼每个办公室都有一个门牌号,201为教务处处长办公室,202为教务处实验科办公室,203为教务处教学科办公室,……。

这些房间就相当于存储单元,而房间号相当于地址。

处长办公室,实验科办公室,教学科办公室,……就相当于变量名。

因而变量对应存储单元,而变量名是给存储单元取得符号名。

可见存储单元对应于一个地址,又对应一个变量名。

因此,对存储单元的访问,既可以通过变量名来实现(前面所介绍的一般是采用这种方式),又可以通过地址来访问。

这正是C语言有别于其他高级语言的特点之一。

而存储对象的地址又称为指向该对象的指针。

因此指针实际上是内存单元的地址码。

指针类型与其他数据类型一样,也有相应的变量——指针变量。

指针变量是用来存放其他对象的内存地址。

如:

    int  a;

    int*p=&a;

p为指针变量,用来存放变量a对应的内存单元的地址。

对于a的存单元,既可以用变量名a访问,也可以用指针p来实现访问:

*p

此时*p和a的值一样,即p中存放a的地址,而*p代表a。

注意:

●           int*p=&a;中“*”相当于一特殊类型说明符,说明p是指针型变量。

在定义p的同时,将系统为a所分配单元的地址赋给p(而不是给*p),在程序的其他地方只能用p=&a;

●           要区分指针与指针变量的概念:

指针是一地址码,为数据对象所分存储空间的首地址;而指针变量是一个数据对象,需要分配存储单元,其中存放的是地址码(即指针),并且只能存放地址,不能存放其他数据。

第二节指针变量的使用

一、指针变量的定义及赋值

指针变量与其他变量一样,都是变量,都需要通过定义来分配存储单元,然后才能使用。

当然也可以在定义的同时进行初始化。

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

       基类型* 指针变量名;

例如:

int*p;

表示定义一个指针变量p。

定义中的“*”表示该变量为一个指针变量而不是其他类型的变量,而前面的基类型,不是指针变量p所存放的类型,而是p的值所指向变量的数据类型,如上例中,p所存放的指针所指向的类型为int。

注意:

在定义中的“*”虽和前面所介绍的类型标识符有同样的功能,但只对一个变量名有效,如果要定义多个指针变量,需要多个“*”。

如:

 int*p1,p2,*p3;

表示定义了两个指针变量p1和p3,而p2为一整型变量。

在定义指针变量的同时,也可以初始化,其初值为基类型变量的地址。

如:

inta;

        int *pa=&a;

在定义指针变量pa的同时将整型变量a的地址赋值给它。

注意:

✍      要求在赋初值时,初值对应的变量必须先定义。

上例可以写成:

inta,*pa=&a; 

但不能写成:

int*pa=&a,a;

✍      要求初值对应变量的类型要与基类型相同。

即一个指针变量只能指向同一种类型的变量。

如:

    int a;

    float*p=&a;

  这种初始化是错误的。

✍      对指针变量初始化时,只能给他赋地址,而不能是其他类型的数据。

虽然地址是存储单元的地址码,是整数,但一般不能将整数作为初值。

因为程序中变量的地址只能由编译程序分配而不能人为指定。

如:

int *p=1000;/*错误赋值*/

       但可以将0赋值给指针变量,表示该指针未指向任何对象,为空指针。

如:

              float*fp=0;/*表示fp未指向任何float对象*/

与其他变量一样,指针变量除了初始化赋值之外,还可以在定义之后给它赋值,让它指向一个对象。

如:

       int a,*p;

       p=&a;

二、     指针的类型

对于不同的数据类型,其对象在内存中要占用不同数目的存储单元,并且其运行方法也完全不同。

如在TurboC下,int类型数据占2byte,float类型数据占4byte,他们的运算一种是整型运算,一种是浮点运算。

但对于指针变量来说,不管它指向的是什么类型的对象,他们的值都是地址,因而都占有相同数目的单元,在TurboC为2byte。

通过指针变量可以访问所指向的对象,进行操作与运算。

由于不同类型的数据占用的单元数不一样,进行的运算也不相同,因而也要对指向它们的指针变量进行区别,即指针变量也有类型。

指针变量的类型就是它所指向对象的类型。

如:

       doube d=3.5;

      double*dp=&d;/*dp为一个double类型的指针变量*/

一个指针变量要有相应类型,并且只能指向同一类型,如dp只能指向double类型的变量,而不能指向其他类型的变量。

注意:

要区分开指针变量的值和指针变量所指向变量的值。

如上例中,指针变量dp的值为变量d的地址,所指向变量的值为d的值,即为3.5。

三、     指针运算符

C语言提供了两种指针运算符:

取地址运算符(&)和引用目标运算符(*)。

1.取地址运算符(&)

取地址运算符是单目运算符,其结合性为从右→左,其功能是取变量的地址,使用格式为:

                  &左值

左值是具有内存单元的数据,如变量、数组元素及结构变量和联合中的数据成员都是左值,可以用“&”运算符取得它们的地址,因而&左值是一个指针表达式,可以用它来作为指针变量的值。

注意:

要区分开取地址运算符&与双目运算符&(按位与)。

2.引用目标运算符(*)

与取地址运算符“&”相对应的是引用目标运算符“*”。

该运算符也是单目运算符,结合性为从右→左,这种运算符只能作用在指针表达式的数据上。

其使用格式为:

                 *指针表达式

功能:

“*”运算符形成的表达式表示指针表达式所指向的对象,通过它可以完成对该对象的访问:

读和写。

例8.2       分析下面程序运行结果。

main( )

{

  int n,*pi;

  double d,*pd;

  pi=&n;

  pd=&d;

  printf("pleaseinputnandd:

");

  scanf("%d%lf",pi,pd);

  printf("n=%d,*pi=%d\n",n,*pi);

 printf("d=%f,*pd=%f\n", d,*pd);

}

程序运行结果为:

pleaseinputnandd:

23  76.8↙

n=23,*pi=23

d=76.800000,*pd=76.800000

通过scanf()函数中使用pi、pd实现对指向对象的赋值。

在printf()函数中通过*pi、*pd实现对变量n和d的输出(即变量的读操作)。

也可以在一个语句中同实现对指向对象的读和写操作,如:

      int  n=10,*pi=&n;

       (*p)++;

首先通过*p取出变量n的值作为表达式的值,然后再将该值加1之后,又赋值给变量n。

注意:

✍      在对指针变量使用“*”运算符时,要求指针变量已经指向了一个确定的对象。

也即对指针变量必须先赋值,再使用“*”运算符。

✍      

(*p)++和*p++之间的关系:

(*p)++表示对p所指向的对象加1修改,p仍指向原来的对象;而*p++表示先通过p去访问所指向的对象,再让p指向下一个对象,它等价于*(p++);

✍      几个等价关系:

假设有int a, *p=&a;则有如下等价关系:

① *p与a等价,等价于*&a;

② p与&a等价,等价于&*p;

例8.3       分析下面程序的运行结果:

main( )

{

  int a=10, b=20;

  int*pa,*pb,*p;

  pa=&a,pb=&b;

 printf("a=%d,b=%d,*pa=%d,*pb=%d\n",a,b,*pa,*pb);

  p=pa; pa=pb;pb=p;

  printf("a=%d,b=%d,*pa=%d,*pb=%d\n",a,b,*pa,*pb);

}

 运行结果为:

       a=10,b=20,*pa=10,*pb=20

       a=10,b=20,*pa=20,*pb=10

指针值的变化过程如图8.2。

 

 

 

 

 

 

 

 

 

 

 

 

 

说明:

图中的“

  ”表示指针值所指向的对象,“    ”表示存储单元之间值的拷贝,本章所有图形中的这两种箭头均表示相同意思。

例8.4       分析下面程序的运行结果。

    main( )

    {

       int a=10,b=20,t;

       int*pa,*pb;

       pa=&a,pb=&b;

printf("a=%d,b=%d,*pa=%d,*pb=%d\n", a, b,*pa,*pb);

       t=*pa,*pa=*pb,*pb=t;

printf("a=%d,b=%d,*pa=%d,*pb=%d\n", a, b,*pa,*pb);

}

运行结果为:

a=10,b=20,*pa=10,*pb=20

a=20,b=10,*pa=20,*pb=10

通过指针交换所指对象的过程如图8.3。

 

四、指针常量

同其他类型一样,有变量就有常量。

在C语言中,指针常量有:

数组名、函数名、字符串常量等。

数组名代表该数组在内存中分配的存储区的首地址,指向数组中的第一个元素,实际上就是指针类型的数据。

由于数组分配空间之后面,直到其生命期结束之前不会重新分配空间,因而数组名为指针常量。

函数代码存放在代码区,在程序的整个运行期,它不会改变,而函数名正好代表函数在代码区的首地址,因而函数名也是指针常量。

如:

 int a[100],*p;

   a就代表数组的首地址,指向第一个元素a[0],而p为一般的指针变量。

对于数组名a和一般的指针变量p它们的区别:

✍      a是指针常量,不能修改它的值,它总是代表数组的首地址;而p为变量,可以重新赋值,即让它指向不同的对象。

✍      a的意义是代表一个能存放100个int类型的存储区;而p在赋值之前,未指向任何有意义的单元。

第三节 指针与数组

在C语言中指针与数组的关系非常密切。

一、    指针与数组名之间的关系

前面已经介绍过数组名代表整个数组空间的首地址,指向的是数组中的第一个元素,而指针也是地址,因此数组名与指针都具有完全相同的数据类型,这使得他们的运算都具有通用性。

对数组元素的访问,是通过数组名利用下标运算符“[]”来实现访问,即:

数组名[下标]

可以转化为:

           地址[整数]

也即:

                 (指针表达式)[整数]

例8.5       分析下面程序的运行结果。

#define N 5

main( )

{

 int a[N],*p,i;

 for(i=0;i

   a[i]=i+10;

 p=a;

 for(i=0;i

   printf("%5d",a[i]);

 printf("\n");

 for(i=0;i

   printf("%5d",p[i]);

}

运行结果为:

10  11  12  13  14

10  11  12  13  14  

由上例可知:

a[i]等价于p[i],说明指针变量也可以用下标方式来访问对应单元中的值。

二、    定义指向数组元素的指针变量

定义指向数组元素的指针变量与指向简单变量的指针变量完全一样。

首先定义与数组元素类型一致的指针变量,然后再将数组元素的地址赋给指针变量,最后通过指针变量来引用数组元素。

例8.6       分析下面程序的运行结果。

  #define N 5

  main( )

  {

 inta[N],*p,i;

 for(i=0;i

  {

  p=&a[i];

  *p=i*2;

}

     for(i=0;i

      {

      p=&a[i];

        printf("%5d",*p);

}

}

运行结果为:

0   2   4   6   8

通过指针引用数组元素实现了对数组元素的读和写操作。

三、     指针的运算

指针是一种特殊的类型,它是以指针变量所持有的地址作为运算对象进行运算。

除了前面介绍的“*”和“&”可以使用,还有指针的加、减、比较等运算,而这些运算主要与数组操作相关。

1.指针与整数的加减运算

一个指针可以加上或减去一个整数,包括加1、减1。

前面介绍的数组元素访问方式为:

a[i],C语言中,实际上是先将a[i]转换成*(a+i)的形式然后再去求值。

这表明a+i是数组中第i+1个元素的地址,i为数组元素a[i]相对于a[0]的偏移量。

一个地址加减上一个整数其结果仍为地址,并且加减的单位不是以字节为单位,而是以指向的数据类型所占用的字节数为单位。

如int指针,以2byte为单位,double指针,以8byte为单位。

因此,p+n表示的实际地址为(假设p指针的基类型为type):

               p+n*sizeof(type)

例8.7       分析下面程序运行结果。

main( )

{

 int a[10]={1,2,3,4,5,6,7,8,9,10};

 int *p=a;

 printf("ais:

%X,a+3is:

%X\n",a,a+3);

 printf("pis:

%X,p+3is:

%X\n",p,p+3);

 printf("*ais:

%d,*(a+3)is:

%d\n",*a,*(a+3));

 printf("*pis:

%d,*(p+3)is:

%d\n",*p,*(p+3));

 printf("p[0]is:

%d,p[3]is:

%d\n",p[0],p[3]);

}

程序运行结果为:

ais:

FFC8,a+3is:

FFCE  (结果有可能不相同)

pis:

FFC8,p+3is:

FFCE

*ais:

1,*(a+3)is:

4

*pis:

1,*(p+3)is:

4

p[0]is:

1,p[3]is:

4

由运行结果可见:

a+i等价于p+i,同时还有:

*(a+i)等价于*(p+i),并且等价于a[i]和p[i]。

对p++、p--也是以基类型占用的存储单元进行,同时要修改p本身的值,让p指向后一个或前一个数据。

2. 指针之间的减运算

一般是同类型指针之间进行减运算。

对同类型的指针p、q,p-q表示p与q所指对象之间的元素个数。

例8.8       分析下面程序运行结果。

main( )

 {

 double d[5];

 double *p,*q;

 p=d, q=d+4;

 printf("p:

%X,q:

%X,q-p:

%d\n",p,q,q-p);

}

运行结果:

      p:

FFB0,q:

FFD0,q-p:

4

由结果可知:

两指针相减并不是两指针值之差,而是两指针所指对象之间的元素个数,所以实际值为指向type类型的两指针p和q之间的的差为:

      (q-p)/sizeof(type)

3.指针之间的关系运算

指针之间的关系运算主要用在同类型的指针之间,用来表示两指针所指对象之间的前后关系。

可以使用所有的关系运算符。

例8.9       将数组中的元素按位置颠倒。

分析:

可由两个指针p和q分别指向数组的两端,p和q分别向中间移动,同时交换它们所指向的值,停止交换的条件是:

p≥q。

       

(8-3.1)中的

符号表示交换,p=a,q=a+N-1对应赋值复句,下面部分对应一while型循环语句,循环头为:

while(p

程序如下:

    #define N 10

main( )

{

  int a[N]={1,2,3,4,5,6,7,8,9,10};

  int *p,*q,t;

  p=a,q=a+N-1;

  while(p

  {

      t=*p;*p++=*q;*q--=t;

}

  p=a,q=a+N;

  printf("theitemsof array are:

\n");

 while(p

    printf("%5d",*p++);

}

运行结果为:

     the itemsofarrayare:

       10   9   8   7   6   5   4   3   2   1

注意:

✍      (*p)++和*p++之间的关系:

(*p)++表示对p所指向的对象加1修改,p仍指向原来的对象;*p++根据运算符的优先级和结合性,先计算表达式p++的值,其值为p原来所指向变量的地址,然后p指向下一个单元,最后再“*”运算符作用于表达式p++的值,取出p原来所指向的对象的值。

它们的区别为:

(*p)++改变的是p所指向对象的值,整个表达式的值为p所指向对象值加1;而*p++改变的是指针p的值,整个表达式的值为p原来所指对象的值。

✍      *(p++)、*++p和*p++:

都要修改p的值;实际上*(p++)与*p++是等价的,都是先取出p所指对象的值,再修改p的值;而*++p先修改p的值,再取出p当前所指向的对象的值。

为了增加可读性,建议使用*(p++)和*(++p)。

实际上(8-3.1)是等价于:

对应一个for型循环语句:

    for(i=0;i

         t=a[i],a[i]=a[N-i-1],a[N-i-1]=t;

加上说明和输入输出则可以编出另一程序,这里程序略。

四、     指针与一维数组

假设有:

      int a[N], *p=&a[0], *q=&a[N-1];

用指针来引用一维数组的元素,主要有以下方法:

✍      偏移量法:

用指针加上偏移量,形如*(p+i)或p[i],表示数组a的第i个元素a[i];

✍      修改指针变量p或q的值,让他们依次指向数组中的每一个元素,一般形式为:

*p++,*q--。

✍      将数组名用作指针,利用偏移量法:

*(a+i)或a[i]。

例8.10     求字符串的长度。

分析:

若p存放字符串的首地址,q存放字符串结束字符的地址,则字符串的长度为:

                len=q-p;

程序如下:

main( )

{

  char c[10]="abcde";

  char*p=c, *q=p;

  int len;

  while(*q!

='\0')

      q++;

  len=q-p;

  printf("thelenof string is:

%d\n", len);

}

运行结果为:

     thelenof string is:

5

例8.11     将数组中的元素先从中间向前输出前半部分的元素,然后再从中间向后输出后半部分的元素。

分析:

首先要确定数组中间位置的元素下标,假设数组的长度为N,则可定中间元素的下标为(注意数组的下标从0开始):

,再分两步完成输出操作。

程序如下:

 #define N 10

 main( )

 {

   int a[N]={1,2,3,4,5,6,7,8,9,10};

   int*p,*q, m, i;

   m=(N-1)/2;

   p=a;/*利用指针偏移量方式*/

   i=m;

while(i>=0)

   {

     printf("%5d",*(p+i));

     i--;

   }

 i=m+1;

 while(i

  {

    printf("%5d",*(p+i));

    i++;

   }

 printf("\n");

}

程序运行结果为:

      5   4   3   2   1   6   7   8   9  10

思考:

该程序是通过指针偏移量来实现的,请问如何通过指针下标方式、指针增量方式、数组名偏移量方式实现?

注意:

在用指针变量引用数组元素时,一定要注意是否越界(即超出数组的范围)。

五、   指针与二维数组

通过指针来引用二维数组中的元素要比处理一维数组复杂得多。

前面已经介绍二维数组在内存中的存放方式:

由于内存单元的组织是以线性结构组织的,因此二维数组也是以线形方式组织,并且以行序为主序进行处理。

虽然他与一维数组的存放形式一样

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 冶金矿山地质

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1