讀古今文學網 > C語言解惑 > 18.3 動態內存 >

18.3 動態內存

數組、指針和動態內存也是密切相關的。容易出現的錯誤仍然是邊界和初始化問題。

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指針。

所以,對指針進行遞增和遞減操作必須預防越界。在達到最後一個邊界時,要特別小心謹慎。釋放不用的內存時,必須保證指針指向所申請內存的首地址,否則就會出錯。在某些場合,為了保證釋放,甚至需要多申請部分內存區域。