字元指針數組的使用--替換人名-題目分析

字元指針數組的使用--替換人名-題目分析

來自專欄 Sun Flowers C World

上篇介紹了指針數組的使用,本篇將使用字元指針數據來完成一個具體題目的實現。如果對字元指針數組還不是很了解的同學,可以先去上篇學習一下。本節中的題目是一個程序填空題,只能修改//start和//end中間的部分。因此,需要先看懂原始代碼,然後在原始代碼的基礎上完成題目。廢話不多說,先上題目。

題目內容:

本題在一批人名中,找到要修改的人名,將其替換為新輸入的人名。

本題首先輸入n個人名(帶空格),然後輸入待查找的人名和待替換人名,將找到的人名換為新人名。

輸入示例:

4

zhou zhiruo

zhang wuji

zhao min

li xunhuan

zhao min

lin shiyin

3

sun xiaohong

a fei

yang yan

tian ji

shangguan fei

輸出示例:

修改後:

zhou zhiruo

zhang wuji

lin shiyin

li xunhuan

no person!

要求在原有代碼之上完成。

原有代碼:

#include <stdio.h>#include <stdlib.h>#include <string.h>//start//請在這裡完成modi_name函數的定義//endint main(){ int n,i,len; char old_name[20],new_name[20],temp_str[20],*pc,*pname[20]; while(scanf("%d",&n)!=-1) { getchar();getchar(); for(i=0;i<n;i++) //for循環用於讀入n個人名 { gets(temp_str); len=strlen(temp_str); pname[i]=(char *)malloc(len+1); strcpy(pname[i],temp_str); } gets(old_name); //輸入待修改人名 gets(new_name); //輸入新人名 pc=modi_name(n,pname,old_name); if(pc==NULL) printf("no person!
"); else { printf("修改後:
"); for(i=0;i<n;i++) { if(pname[i]==pc) //找到要修改的人名的指針 pname[i] = new_name; //替換為新人名的地址 printf("%s
" ,pname[i]); } } } return 0;}

一、程序分析:

(1)變數定義:n,i,len分別表示人的個數、循環變數和用來存儲接收字元串的長度,old_name,new_name,temp_str數組分別存儲需要修改的人名,新的人名,pc為一個char類型指針,pname數組是一個指針數組,用來存儲n個同學姓名字元串的地址;

(2) while(scanf("%d",&n)!=-1)用於控制多組數據處理,循環內的兩個getchar();語句用於處理n後面的回車。銳格系統中回車是由
兩個字元組成,而大家平時練習的Codeblocks中回車是
,一個字元,因此在調試程序時注意注釋掉其中一個。

(3) for(i=0;i<n;i++)用於接收n個人名,對於每個人名處理的過程是先將人名存入temp_str臨時數組,再調用strlen函數獲取temp_str中字元串的長度存入len,然後利用malloc函數動態申請len+1個位元組的空間,並把新申請內存的地址存入指針數組pname[i],最後把temp_str中的字元串拷貝至pname[i]指向的內存,也就是新申請的內存中。

(4) gets(old_name); 和gets(new_name); 用於接收待修改人名和新人名。輸入示例1執行完到此後內存中數據存儲示意圖如下圖所示:

程序運行到gets(new_name);行時的內存示意圖

(5)pc=modi_name(n,pname,old_name);這裡的modi_name就是需要大家自行編寫的函數,在不清楚這個函數的功能時,先分析函數的原型,由於pc是char *類型,因此函數的返回值類型一定是char *,函數接收的實參n,pname,old_name的數據類型分別為int,char *[],char *,因此,這裡可以得出函數modi_name的原型是:char * modi_name(int n,char *pname[], char * old_name),具體它的功能需要根據下文來進行分析,這裡僅知道它接收指針數組pname地址,其中元素的個數n,待修改的人名,返回一個char *,也就是一個字元串的地址,但是功能具體實現什麼還需要往下分析。mo_name函數的返回值存入pc變數中。

(6)if(pc==NULL) printf("no person!
");這裡說明如果pc為NULL,則列印no person!也就是沒有找到要修改的人名;else部分for語句中i從0到n,其中遍歷了指針數組pname,如果pname[i]的值等於pc,就把pname[i]的值修改為new_name,這樣pname[i]元素的值就修改為new_name,也就是它指向了存儲新人名的數組,然後列印pname[i]指向的字元串。由此,可以看出pc中存儲的就是待修改的人名字元串的首地址。那麼,可以分析得出函數mode_name實現的功能是遍歷pname指針數組n個指針元素,判斷各個元素指向的字元串是否與old_name待修改的人名相同,如果相同就返回它指向的字元串的首地址,相對於上圖總的例子而言,它返回的是「zhaomin」的地址,也就是pname[i]的值(此時,i=2,pname[i]=2800)。如果遍歷完pname數組後仍然沒有找到待修改的人名,則返回NULL。

二、程序實現

根據上面的程序分析,modi_name函數的定義應該是:

char * modi_name(int n,char *pname[], char * old_name)//函數的原型也可以是:char * modi_name(int n,char ** pname, char * old_name){ int i; for(i=0;i<n;i++) { if(strcmp(pname[i],old_name)==0) return pname[i]; //strcmp函數用於判斷兩個字元串是否相等,接收兩個字元串的首地址。 //因此,這裡要用pname[i],不能用pname+i,因為pname+i是pname數組中第i個元素的地址,不是第i個人名字元串的首地址 //pname和pname+i的值為一個char *變數的地址,因此類型為char **類型。 } return NULL;}

程序執行完後內存示意圖如下圖所示:pname[2]不再指向2800(zhao min),轉而指向new_name數組(1032,紅色顯示的指向箭頭)。

三、反思

從上面的程序分析和運行後內存的情況可以看出,該程序至少存在以下錯誤:

(1)程序中只能更新一個元素,當存在n個人名中有重複,存在多個待修改的人名時只能修改一個,不能修改所有的;因為更新的代碼「 if(pname[i]==pc) pname[i] = new_name;」語句中判斷待修改的人名時使用的是判斷與pc是否相同,不管在modi_name函數中返回的是哪一個字元串的首地址,但是它只能返回一個。因此,不能更新所有的。如果想更新所有的待修改人名,這裡應該用if(strcmp(pname[i],old_name)==0)來進行判斷;

(2)更新的方法不好,因為new_name是一個數組,其內存在棧上由系統自動分配,不是pname指針數組中其他元素都存儲的動態內存地址,因此,用pname[i] = new_name;語句更新帶來至少帶來兩個bug,一是由於沒有釋放舊人名所佔據的內存(2800開頭的內存),造成內存泄露,二是程序最後在用free釋放內存時會帶來系統錯誤。而本程序中由於main函數的最後沒有釋放動態分配內存,所以沒有錯誤沒有顯示出來。因此,應該對更新的方法進行修改,為新人名的存儲更新內存。具體函數實現見稍後代碼。

(3)main函數的最後沒有釋放動態分配內存。建議編寫一個函數釋放pname指針數組中各元素指向的內存。

因此,本程序中人名的更新可以由兩個函數完成,一個search函數用於查找在pname指針數組指向的字元串中是否存在待修改人名,如果存在返回1,不存在返回0,不需要返回地址,因為返回的地址也沒有什麼具體的作用,一個update函數用戶完成在pname指針數組中查找待修改人名old_name,如果存在就釋放舊人名佔據的空間,申請新的空間,並將new_name中存儲的字元串拷貝至動態內存空間中。內存的釋放可以編寫一個freeCharStarArray函數。具體代碼如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>//startint search(int n,char * pname[],char * old_name){ int i; for(i=0;i<n;i++) if(strcmp(pname[i],old_name)==0) return 1; return 0;}int modi_name(int n,char *pname[], char * old_name,char * new_name){ int i,len,num=0; for(i=0;i<n;i++) { if(strcmp(pname[i],old_name)==0) { len=strlen(new_name); pname[i]=(char *)realloc(pname[i],len+1);//也可以用下面兩行代碼 //free(pname[i]); //pname[i]=(char *)malloc(len+1); if(pname[i]==NULL) exit(1); strcpy(pname[i],new_name); num++; } } return num;}void freeCharStarArray(int n,char *pname[]){ int i; for(i=0;i<n;i++) { free(pname[i]); pname[i]=NULL; }}//endint main(){ int n,i,len; char old_name[20],new_name[20],temp_str[20],*pname[20]; while(scanf("%d",&n)!=-1) { getchar();getchar(); for(i=0;i<n;i++) //for循環用於讀入n個人名 { gets(temp_str); len=strlen(temp_str); pname[i]=(char *)malloc(len+1); strcpy(pname[i],temp_str); } gets(old_name); //輸入待修改人名 gets(new_name); //輸入新人名 if(search(n,pname,old_name)==0) printf("no person!
"); else { modi_name(n,pname,old_name,new_name); printf("修改後:
"); for(i=0;i<n;i++) { printf("%s
" ,pname[i]); } } freeCharStarArray(n,pname);//釋放內存 } return 0;}

推薦閱讀:

科普向:編程語言發展
Python的起源是什麼?
偽·從零開始學Python - 1.1 認識Python
突然感知自己最近學習能力增強了!
C語言基礎:指針做參數

TAG:編程語言 | 編程 | 指針編程 |