日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

这5个bug我不信你没有写过

發布時間:2023/12/20 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 这5个bug我不信你没有写过 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,我是寫代碼的籃球。

計算機專業的小伙伴,在學校期間一定學過 C 語言。它是眾多高級語言的鼻祖,深入學習這門語言會對計算機原理、操作系統、內存管理等等底層相關的知識會有更深入的了解,所以我在直播的時候,多次強調大家一定要好好學習這門語言。

但是,即使是最有經驗的程序員也會寫出各種各樣的 Bug。本文就盤點一下學習或使用 C 語言過程中,非常容易出現的 5 個 Bug,以及如何規避這些 Bug。

這篇文章主要面向初學者,老鳥可以忽略哈(其實不少老鳥依然還會犯這些低級錯誤哦)~

1. 變量未初始化

當程序啟動時,系統會給它自動分配一塊內存,程序可以用它來存儲數據。所以如果你在定義一個變量時,在未初始化的情況下,它的值有可能是任意的。

但這也不是絕對的,有些環境就會在程序啟動時自動將內存「清零」,因此每個變量默認值都是零。考慮到可移植性,最好要將變量進行初始化,這是一名合格軟件工程師應該養成的好習慣。

我們來看下下面這個使用幾個變量和兩個數組的示例程序:

#include?<stdio.h> #include?<stdlib.h>int?main() {int?i,?j,?k;int?numbers[5];int?*array;puts("These?variables?are?not?initialized:");printf("??i?=?%d\n",?i);printf("??j?=?%d\n",?j);printf("??k?=?%d\n",?k);puts("This?array?is?not?initialized:");for?(i?=?0;?i?<?5;?i++)?{printf("??numbers[%d]?=?%d\n",?i,?numbers[i]);}puts("malloc?an?array?...");array?=?malloc(sizeof(int)?*?5);if?(array)?{puts("This?malloc'ed?array?is?not?initialized:");for?(i?=?0;?i?<?5;?i++)?{printf("??array[%d]?=?%d\n",?i,?array[i]);}free(array);}/*?done?*/puts("Ok");return?0; }

這段程序沒有對變量進行初始化,所以變量的值有可能是隨機的,不一定是零。在我的電腦上它的運行結果如下 :

These?variables?are?not?initialized:i?=?0j?=?0k?=?32766 This?array?is?not?initialized:numbers[0]?=?0numbers[1]?=?0numbers[2]?=?4199024numbers[3]?=?0numbers[4]?=?0 malloc?an?array?... This?malloc'ed?array?is?not?initialized:array[0]?=?0array[1]?=?0array[2]?=?0array[3]?=?0array[4]?=?0 Ok

從結果可以看出,i 和 j 的值剛好是 0,但 k 值為 32766。在 numbers 數組中,大多數元素也恰好是零,除了第三個(4199024)。

在不同的操作系統上編譯這段相同的程序,運行的結果有可能又是不一樣的。所以千萬不要覺得你的結果就是正確唯一的,一定要考慮可移植性。

例如,這是在 FreeDOS 上運行的相同程序的結果:

These?variables?are?not?initialized:i?=?0j?=?1074k?=?3120 This?array?is?not?initialized:numbers[0]?=?3106numbers[1]?=?1224numbers[2]?=?784numbers[3]?=?2926numbers[4]?=?1224 malloc?an?array?... This?malloc'ed?array?is?not?initialized:array[0]?=?3136array[1]?=?3136array[2]?=?14499array[3]?=?-5886array[4]?=?219 Ok

可以看出來,運行的結果跟上面幾乎是天差地別。所以,對變量進行初始化將為你省去很多不必要的麻煩,也便于將來的調試。

2. 數組越界

在計算機世界里,都是從 0 開始計數,但總有人有意無意忘記這點。比如一個數組長度為 10 ,想要獲取最后一個元素的值,總有人用 array[10] ……

別問,問就是我寫過……

新手朋友犯這種低級錯誤特別多。我們來看下數組越界會發生什么。

#include?<stdio.h> #include?<stdlib.h>int?main() {int?i;int?numbers[5];int?*array;/*?test?1?*/puts("This?array?has?five?elements?(0?to?4)");/*?initalize?the?array?*/for?(i?=?0;?i?<?5;?i++)?{numbers[i]?=?i;}/*?oops,?this?goes?beyond?the?array?bounds:?*/for?(i?=?0;?i?<?10;?i++)?{printf("??numbers[%d]?=?%d\n",?i,?numbers[i]);}/*?test?2?*/puts("malloc?an?array?...");array?=?malloc(sizeof(int)?*?5);if?(array)?{puts("This?malloc'ed?array?also?has?five?elements?(0?to?4)");/*?initalize?the?array?*/for?(i?=?0;?i?<?5;?i++)?{array[i]?=?i;}/*?oops,?this?goes?beyond?the?array?bounds:?*/for?(i?=?0;?i?<?10;?i++)?{printf("??array[%d]?=?%d\n",?i,?array[i]);}free(array);}/*?done?*/puts("Ok");return?0; }

請注意,程序初始化了數組 numbers 所有元素的值(0~4),但是越界讀取了第 0~9 元素的值。可以看出來,前五個值是正確的,但之后鬼都不知道這些值會是什么:

This?array?has?five?elements?(0?to?4)numbers[0]?=?0numbers[1]?=?1numbers[2]?=?2numbers[3]?=?3numbers[4]?=?4numbers[5]?=?0numbers[6]?=?4198512numbers[7]?=?0numbers[8]?=?1326609712numbers[9]?=?32764 malloc?an?array?... This?malloc'ed?array?also?has?five?elements?(0?to?4)array[0]?=?0array[1]?=?1array[2]?=?2array[3]?=?3array[4]?=?4array[5]?=?0array[6]?=?133441array[7]?=?0array[8]?=?0array[9]?=?0 Ok

所以大家在寫代碼過程中,一定要知道數組的邊界。像這種數據讀取的還好,如果一旦對這些內存進行寫操作,直接就 core dump !

3. 字符串溢出

在 C 編程語言中,字符串是一組 char 值,也可以將其視為數組。因此,你也需要避免超出字符串的范圍。如果超出,則稱為字符串溢出。

為了測試字符串溢出,一種簡單方法是使用 gets 函數讀取數據。gets 函數非常危險,因為它不知道接收它的字符串中可以存儲多少數據,只會天真地從用戶那里讀取數據。

如果用戶輸入字符串比較短那很好,但如果用戶輸入的值超過接收字符串的長度,則可能是災難性的。

下面我們來演示一下這個現象:

#include?<stdio.h> #include?<string.h>int?main() {char?name[10];???????????????????????/*?Such?as?"Beijing"?*/int?var1?=?1,?var2?=?2;/*?show?initial?values?*/printf("var1?=?%d;?var2?=?%d\n",?var1,?var2);/*?this?is?bad?..?please?don't?use?gets?*/puts("Where?do?you?live?");gets(name);/*?show?ending?values?*/printf("<%s>?is?length?%d\n",?name,?strlen(name));printf("var1?=?%d;?var2?=?%d\n",?var1,?var2);/*?done?*/puts("Ok");return?0; }

在這段代碼里,接收數組的長度為 10 ,所以當輸入數據長度小于 10 的話,程序運行就沒問題。

例如,輸入城市 Beijing ,長度為 7 :

var1?=?1;?var2?=?2 Where?do?you?live? Beijing <Beijing>?is?length?7 var1?=?1;?var2?=?2 Ok

威爾士小鎮 Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch 是世界上名字最長的城市,這個字符串有 58 個字符,遠遠超出了 name 變量中可保留的 10 個字符。

如果輸入這個字符串,其結果是程序運行內存的其它位置,比如 var1和var2 ,都有可能被波及:

var1?=?1;?var2?=?2 Where?do?you?live? Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch <Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch>?is?length?58 var1?=?2036821625;?var2?=?2003266668 Ok Segmentation?fault?(core?dumped)

在中止之前,程序使用長字符串覆蓋內存的其他部分。請注意,var1 和 var2 不再是它們的起始值 1 和 2 。

所以我們需要使用更安全的方法來讀取用戶數據。例如,getline 函數就是一個不錯的選擇,它將分配足夠大的內存來存儲用戶輸入,因此用戶不會因輸入太長字符串而意外溢出。

4. 內存重復釋放

良好的 C 編程規則之一是,如果分配了內存,就一定要將其釋放。

我們可以使用 malloc 函數為數組和字符串申請內存,系統將開辟一塊內存并返回一個指向該內存起始地址的指針。內存使用完畢后,我們一定要記得使用 free 函數釋放內存,然后系統將該內存標記為未使用。

但是,這個過程中,你只能調用 free 函數一次。如果你第二次調用 free 函數,將導致意外行為,而且可能會破壞你的程序。

下面我們舉個簡單的例子:

#include?<stdio.h> #include?<stdlib.h>int?main() {int?*array;puts("malloc?an?array?...");array?=?malloc(sizeof(int)?*?5);if?(array)?{puts("malloc?succeeded");puts("Free?the?array...");free(array);}puts("Free?the?array...");free(array);puts("Ok"); }

運行此程序會導致第二次調用 free 函數時出現 core dump 錯誤:

malloc?an?array?... malloc?succeeded Free?the?array... Free?the?array... free():?double?free?detected?in?tcache?2 Aborted?(core?dumped)

那么怎么避免多次調用 free 函數呢?一個最簡單的方法就是將 malloc 和 free 語句放在一個函數里。

如果你將 malloc 放在一個函數里,而將 free 放在另一個函數里,那么,在使用的過程中,如果邏輯設計不恰當,都有可能出現 free 被調用多次的情況。

5. 使用無效的文件指針

文件是操作系統里一種非常常見的數據存儲方式。例如,您可以將程序的配置信息存儲在名為 config.dat 文件里,程序運行時,就可以調用這個文件,讀取配置信息。

因此,從文件中讀取數據的能力對所有程序員都很重要。但是,如果你要讀取的文件不存在怎么辦?

在 C 語言中,要讀取文件一般是先使用 fopen 函數打開文件,然后該函數返回指向文件的流指針。

如果您要讀取的文件不存在或您的程序無法讀取,則 fopen 函數將返回 NULL 。在這種情況下,我們仍然對其進行操作,會發生什么情況?我們一起來看下:

#include?<stdio.h>int?main() {FILE?*pfile;int?ch;puts("Open?the?FILE.TXT?file?...");pfile?=?fopen("FILE.TXT",?"r");/*?you?should?check?if?the?file?pointer?is?valid,?but?we?skipped?that?*/puts("Now?display?the?contents?of?FILE.TXT?...");while?((ch?=?fgetc(pfile))?!=?EOF)?{printf("<%c>",?ch);}fclose(pfile);/*?done?*/puts("Ok");return?0; }

當你運行這個程序時,如果 FILE.TXT 這個文件不存在,那么 pfile 將返回 NULL。在這種情況下我們還對 pfile 進行寫操作的話,會立刻導致 core dump :

Open?the?FILE.TXT?file?... Now?display?the?contents?of?FILE.TXT?... Segmentation?fault?(core?dumped)

所以,我們要始終檢查文件指針是否有效。例如,在調用 fopen 函數打開文件后,使用 if (pfile != NULL) 以確保指針是可以使用的。

小結

再有經驗的程序員都有可能犯錯誤,所以寫代碼的時候我們要嚴謹再嚴謹。但是,如果你養成一些良好的習慣,并添加一些額外的代碼來檢查這五種類型的錯誤,則可以避免嚴重的 C 編程錯誤。

上面介紹的 5 種常見錯誤,你都寫過哪些 Bug 呢?留言跟大家交流哦,看看誰是 Bug 王!


推薦閱讀:

專輯|Linux文章匯總

專輯|程序人生

專輯|C語言

我的知識小密圈

關注公眾號,后臺回復「1024」獲取學習資料網盤鏈接。

歡迎點贊,關注,轉發,在看,您的每一次鼓勵,我都將銘記于心~

總結

以上是生活随笔為你收集整理的这5个bug我不信你没有写过的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。