bug:這個詞的本意是蟲子的意思,在計算機剛出來的時候,裏面都是發光發熱電子管,經常會吸引一些bug,通常會引起電子管的短路,所以那時候如果人們發現計算機無法工作,第一件事就是打開計算機看看是不是又飛進去了bug。現在bug的含義是缺陷。發現並修正的bug叫做debug
錯誤(error):一般泛指結果和預期的不一樣,一類是設計實現錯誤,另一類是用戶操作引起的錯誤。我們希望能夠預見並有效處理的錯誤叫做Error-handle。
異常(exception):這個詞是在C++語言中被引入的,C語言中並不支持異常。
bug主要引發三種錯誤:
– | – | – |
---|---|---|
編譯錯誤 | 不符合C語言基本語法要求 | int i //沒有分號 |
運行錯誤 | 非法操作,如被零除或讀取非法內存 | int i=0;5/i;//被零除 |
邏輯錯誤 | 算法級或邏輯級錯誤 | if(i=0)//邏輯錯誤 |
越複雜的設計,隱含的Bug就越多,所以,不僅需要程序簡潔,而且還需要程序員細心縝密。但是無論程序員再怎麼仔細也不能完全的避免Bug。所以在開發時,應當儘早的的發現並改正bug,而在C語言中,有一個有趣的函數,叫做assert斷言函數,能夠發現一些潛在的bug。
assert的三個基本知識:
- assert函數的原型爲void assert(int expression)。它的作用就是判斷表達式的邏輯值,如果其值爲假(爲0),它會先向stderr打印一條出錯信息,然後調用abort來終止程序。如果爲真,則什麼也不做。
- 頻繁調用會極大地影響程序的性能,增加額外開銷。
- 可以在#include<assert.h>的語句前面插入#define NDEBUG來禁用或者關閉assert。見下面代碼塊。
#include<stdio.h>
#define NDEBUG
#include<assert.h>
assert是用於軟件開發與測試階段,一般用於以下幾個方面。
– |
---|
在函數開始處檢驗出入參數的合理性 |
在調用庫函數之前,檢查出入參數合理性 |
檢查變量的合理性和一致性 |
我們用下面這段程序進行舉例:
void fun(char *p){
assert(p!=NULL);
...
}
我們需要得到指針p的指向內容,一定會假設p不是一個空值,如果經過assert驗證後發現假設不成立,那麼調用fun函數部分一定存在問題。
常見的Debug工具
《C語言程序設計》給出了必須掌握的七種debug武器:
– |
---|
斷點 |
單步執行 |
監視窗 |
內存影響 |
函數調用棧 |
assert |
fprintf |
assert()的使用
assert()定義在頭文件assert.h中,它的原型爲
void assert(int expression)
assert()最常用的作用是檢查bug,當你不希望出現某些結果的時候可以利用這個函數進行檢驗,若是滿足判斷條件則不會有任何影響,若是不滿足條件,將會發出警告並停止程序運行。我們以下面的代碼爲例,要求輸入不能爲0,利用assert()進行校驗.
#include <stdio.h>
#include<assert.h>
int main()
{
int x;
scanf_s("%d", &x);//輸入x
assert(x != 0);//bug判斷
printf("%d\n", x);//輸出x
return 0;
}
對應輸入不爲0和不爲0的結果如下圖:
錯誤(error)
errno.h頭文件定義了多個用於定義和記錄運行時錯誤的宏。這些宏與perror()函數一起使用。perror()函數的頭文件是stdio.h。
errno是一個外部int型變量,許多庫函數在函數運行期間發生錯誤,都會講一個值賦給該變量。即初始化errno爲0,不同錯誤下會得到一個不同的值,即判斷是否有錯誤。
perror()是標準庫裏面另一個錯誤處理工具。調用時,會在stdeer上顯示一條消息,即根據errno的值給出錯誤描述。其調用格式爲:
void perror(const char *msg)
msg爲可選的,用戶定義的值。常用的error值及perror()錯誤描述見下表。
名稱 | 值 | 錯誤描述 |
---|---|---|
E2BIG | 1000 | 參數列表過長 |
EACCES | 5 | 沒有權限 |
EBADF | 6 | 文件描述無效 |
EDOM | 1002 | 數學參數超出值域 |
EEXIST | 80 | 文件已存在 |
EMFILE | 4 | 打開文件過多 |
ENOENT | 2 | 文件或路徑無效 |
ENOEXEC | 1001 | 執行格式錯誤 |
ENOMEM | 8 | 內存不夠 |
ENOPATH | 3 | 未找到路徑 |
ERANGE | 1003 | 結果超出範圍 |
我們給出一個例子,利用errno和perror來確定具體錯誤。sqrt()是開方運算,需要值大於0,如果我們給予其一個小於零的數,參數會在範圍外。其實現代碼爲:
#include <stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<math.h>
int main()
{
errno = 0;
sqrt(-1.0);
if (errno > 0) {
perror("sqrt");
}
return 0;
}
運算結果爲
我們經常進行文件操作,也可以用於文件權限操作是否實現的驗證。如下:
#include <stdio.h>
#include<errno.h>
#include<stdlib.h>
int main()
{
FILE *fp;
errno_t err;
err = fopen_s(&fp,"a", "r+");
if (fp == NULL) {
printf("%d\n", errno);
perror("a");
}
return 0;
}
運行結果:
參考Bradley Jones.Peter Aitken.Dean Miller的《21天學通C語言》
參考趙巖的《c語言點滴》