碼字不易试伙,對你有幫助 點贊/轉(zhuǎn)發(fā)/關注 支持一下作者
微信搜公眾號:不會編程的程序圓
看更多干貨,獲取第一時間更新
推薦閱讀原文:
https://mp.weixin.qq.com/s/c5jot1YJcyeIniFkTVdNag
請看下面的程序四啰,它用來進行復制文件的操作雄可,你覺得它有問題嗎:
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[]) {
FILE* src_fp, * dest_fp;
int ch;
if (argc != 3) {
fprintf(stderr, "usage: fcopy source dest\n");
exit(EXIT_FAILURE);
}
if ((src_fp = fopen(argv[1], "rb") == NULL)) {
fprintf(stderr, "Can't open file %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if ((dest_fp = fopen(argv[2], "wb")) == NULL) {
fprintf(stderr, "Can't open file %s\n", argv[2]);
fclose(src_fp);
exit(EXIT_FAILURE);
}
while ((ch = getc(src_fp)) != EOF)
putc(ch, dest_fp);
fclose(src_fp);
fclose(dest_fp);
return 0;
}
程序可以正常編譯乌助,但是當我們在命令行中輸入正確的命令(命令格式:可執(zhí)行程序名 文件1 文件2)想要復制文件時呻率,會出現(xiàn)一個 assertion :
遇到這種問題我們不要慌張。我們先閱讀一下這個錯誤提示锄弱,看能否找到可以幫助自己 debug 的有用信息考蕾。
第一次看這個錯誤提示我是摸不著頭腦的。我仔細的檢查了自己的程序会宪,在確定“沒有問題”后肖卧,我又仔細的看了看這個錯誤提示。請大家注意 fgetc.cpp 這里掸鹅,這說明出錯的地方有可能是 fgetc 函數(shù)內(nèi)部塞帐。
注意接下來的一行:Expression: stream.valid();
到目前為止巍沙,我們知道可能是是程序執(zhí)行到 fgetc 函數(shù)內(nèi)部中 stream.valid() 這一行時產(chǎn)生了錯誤壁榕。那么問題到底是什么呢?
第一種辦法是 google 一下赎瞎,看看有沒有類似的問題解答;如果沒有颊咬,那么你就要靠自己了务甥。
我們思考一下,表達式 stream.valid() 什么情況下會出錯喳篇?有人可能要說了敞临,我連這個表達式的意思是什么都不知道,我怎么知道什么情況下會出錯呢麸澜?其實我也不知道挺尿,但這不影響我們思考出錯的可能性。
最直觀的一種可能就是 stream 是空指針時,對其進行成員訪問”喾現(xiàn)在我們需要做的就是找到自己程序中哪里可能傳入了空指針熟史。
這也很簡單,程序中我們只有一個函數(shù)和 fgetc 有關 —— getc 窄俏。但為什么我們用的是 getc 但是出錯的是 fgetc 呢蹂匹?因為 getc 函數(shù)的實現(xiàn)依賴于 fgetc 函數(shù)。
我們來看一下我們對 getc 的調(diào)用:
getc(src_fp)
這時你可能又要說了凹蜈,src_fp 我們不是做過空指針檢查嗎限寞,為什么還會出錯?不要著急著下結(jié)論仰坦,我們來看看這個檢查 src_fp 的語句:
if ((src_fp = fopen(argv[1], "rb") == NULL)) {
...
}
其實到這一步履植,明眼人都能看出問題的所在了。
當我確定了 src_fp 可能為空指針時悄晃,又看到了 if 語句中的 = 和 == 玫霎,我已經(jīng)明白錯誤所在了。讀過《C 陷阱和缺陷》的朋友都清楚传泊,這是一個經(jīng)典的由優(yōu)先級引發(fā)的錯誤:
因為 C 語言中鼠渺,賦值運算的優(yōu)先級往往是比大多數(shù)其他運算符都要低的(包括關系運算符 == 和 != ),if 語句的判斷會被編譯器理解為:
if ( (src_fp = (fopen(argv[1], "rb") == NULL) ) {
...
}
我們知道:fopen(argv[1], "rb") == NULL
的值的可能只有兩個:0 或 1眷细,不管我們將哪個賦值給 src_fp 拦盹,都會引發(fā)問題。
雖然最終的錯誤嚴謹?shù)恼f并不能說是空指針異常溪椎,但是我們發(fā)現(xiàn)了形參 stream 是有問題的普舆,也就說明我們傳入的實參是不正確的。
引發(fā)這個問題的原因是這樣的:在我寫這個 if 語句的判斷表達式時校读,我在 if 后打了一次左括號沼侣,VS 為我補了一個右括號(這樣就完整的錄入了()
)。然后在括號內(nèi)我先寫了 src_fp = fopen(argv[1], "rb")
歉秫。在要寫出判等語句前蛾洛,我認為應該為前面的賦值語句添加括號,所以我將光標移動到了 src_fp 前雁芙,打出了左括號(
轧膘,然后又我將光標移動到 fopen 函數(shù)的調(diào)用后,敲了一次左括號兔甘,很可惜谎碍,VS 就是這么坑爹,由于右邊已經(jīng)有了一個右括號(這是屬于整體的)洞焙,我白敲了一次右括號蟆淀,并沒有錄入這個右括號拯啦。后面在寫出判等代碼后(if ((src_fp = fopen(argv[1], "rb") == NULL)
),VS 其實是有報錯的熔任,但我自以為是的只是簡單的在后面添加了一個右括號而已褒链。從而造成了這個 bug 。
因為一個不經(jīng)大腦的右括號笋敞,我花了很久 debug 碱蒙,又“浪費”時間寫了一篇博客!這個故事告訴我們夯巷,下次在你添加右括號時赛惩,一定要停下了思考一下,以避免寫一篇“不必要”的博客趁餐。
雖然這個 bug 其實并不難找喷兼,但是我依然明白了,即使面對可能再難的 bug后雷,稍加思考季惯,它可能就會變成“紙老虎”。
或者臀突,讓你自己不要寫出這樣的 bug 勉抓。