1、釋放動態記憶體時遇到“user breakpoint called from code at 0x********”
這個bug多在調用free函數來釋放malloc的記憶體時出現,前先曾多次遇到,但一直未找出問題原因。
在網上查了一下,有些寫得挺複雜,涉及到了作業系統推維護的內容,現在還不有接觸
到這一塊,所以沒有細看。其他有說到重複釋放動態記憶體,修改動態記憶體指標或者破壞了系統的動態記憶體結束標誌都會導致這個bug。後來查看自己的代碼,發現
重複釋放的問題倒是沒有,卻在操作記憶體時有一處頻繁操作超出動態申請記憶體之外的單元,故導致這個問題的出現。
問題總結:在動態記憶體釋放時出現,多由於動態記憶體使用不當
1、重複釋放動態記憶體,調用free後應立即將指標賦NULL,最好寫個宏將free和指標賦NULL包含進去,以免遺漏;
2、讀寫操作超出了動態申請記憶體邊界(讀應該不會發生問題,但也要避免,除非你真正明白自己在幹什麼)
2、C中的字串異常
嚴格地說,C中是沒有字串變數的,一般採用字元數組或字元指標的方式才實現字串的操作,二者在使用中有諸多類似,但並不是完全等價的,注意下面兩句的區別:
char a[] = "Hello world";//存放在棧中,可以修改a資料群組的任意值
char *s = "Hello world";//指標s存放在棧中,字串常量在程式碼片段,不可修改
給s[i]賦值將導致錯誤
更正:經測試,在VC 6.0環境下
指標指向常量字串時,在Debug模式下會出現程式崩潰,而在Release模式下則不會,且字串被儲存在資料區段,而不是程式碼片段。
另外在TC 2.0下也不會出現該問題
但還是盡量使用char p[]="**"的形式來定義字串
因為在Release下,編譯器會進行一些最佳化,如下
char *p1 = "ABC";
char *p2 = "ABC";
*p1 = 'D';
puts(p1);
puts(p2);
p1和p2指向的是同一塊記憶體位址,修改p1指向的內容會導致p2的內容改變,這是不符合期望的。而在Debug模式下雖不進行如此最佳化,但會使程式會崩潰。
3、條件判斷時一定要學會利用短路特性來防止異常,如
while(j >=0 && a[j] >0) j--;
if(d != 0 && n/d == 0)
if(p == NULL || *p == '\0')/*no string*/
例中3種情況漏掉前一個條件或將兩個判斷條件調換了次序都會導致記憶體溢出。
4、關於混亂的類型擴充問題
因為不確定編譯器採用哪種保護規則(無符號保護或是值保護),應盡量避免在同一個運算式內混用有符號和無符號的變數。任何時候,總可以用顯式的類型轉換來明確無誤地表達所希望的轉換的地方或方式。
5、類型轉換被當成左值
在C語言中,類型轉換操作只能產生一個右值,不能被賦值或進行自增(減)運算。
如
char *p;
((int *)p)++;
不能完成預想中的將p增加一個int的長度,而應該如下:
p= (char *)((int *)p + 1);
或p += sizeof(int);
6、變長參數函數調用中,會進行預設類型轉換嗎?
下面這段代碼的輸出是什嗎?
#include <stdio.h>
int main()
{
__int64 n;
int b;
n = 1;
b = 2;
printf("%d,%d\n",n,b);
return 0;
}
和預料不同的是,它輸出:1,0
這是因為在可變參數函數調用中,一般編譯器是不進行預設的類型轉換的,當函數根據格式字串從棧中提取參數時就會由於變數長度不一致而產生異常的輸出。在上例中,printf函數首先根據%d從棧中提取一個int將其輸出(n的低32位內容),然後將指標下移一個int的長度,再取(此時取的正是n的高32位內容)一個int輸出。
但應注意:像printf這樣的函數有“預設參數提升”規則,即char和short總是被提升為int,float總是被提升為double。所以printf只能用“%f”輸出double,而scanf必須用"%lf”來接受一個double型資料的輸入。
7、返回局部變數的地址(或引用:C++)
不要對局部變數進行地址返回和引用返回,即使你設想相應的地址空間十之八九還保持原值。局部變數被放在程式的堆棧空間,函數返回後,其棧空間即被釋放,相當於失去了保護,相應的棧空間可能馬上會被破壞掉,比如說進行另一函數調用。
即使立即將資料轉存也不是百分之百的安全(VC 6.0下測試發現:debug模式下執行行checkesp會存壞一部分,release下可能局部變數直接被最佳化掉了,根本沒有直正存入記憶體),所以,最保險的辦法就是永遠不要這樣幹!!