數組、指針和動態內存也是密切相關的。容易出現的錯誤仍然是邊界和初始化問題。
18.3.1 非數組的指針
【例18.8】下面程序將數組t和s中的內容賦給指針變量p,但輸出結果並沒有包括s的全部內容。找出錯誤之處並改正之。
#include <stdio.h> #include <string.h> int main () { int i=0 ,j=0 ; char t="abcdefghij" ,s="klmnopqrstuvwxyz" ,*p ; p=t ; i=strlen (t ); while (( p[i+j] = s[j] ) !='\0' ) j++ ; printf ("%s\n" ,p ); return 0 ; }
原因是用數組t初始化指針的想法是想利用超出t的存儲空間來存儲s,這是危險的做法。越界之後,並不能保證有連續的有效存儲空間用以存儲字符串s。
可以另外定義一個大於s和t總長度的字符數組。例如
char st[30] ; p=st ;
然後使用如下兩個循環完成賦值:
while (( p[i] = t[i] ) !='\0' ) i++ ; while (( p[i+j] = s[j] ) !='\0' ) j++ ; p[i+j]='\0' ;
一般採用申請動態內存的方法,即為指針變量申請足夠的存儲空間。
p= (char* )malloc ( strlen (t )+strlen (t )+1 )
strlen函數計算的是實際字符串長度,所以要增加一個結束位。實際使用時,需要判別申請是否成功。這塊內存雖然是非數組的指針,但卻可以像數組那樣使用下標。程序中演示了兩種反序輸出的方法,特別是演示下標為負值的使用方法,以便更好地理解動態內存的特點及指針的靈活使用方法。
// 完整的程序 #include <stdio.h> #include <string.h> #include <stdlib.h> int main () { int i=0 ,j=0 ; char t="abcdefghij" ,s="klmnopqrstuvwxyz" ,*p ; if ( (p= (char* )malloc ( strlen (t )+strlen (t )+8 )) == NULL ) { printf ( " 內存分配錯誤!\n" ); exit (1 ); } while (( p[i] = t[i] ) !='\0' ) i++ ; while (( p[i+j] = s[j] ) !='\0' ) j++ ; p[i+j]='\0' ; printf ("%s\n" ,p ); for (i=25 ; i>-1 ; i-- ) printf ("%c" ,p[i] ); printf ("\n" ); p=p+25 ; for (i=0 ; i>-26 ; i-- ) printf ("%c" ,p[i] ); printf ("\n" ); p=p-25 ; free (p ); return 0 ; }
程序輸出結果如下:
abcdefghijklmnopqrstuvwxyz zyxwvutsrqponmlkjihgfedcba zyxwvutsrqponmlkjihgfedcba
釋放內存,必須保證指針指向申請的動態內存的開始位置,否則會出錯。所以程序中執行「p=p-25;」。申請內存時多申請了6個,是為了保證free可靠執行。
數值數組的使用方法與此類似,不再贅述。
18.3.2 NULL指針
在語句
if ( (p= (char* )malloc ( strlen (t ) + strlen (t ) + 8 )) == NULL )
中使用了空指針。空指針的表示為:
p=NULL ;
有時在賦值或比較運算的情況下會使用NULL指針,但在其他情況不能使用NULL指針。因為NULL指針並不指向任何對象,而且空指針也不是空字符串,所以對空指針p而言,使用如下兩個語句會得到什麼結果呢?
printf ("%s\n" , p ); printf (p );
為了代碼的文檔化,常採取如下定義:
#define NULL 0
由此可見,p的行為沒有定義,這兩條語句在不同的機器上可能有不同的效果。
在禁止讀取內存0地址的機器上,語句
printf ("%d\n" , *p );
將會執行失敗。在允許的機器上,則會以十進制方式輸出內存位置0中存放的字符內容。
要注意的是,空指針並不是空字符串。無論使用0還是NULL,效果都是相同的。當將0賦值給一個指針變量時,絕對不能企圖使用該指針所指向的內存中存儲的內容。
有些C語言實現對內存位置0只允許讀,不允許寫。在這種情況下,NULL指針指向的也是垃圾信息,所以也不能錯用NULL指針。
所以,對指針進行遞增和遞減操作必須預防越界。在達到最後一個邊界時,要特別小心謹慎。釋放不用的內存時,必須保證指針指向所申請內存的首地址,否則就會出錯。在某些場合,為了保證釋放,甚至需要多申請部分內存區域。