C語言指標部分教學總結

iamzxf發表於2014-12-25

    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.

相關文章