數組、指針和動態內存分配是編程的必備技術,一定要掌握它們的正確使用方法。縱觀一下,需要深入理解並正確使用的有如下幾點:
(1)越界。一定要注意防止在使用時產生越界行為。
(2)限制。限製程序脫離控制以避免對系統造成傷害。
這兩點的意思是:不但自己用好,還不要去干擾系統,否則可能造成系統崩潰。
對字符串數組要特別注意的是賦值問題。字符串和指針本身都可以有空格,關鍵是賦值。不能使用scanf語句讀入數據,因為它不允許讀入空格。
要注意中文的特點,避免不必要的錯誤。
【例18.15】分析下面程序的輸出結果。
#include <stdio.h> int main ( ) { int i ; char s=\" 數 學\" ,*cp=s ; printf (\"%sn\" ,cp ); printf (\"%cn\" ,*cp ); for (i=0 ;i<8 ;i++ ) printf (\"%c\" ,* (cp+i )); printf (\"n\" ); return 0 ; }
這是對中文字符編碼理解不清引起的錯誤。一個中文字符相當於兩個英文字符,如果對中文輸出一個字符,就不是所要的輸出信息。for循環語句超過邊界,會輸出無用的字符。字符串有效的字符是6個。如果要輸出單個中文「數」字,下面兩種方式是等效的。
printf (\"%c%cn\" ,cp[0] ,cp[1] ); printf (\"%c%cn\" ,*cp ,* (cp+1 ));
修改後的程序如下:
#include <stdio.h> int main ( ) { int i ; char s=\" 數 學\" ,*cp=s ; printf (\"%sn\" ,cp ); printf (\"%c%cn\" ,cp[0] ,cp[1] ); for (i=0 ;i<6 ;i++ ) printf (\"%c\" ,* (cp+i )); printf (\"n\" ); return 0 ; }
程序運行輸出結果如下:
數 學 數 數 學
【例18.16】在下面的程序中,要求輸入「See you tomorrow!」,輸出也是同樣內容。程序能達到要求嗎?
#include <stdio.h> void main ( ) { char st[18] ; scanf (\"%s\" ,st ); printf (st ); printf (\"n\" ); }
不是的。輸出是「See」。scanf語句的讀入是以空格結束的,所以它只取第1個連續字符作為輸入。要想得到正確的結果,必須放棄scanf函數。例如使用gets函數。下面是正確的程序。
#include <stdio.h> void main ( ) { char st[18] ; printf (\" 輸入:\" ); gets (st ); printf (\" 輸出:%sn\" ,st ); }
運行示範如下:
輸入: See you tomorrow ! 輸出:See you tomorrow !
【例18.17】下面的程序能實現將輸入字符串的內容複製到指針變量t中嗎?
#include <stdio.h> #include <malloc.h> void main ( ) { int i=0 ; char a[100] , *t ; gets (a ); while ( (t[i]= a[i] )!=\'\0\' ) i++ ; printf ( \"%sn\" , t ); }
不能。因為指針變量沒有初始化。可以為它申請一塊內存來完成初始化,而且要判斷是否申請成功,使用完之後也要及時釋放。
// 完整的程序 #include <stdio.h> #include <malloc.h> #include <stdlib.h> int main ( ) { int i=0 ; char a[100] , *t ; t= (char* )malloc (100*sizeof (char )); if (t== NULL ) { printf ( \" 內存分配錯誤!n\" ); exit (1 ); } gets (a ); while ( (t[i]= a[i] )!=\'\0\' ) i++ ; printf ( \"%sn\" , t ); free (t ); return 0 ; }
【例18.18】假設給定班級各科考試平均成績的原始資料如下:
數學:75
物理:80
外語:83
政治:85
體育:86
人數:30
要求統計出全班學期總平均成績以及得分最低的科目和該科目的成績。
要求的輸出結果如下:
原始信息如下:
數學:75
物理:80
外語:83
政治:85
體育:86
人數:30
平均成績:0
最低分數科目的成績:0
最低分數的科目:
全班各科平均成績如下:
數學:75
物理:80
外語:83
政治:85
體育:86
統計結果如下:
人數:30
平均成績:81
最低分數科目的成績:75
最低分數的科目:數學
這裡不使用數組而使用動態內存,這種非數組的內存區域可以起到數組的作用。為此設計一個字符串指針數組,為每一個字符串指針數組申請一塊內存空間,存放所提供的字符串。例如科目的名稱,可以聲明指針數組「char*pcn[6];」,使用
for (i=0 ;i<6 ;i++ ) pcn[i]= (char * )malloc (4*sizeof (char ));
循環語句分別為每個指針申請內存,用以存儲「數學」和「物理」等名稱。
(1)使用整型指針申請內存來存放數學、物理、外語、政治、體育、人數、平均成績、最低分數科目的成績。
(2)使用字符串指針數組pcn申請內存來存放數學、物理、外語、政治、體育、最低分數的科目。
(3)使用字符串指針數組pcm申請內存來存放人數、平均成績、最低分數科目的成績、最低分數的科目。
// 全部使用動態內存的程序 #include<stdio.h> #include<stdlib.h> #include<string.h> int main ( ) { int i ,*p ,min=100 ,sum=0 ; int a[8]={75 ,80 ,83 ,85 ,86 ,30} ; char *pcn[6] , *pcm[4] ; // 為科目名稱申請內存 for (i=0 ;i<6 ;i++ ) { pcn[i]= (char * )malloc (4*sizeof (char )); if (pcn[i]==NULL ) { printf (\" 內存分配錯誤\" ); exit (1 ); } } // 為科目賦值 strcpy (pcn[0] ,\" 數學\" ); strcpy (pcn[1] ,\" 物理\" ); strcpy (pcn[2] ,\" 外語\" ); strcpy (pcn[3] ,\" 政治\" ); strcpy (pcn[4] ,\" 體育\" ); // 為欄目申請內存 for (i=0 ;i<4 ;i++ ) { pcm[i]= (char * )malloc (20 ); if (pcm[i]==NULL ) { printf (\" 內存分配錯誤\" ); exit (1 ); } } // 為欄目賦值 strcpy (pcm[0] ,\" 人數\" ); strcpy (pcm[1] ,\" 平均成績\" ); strcpy (pcm[2] ,\" 最低分數科目的成績\" ); strcpy (pcm[3] ,\" 最低分數的科目\" ); // 為分數分配內存地址 p= (int * )malloc (8*sizeof (int )); if (p==NULL ) { printf (\" 內存分配錯誤\" ); exit (1 ); } // 存儲初始值 p[0]=75 ; p[1]=80 ; p[2]=83 ; p[3]=85 ; p[4]=86 ; p[5]=30 ; p[6]=0 ; p[7]=0 ; // 輸出原始信息 printf (\" 原始信息如下:n\" ); for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,* (p+i )); for (i=0 ;i<3 ;i++ ){ printf (\"%s :%dn\" ,pcm[i] ,p[i+5] ); } printf (pcm[3] ); printf (\"n\" ); // 計算最小值和平均值 for (i=0 ;i<5 ;i++ ) { sum=sum+p[i] ; if (min>p[i] ) min=p[i] ; } p[6]=sum/5 ; p[7]=min ; // 填寫最低分數的科目名稱 for (i=0 ;i<5 ;i++ ) if (min==* (p+i )) { strcpy (pcn[5] ,pcn[i] ); break ; } printf (\"n 全班各科平均成績如下:n\" ); for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,p[i] ); // 輸出信息 printf (\"n 統計結果如下:n\" ); for (i=0 ;i<3 ;i++ ) { printf (\"%s :%dn\" ,pcm[i] ,* (p+i+5 )); } printf (\"%s :%sn\" ,pcm[3] ,pcn[5] ); free (p ); return 0 ; }
【例18.19】使用二級字符串指針和指向二維字符串的一維字符指針實現上述程序。
(1)使用整型數組a存放數學、物理、外語、政治、體育、人數、平均成績、最低分數科目的成績。
(2)使用字符串數組name存放數學、物理、外語、政治、體育、最低分數的科目。
(3)使用字符串數組MeanLow存放人數、平均成績、最低分數科目的成績、最低分數的科目。
(4)為MeanLow設計一個二級指針:
char **pcm ;
讓它指向MeanLow,就可很方便地使用指針pcm來實現存取。
pcm=MeanLow ; for (i=0 ;i<3 ;i++ ) printf (\"%s :%dn\" ,pcm[i] ,* (p+i+5 ));
(5)定義一個指向一維數組的指針變量來實現對數組name的存取。語句
char (*pcn )[5] ;
定義指針變量pcn,因為指向name的一維數組,所以不能使用name[0],而應使用「p=name;」語句初始化。
pcn=name ; for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,* (p+i )); // 完整的程序 #include<stdio.h> #include<stdlib.h> int main ( ) { int i ,sum=0 ,min=100 ,*p ; char **pcm ; char (*pcn )[5] ; int a[8]={75 ,80 ,83 ,85 ,86 ,30} ; char *MeanLow[4]={\" 人數\" ,\" 平均成績\" ,\" 最低分數科目的成績\" , \" 最低分數的科目\"} ; char name[5][5]={\" 數學\" ,\" 物理\" ,\" 外語\" ,\" 政治\" ,\" 體育\"} ; for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,name[i] ,a[i] ); for (i=0 ;i<3 ;i++ ) printf (\"%s :%dn\" ,MeanLow[i] ,a[i+5] ); printf (\"%s :n\" ,MeanLow[i] ); // 計算 p=a ; for (i=0 ;i<5 ;i++ ) { sum=sum+p[i] ; if (min>p[i] ) min=p[i] ; } p[6]=sum/5 ; p[7]=min ; pcm=MeanLow ; pcn=name ; // 輸出信息 printf (\"n 全班各科平均成績如下:n\" ); for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,* (p+i )); printf (\"n 統計結果如下:n\" ); for (i=0 ;i<3 ;i++ ) printf (\"%s :%dn\" ,pcm[i] ,* (p+i+5 )); return 0 ; }