C语言指针部分教学总结

来源:互联网 发布:淘宝店铺颜色代码 编辑:程序博客网 时间:2024/04/20 11:45

    C语言作为一门学科基础课程,几乎覆盖了高校的所有理工科专业。开设该课程的目的旨在培养学生运用计算机解决问题的基本能力,为后续的工作和学习打下基础。而在C语言的教学内容中,指针部分是必不可少的教学内容。考虑到同学们对指针理解和掌握存在一定的难度,因此,将指针的相关知识点贯穿在整个C语言的教学中,将有助于同学们更快的理解和掌握指针。

1、指针与地址

       C语言中,指针就是地址。如何让同学正确认识地址,是能否正确理解和掌握指针的关键。我们从课程的第一章开始,就一直学生反复地介绍地址,为后续知识点的学习奠定必要的基础。涉及的主要内容有以下三点:

1.1变量的地址

在第一章让同学们熟悉C语言的编程环境时,同学们容易出现如下的错误:

      

 int a; scanf(“%d”,&a); printf(“%d\n”, &a);

       在该程序中,输入的并不是变量a的值,而是一个比较大的数。此时,需要给学生解释,&a是变量a在内存中的存储地址,并不是a的值。

1.2 指针与一维数组

       在介绍一维数组中,教学时需要从变量的教学内容过渡过来。普通的变量是申请一个存储单元,而数组是申请一块连续的存储空间。对普通变量a而言,空间的存储地址为&a,而对数组而言,存储空间的首地址则是数组名。如果定义了一维数组inta[10]后,介绍一维数组时,需要讲清楚 a+9是第10个元素的存储地址,而不是地址的简单相加。访问第i个元素可以用a[i],也可以用*(a+i)。如果在数组中能讲清楚这一点,就可以在讲解返回指针的函数中进一步介绍。例如,要求用返回指针的函数编写程序,判断某一输入的元素是某数组中的第几个元素,可以编写如下的程序:

#include<stdio.h>int *find(intarr[10], int num){       int i,*p;       for(i=0,p=arr;i<10;i++,p++){              if(*p==num)                     return p;       }       return NULL;} int main(){       int a[10]={1,2,3,4,5,6,7,8,9,10};       int num;       int *findAddress;       scanf("%d",&num);       findAddress=find(a,num);        if(findAddress!=NULL)              printf("%d-th element. \n",findAddress-a+1);       else              printf("not found");        return 0;}


1.3 指针与函数

       函数是另一个涉及地址的知识点。当定义函数时,编译好的函数在存储空间中同样占据连续的存储空间,而该存储空间的首地址即为函数名称。在讲授函数时,可以通过下面的程序,简单地演示函数的这一特性。

#include<stdio.h>int max(int a, intb){       return a>b?a:b;} int main(){       printf("the address of the functionis %d.\n",max);       return 0;}


 

       如果介绍函数时,对这个知识点进行介绍,在指针部分介绍指向函数的指针就可以让同学们理解为什么可以把指针和函数关联起来。

2、指针与字符串

       字符串是一个特殊的数组,与一般的数组相比,有两个特性:(1)数组中元素的类型是字符;(2)字符串与一般的数组相比,更具有整体意义。当指针与字符串结合时,与数值型数组相比,字符串数组要注意以下几点:

       (1)定义指向字符的指针时,可以直接赋值,而指向数值型的数组不可以。例如,可以定义char *sp=”I am a student”,但定义int *a={1,2,3,4,5}就是错误的用法。这是因为C语言编译系统将”I am astudent”看成是字符串常量,在存储空间中存储这个常量,并把存储地址赋给指针变量sp。

       (2)注意字符串与字符指针的区别

       char *temp, str1[]=”I am a student”;

       strcpy(temp, str1); 是错误的

 

3、指针与二维数组

       许多高校在介绍指针部分时,通常略去指针在二维数组中的应用,原因在于无法恰当地让同学们理解二维数组中行地址和列地址的关系。本部分从一维数组的角度出发,对二维数组中的指针进行详细介绍。

       讲授二维数组时,应从一维数组的教学内容进行过渡,因为二维数组是一个一维数组,一维数组的每一个元素都是一个一维数组。所以说,二维数组是一维数组的一维数组。以数组int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}为例,它实际是由三个元素a[0]、a[1]和a[2]组成的,因此如果a指向二维数组存储空间的首地址,a+1则指向下一个元素的存储地址,即a[1]的存储地址;同样,a+2指向a[2]的存储地址。因此,a、a+1、a+2就是教材上所讲的行地址。而对a[0]、a[1]和a[2]三个元素而言,每一个元素都是一个一维数组。即,

              a[0]={1,2,3,4}

              a[1]={5,6,7,8}

              a[2]={9,10,11,12}

在这三个元素中,a[0]、a[1]和a[2]是三个一维数组的数组名,因此,第一个一维数组的四个元素分别为a[0][0]、a[0][1]、a[0][2]和a[0][3]。而对这个一维数组来讲,数组名a[0]即为首元素的存储地址,a[0]+1、a[0]+2、a[0]+3则是后三个元素的存储地址。因此a[0]、a[1]、a[2]和a[3]即为教材上所讲的列地址。基于这点考虑,我们不建议介绍行地址和列地址时,采用*(*(a+i)+j)的方式进行,可以采用如下的方式进行:

       for(i=0;i<3;i++){

              for(j=0;j<4;j++)

                     printf(“%d”,*(a[i]+j));

              printf(“\n”);

       }

       如果利用指针来处理,要正确理解指针与地址的关系。如果定义指针int *ps,而用ps=a去让指针与二维数组关联,会产生类型不相符的错误。此时,要注意引导学生理解指针指向的内容。对指针ps而言,它指向的是一个整型变量,而对二维数组而言,指向的内容却不是一个整型变量,而是一个整型数组。因此,如果要采用指针去处理,应采用指向一维数组的指针int (*ps)[4],此时令ps=a后,可以采用如下的方式输出整个数组的元素:

       for(i=0;i<3;i++){

              for(j=0;j<4;j++)

                     printf(“%d”,*(*(p+i)+j));

              printf(“\n”);

       }

这里采用的*(p+i),只是为了使形式上统一,可以将其替换为p[i],以便于学生加深理解。

4、指针与指针数组

       在指针部分,指针数组也是一个非常重要的概念,需要同学们理解。稍有不慎,就容易和指向一维数组的指针混淆。因此,要及时地对相关定义进行总结,定义指向一维数组的指针定义为int (*p)[4],代表指向含有4个元素的一维数组的指针,而int*p[4],则由于[]运算的优先级高于*运算,因此,从本质上讲它是一个数组,数组中的元素是指向整数的指针变量。下面我们通过对字符串排序加深对指向一维数组的指针和指针数组加深理解。

       从主函数输入十个不等长的字符串,编写函数,对这些串进行排序,在主调函数中输出排好序的串。

       对于本程序,可以通过以下三种不同的方式加以实现。通过这三个小函数,将有助于同学们进一步理解指针、指向数组的指针以及指针数组。

 

#include <stdio.h>  #include <string.h>  #define N 5 void sort1(char (*str)[10]){    int i,j;      char temp[10];        for(i=0;i<N;i++) {          for(j=i+1;j<N;j++){              if(strcmp(str[i],str[j])>0){                  strcpy(temp,str[i]);                  strcpy(str[i],str[j]);                  strcpy(str[j],temp);              }          }      }  }   void sort2(char *ps[N]){     int i,j;     char *temp;          for(i=0;i<N;i++){         for(j=i+1;j<N;j++){             if(strcmp(ps[i],ps[j])>0){                temp=ps[i];ps[i]=ps[j];ps[j]=temp;            }         } } } void sort3(char *ps[N]){     int i,j;     char temp[10];         for(i=0;i<N;i++){        for(j=i+1;j<N;j++){             if(strcmp(ps[i],ps[j])>0){                 strcpy(temp,ps[i]);                strcpy(ps[i],ps[j]);                 strcpy(ps[j],temp);             }         }     }} int main() {  char str[N][10];  char *ps[N];     int i;            for(i=0;i<N;i++)  {        gets(str[i]);         ps[i]=str[i];}         sort1(str); //基于指向一维数组的指针实现    sort2(ps); //基于指针数组实现    sort3(ps); //基于指针数组实现        return 0;  }


在上述例子中,如果通过键盘输入的五个字符串分别为str[0]=”Jinan”、str[1]=”Qingdao”、str[2]=”Yantai”、str[3]=”Weifang”、str[4]=”Weihai”,则函数sort1、sort2和sort3函数排序原理如下。

(1)sort1函数。是通过指向一维数组的指针实现的,因此,传递的参数str[0]、str[1]、…、str[N]从本质上讲,都是指向字符的指针,即字符串。因此,sort1函数的本质是对字符串进行排序,排序的结果如下:

 

图1 sort1函数的排序结果

 

       (2)sort2函数。通过指针数组进行排序,传递的参数中,ps[0]、ps[1]、…、ps[N]是相应的字符串的名称,也就是相应的地址。由于sort2函数是对指针数组中的地址进行了交换,因此str[0]、str[1]、…、str[N]中对应的字符串没有发生改变,即通过输入str无法得到排序后的结果,而通过指针数组ps[0]、ps[1]、…ps[N]可以得到正确的结果,排序结果如下:

 

图2 sort2函数的排序结果

      

       (3)与sort2函数不同的是,sort3函数是对地址对应的字符串进行了交换,因此,ps[i]与str[i]的对应关系没有改变,而str[i]中存储的字符串发生了改变。所以,sort3函数的排序结果如下图所示:

 

图3 sort3的排序结果

 

       如果授课过程中可以从这个角度引导同学们加深对指针的理解,将有助于同学们正确的认识并理解指针、数组以及指针数组。

5、结束语

       指针部分是C语言教学过程中的重点和难点,本文通过实例教学,介绍了如何在授课过程中介绍了指针与地址的关系,如何讲授二维数组中的指针,指向一维数组的指针以及指针数组等内容。

       以上是笔者对指针部分在授课过程中的经验总结,由于笔者水平有限,期待更精彩的教学案例设计,以期促进C语言的教学。

参考文献

[1] 谭浩强.C程序设计[M].北京:清华大学出版社,2002.

[2] 宋丽华,雷鹏,张小峰.C语言程序设计[M].北京:清华大学出版社,2014.

[3] 赵忠孝,杨亚蕾.对C 语言指针教学问题的探究[J].计算机教育,19:72-74,2009.

[4] 于福生,刘玉铭,王加银.C 语言课程中指针内容体系设置的改革尝试[J].计算机教育,4:22-24,2013.

[5] 李俊萩,赵家刚,张晴晖.C 语言指针教学中的知识点分析与总结[J].计算机教育,8:55-61,2011.

0 0
原创粉丝点击