成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C 語言常見的錯誤及解決辦法之避坑筆記

開發 后端
但是很多學弟學妹們之前都沒有學習過太多相關的具體知識,因此經常犯一些非?;A的低級錯誤,所以今天提前為學弟學妹們解決一些無基礎初學 C 語言常見的錯誤,希望能夠幫助學弟學妹們少走一些彎路。

但是很多學弟學妹們之前都沒有學習過太多相關的具體知識,因此經常犯一些非?;A的低級錯誤,所以今天提前為學弟學妹們解決一些無基礎初學 C 語言常見的錯誤,希望能夠幫助學弟學妹們少走一些彎路。

一、指針沒有指向一塊合法的內存

定義了指針變量,但是沒有為指針分配內存,即指針沒有指向一塊合法的內存。淺顯的例子就不舉了,這里舉幾個比較隱蔽的例子。

1、結構體成員指針未初始化

struct student
{
   char *name;
   int score;
}stu,*pstu;

int main()
{
   strcpy(stu.name,"Jimy");
   stu.score = 99;
   return 0;
}




很多初學者犯了這個錯誤還不知道是怎么回事。這里定義了結構體變量stu,但是他沒想到這個結構體內部char *name 這成員在定義結構體變量stu 時,只是給name 這個指針變量本身分配了4 個字節。name 指針并沒有指向一個合法的地址,這時候其內部存的只是一些亂碼。所以在調用strcpy 函數時,會將字符串"Jimy"往亂碼所指的內存上拷貝,而這塊內存name 指針根本就無權訪問,導致出錯。解決的辦法是為name 指針malloc 一塊空間。

同樣,也有人犯如下錯誤:

int main()
{
   pstu = (struct student*)malloc(sizeof(struct student));
   strcpy(pstu->name,"Jimy");
   pstu->score = 99;
   free(pstu);
   return 0;
}






為指針變量pstu 分配了內存,但是同樣沒有給name 指針分配內存。錯誤與上面第一種情況一樣,解決的辦法也一樣。這里用了一個malloc 給人一種錯覺,以為也給name 指針分配了內存。

2、沒有為結構體指針分配足夠的內存

int main()
{
   pstu = (struct student*)malloc(sizeof(struct student*));
   strcpy(pstu->name,"Jimy");
   pstu->score = 99;
   free(pstu);
   return 0;
}






為pstu 分配內存的時候,分配的內存大小不合適。這里把sizeof(struct student)誤寫為sizeof(struct student*)。當然name 指針同樣沒有被分配內存。解決辦法同上。

3、函數的入口校驗

不管什么時候,我們使用指針之前一定要確保指針是有效的。

assert 是一個宏,而不是函數,包含在assert.h 頭文件中。如果其后面括號里的值為假,則程序終止運行,并提示出錯;如果后面括號里的值為真,則繼續運行后面的代碼。這個宏只在Debug 版本上起作用,而在Release 版本被編譯器完全優化掉,這樣就不會影響代碼的性能。有人也許會問,既然在Release 版本被編譯器完全優化掉,那Release 版本是不是就完全沒有這個參數入口校驗了呢?這樣的話那不就跟不使用它效果一樣嗎?是的,使用assert 宏的地方在Release 版本里面確實沒有了這些校驗。但是我們要知道,assert 宏只是幫助我們調試代碼用的,它的一切作用就是讓我們盡可能的在調試函數的時候把錯誤排除掉,而不是等到Release 之后。它本身并沒有除錯功能。再有一點就是,參數出現錯誤并非本函數有問題,而是調用者傳過來的實參有問題。assert 宏可以幫助我們定位錯誤,而不是排除錯誤。

二、為指針分配的內存太小?

為指針分配了內存,但是內存大小不夠,導致出現越界錯誤。

char *p1 = “abcdefg”;
char *p2 = (char *)malloc(sizeof(char)*strlen(p1));
strcpy(p2,p1);





p1 是字符串常量,其長度為7 個字符,但其所占內存大小為8 個byte。初學者往往忘了字符串常量的結束標志“\0”。這樣的話將導致p1 字符串中最后一個空字符“\0”沒有被拷貝到p2 中。解決的辦法是加上這個字符串結束標志符:

char *p2 = (char *)malloc(sizeof(char)*strlen(p1)+1*sizeof(char));





這里需要注意的是,只有字符串常量才有結束標志符。比如下面這種寫法就沒有結束標志符了:

char a[7] = {‘a’,’b’,’c’,’d’,’e’,’f’,’g’};





另外,不要因為char 類型大小為1 個byte 就省略sizof(char)這種寫法。這樣只會使你的代碼可移植性下降。

三、內存分配成功,但并未初始化

犯這個錯誤往往是由于沒有初始化的概念或者是以為內存分配好之后其值自然為0。未初始化指針變量也許看起來不那么嚴重,但是它確確實實是個非常嚴重的問題,而且往往出現這種錯誤很難找到原因。

也許這種嚴重的問題并不多見,但是也絕不能掉以輕心。所以在定義一個變量時,第一件事就是初始化。你可以把它初始化為一個有效的值,比如:

我在檢查了他的代碼之后,沒有發現什么問題,于是單步調試。在觀察這個結構體變量的內存時,發現有幾個成員的值為亂碼。就是其中某一個亂碼惹得禍!因為系統會按照這個結構體中的某些特定成員的值去字庫中尋找匹配的字體,當這些值與字庫中某種字體的某些項匹配時,就調用這種字體。

但是很不幸,正是因為這幾個亂碼,導致沒有找到相匹配的字體!因為系統并無法區分什么數據是亂碼,什么數據是有效的數據。只要有數據,系統就理所當然的認為它是有效的。

也許這種嚴重的問題并不多見,但是也絕不能掉以輕心。所以在定義一個變量時,第一件事就是初始化。你可以把它初始化為一個有效的值,比如:

int i = 10;
char *p = (char *)malloc(sizeof(char));





但是往往這個時候我們還不確定這個變量的初值,這樣的話可以初始化為0 或NULL。

int i = 0;
char *p = NULL;





如果定義的是數組的話,可以這樣初始化:

int a[10] = {0};





或者用memset 函數來初始化為0:

memset(a,0,sizeof(a));





memset 函數有三個參數,第一個是要被設置的內存起始地址;第二個參數是要被設置的值;第三個參數是要被設置的內存大小,單位為byte。這里并不想過多的討論memset 函數的用法,如果想了解更多,請參考相關資料。

至于指針變量如果未被初始化,會導致if 語句或assert 宏校驗失敗。這一點,上面已有分析。

四、內存越界

內存分配成功,且已經初始化,但是操作越過了內存的邊界。這種錯誤經常是由于操作數組或指針時出現“多1”或“少1”。比如:

int a[10] = {0};
for (i=0; i<=10; i++)
{
   a[i] = i;
}






所以,for 循環的循環變量一定要使用半開半閉的區間,而且如果不是特殊情況,循環變量盡量從0 開始。

五、內存泄漏

內存泄漏幾乎是很難避免的,不管是老手還是新手,都存在這個問題。甚至包括windows,Linux 這類軟件,都或多或少有內存泄漏。

也許對于一般的應用軟件來說,這個問題似乎不是那么突出,重啟一下也不會造成太大損失。但如果你開發的是嵌入式系統軟件呢?比如汽車制動系統,心臟起搏器等對安全要求非常高的系統。你總不能讓心臟起搏器重啟吧,人家閻王老爺是非常好客的。

會產生泄漏的內存就是堆上的內存(這里不討論資源或句柄等泄漏情況),也就是說由malloc 系列函數或new 操作符分配的內存。如果用完之后沒有及時free 或delete,這塊內存就無法釋放,直到整個程序終止。

1、告老還鄉求良田

怎么去理解這個內存分配和釋放過程呢?先看下面這段對話:

萬歲爺:愛卿,你為朕立下了汗馬功勞,想要何賞賜?。?/span>

某功臣:萬歲,黃金白銀,臣視之如糞土。臣年歲已老,欲告老還鄉。臣乞良田千畝以蔭后世,別無他求。

萬歲爺:愛卿,你勞苦功高,卻僅要如此小賞,朕今天就如你所愿。戶部劉侍郎,查看湖廣一帶是否還有千畝上等良田未曾封賞。

劉侍郎:長沙尚有五萬余畝上等良田未曾封賞。

萬歲爺:在長沙撥良田千畝封賞愛卿。愛卿,良田千畝,你欲何用???

某功臣:謝萬歲。長沙一帶,適合種水稻,臣想用來種水稻。種水稻需要把田分為一畝一塊,方便耕種。

。。。。

2、如何使用malloc 函數

不要莫名其妙,其實上面這段小小的對話,就是malloc 的使用過程。malloc 是一個函數,專門用來從堆上分配內存。使用malloc 函數需要幾個要求:

內存分配給誰?這里是把良田分配給某功臣。

分配多大內存?這里是分配一千畝。

是否還有足夠內存分配?這里是還有足夠良田分配。

內存的將用來存儲什么格式的數據,即內存用來做什么?

這里是用來種水稻,需要把田分成一畝一塊。分配好的內存在哪里?這里是在長沙。

上面就是使用malloc 函數成功分配一塊內存的過程。但是,每次你都能分配成功嗎?

不一定。上面的對話,皇帝讓戶部侍郎查詢是否還有足夠的良田未被分配出去。使用malloc函數同樣要注意這點:如果所申請的內存塊大于目前堆上剩余內存塊(整塊),則內存分配會失敗,函數返回NULL。注意這里說的“堆上剩余內存塊”不是所有剩余內存塊之和,因為malloc 函數申請的是連續的一塊內存。

3、用malloc 函數申請0 字節內存

另外還有一個問題:用malloc 函數申請0 字節內存會返回NULL 指針嗎?

可以測試一下,也可以去查找關于malloc 函數的說明文檔。申請0 字節內存,函數并不返回NULL,而是返回一個正常的內存地址。但是你卻無法使用這塊大小為0 的內存。這好尺子上的某個刻度,刻度本身并沒有長度,只有某兩個刻度一起才能量出長度。

4、內存釋放

既然有分配,那就必須有釋放。不然的話,有限的內存總會用光,而沒有釋放的內存卻在空閑。與malloc 對應的就是free 函數了。free 函數只有一個參數,就是所要釋放的內存塊的首地址。比如上例: free(p); free 函數看上去挺狠的,但它到底作了什么呢?

六、內存已經被釋放了,但是繼續通過指針來使用

這里一般有三種情況:

第一種:就是上面所說的,free(p)之后,繼續通過p 指針來訪問內存。解決的辦法就是給p 置NULL。

第二種:函數返回棧內存。這是初學者最容易犯的錯誤。比如在函數內部定義了一個數組,卻用return 語句返回指向該數組的指針。解決的辦法就是弄明白棧上變量的生命周期。

第三種:內存使用太復雜,弄不清到底哪塊內存被釋放,哪塊沒有被釋放。解決的辦法是重新設計程序,改善對象之間的調用關系。

上面詳細討論了常見的六種錯誤及解決對策,希望讀者仔細研讀,盡量使自己對每種錯誤發生的原因及預防手段爛熟于胸。一定要多練,多調試代碼,同時多總結經驗。

責任編輯:龐桂玉 來源: Linux公社
相關推薦

2024-04-03 12:30:00

C++開發

2011-08-02 13:46:43

iPhone開發 iPhone SDK

2012-05-30 16:19:11

2010-01-27 12:06:00

UPS常見故障

2020-10-19 09:34:04

C語言內存錯誤編程語言

2023-11-01 15:32:58

2012-05-23 09:28:14

Titanium錯誤應對辦法

2025-04-07 11:20:00

KubernetesPodPod容器

2024-10-07 08:26:05

編程Python異常處理

2010-09-07 09:50:35

DIVCSS

2019-10-17 09:58:01

深度學習編程人工智能

2011-04-21 16:42:40

傳真機

2021-10-19 05:54:58

C語言編程

2009-12-03 17:36:02

PHP Date()出

2016-02-29 09:57:53

開發管理錯誤辦法

2011-04-29 17:11:17

激光打印機常見故障解決方法

2011-10-28 10:56:24

jQTouchjQueryiPhone

2013-05-23 09:22:10

網吧網絡交換機故障

2020-06-02 09:57:51

Python數據技術

2010-08-17 11:35:46

DIV CSS
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91.色| 色久在线| av天空 | 黑人精品xxx一区一二区 | 亚洲综合大片69999 | 日韩一区二区在线播放 | 久久男人| 日韩久久精品 | 免费av毛片 | 国产激情福利 | 国产福利在线视频 | 国产精品久久久久久久久久妞妞 | www国产成人免费观看视频 | 国产农村妇女毛片精品久久麻豆 | 久久久天天 | 黄色毛片在线观看 | 日本不卡一区二区三区在线观看 | 精品亚洲一区二区三区四区五区 | 视频二区 | 日韩国产欧美视频 | 国产福利91精品一区二区三区 | av官网在线 | 成人在线视 | av日韩高清 | 国产精品久久久久久久午夜片 | 精品乱码一区二区三四区视频 | 国产成人高清 | 久久国产区 | 国产精品久久久久久久久久尿 | 久久精彩视频 | 国产一区精品在线 | av影音资源 | 超碰97av| 男女羞羞视频大全 | 欧美黄色小视频 | 成人免费视频在线观看 | 久久综合狠狠综合久久综合88 | 久久福利 | 日韩精品一区二区三区视频播放 | 国产高清精品在线 | 国产福利在线 |