c語言不能除錯怎麼辦(c語言為什麼除錯不了)
相信大家在嵌入式C開發中,或多或少都會遇到段錯誤(segmentation fault )。相比匯流排錯誤,段錯誤是一種更為常見的錯誤。
那麼,段錯誤是怎麼產生的呢?簡單來說,段錯誤是因為訪問不可訪問的記憶體產生的。
下面是一些典型的段錯誤產生的原因:
- 訪問不存在的記憶體地址
- 訪問只讀的記憶體地址
- 棧溢位
- 記憶體越界
- ……
段錯誤例項
1、例項1:訪問不存在的記憶體地址
#includeint main(int argc, char **argv) { printf("==================segmentation fault test==================/n"); int *p = NULL; *p = 1234; return 0; }
2、例項2:訪問只讀的記憶體地址#include
int main(int argc, char **argv)
{
printf("==================segmentation fault test1==================/n");
char *str = "hello";
str[0] = 'H';
return 0;
}
嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和內容,導致工資要不上去!
無償分享大家一個資料包,差不多150多G。裡面學習內容、面經、專案都比較新也比較全!某魚上買估計至少要好幾十。
點選這裡找小助理0元領取:嵌入式物聯網學習資料(頭條)
3、例項3:棧溢位#include
static void test(void)
{
char buf[1024 * 1024] = {0};
static int i = 0;
i ;
printf("i = %d/n", i);
test();
}
int main(int argc, char **argv)
{
printf("==================segmentation fault test2==================/n");
test();
return 0;
}
4、例項4:記憶體越界#include
int main(int argc, char **argv)
{
printf("==================segmentation fault test3==================/n");
static char arr[5] = {0, 1, 2, 3, 4};
printf("arr[10000] = %d/n", arr[10000]);
return 0;
}
段錯誤除錯方法
從上面的幾個例子中,我們應該對段錯誤有了一定的認識,但實際專案中,實際中,段錯誤可能沒有上面的例子那麼明顯看出。如果之前沒有這方面的經驗,可能一時半會也定位不到問題。
接下來,分享一下段錯誤的3種除錯方法,供大家參考。
我們依舊使用例子來說明,例子:
#includestatic void func0(void) { printf("This is func0/n"); int *p = NULL; *p = 1234; } static void func1(void) { printf("This is func1/n"); func0(); } int main(int argc, char **argv) { printf("==================segmentation fault test4==================/n"); func1(); return 0; }
1、gdb一步步執行
使用gdb除錯,打一些斷點、按流程執行下去,執行到段錯誤的地方會直接提示報錯。
或者使用命令列直接gdb除錯:
這裡我們是在x86上執行,如果是定位arm嵌入式Linux程式,我們怎麼做的?
同樣也是可以使用gdb的,可以參考我們之前分享的文章:VSCode gdb gdbserver遠端除錯ARM程式
2、通過core檔案
Linux下,一個程式崩潰時,它一般會在指定目錄下生成一個core檔案。core檔案僅僅是一個記憶體映象(同時加上除錯資訊),主要是用來除錯的。
core檔案可開啟與關閉。相關命令:
ulimit -c # 檢視core檔案是否開啟 ulimit -c 0 # 禁止產生core檔案 ulimit -c unlimited #設定core檔案大小為不限制大小 ulimit -c 1024 #限制產生的core檔案的大小不能超過1024KB
0代表關閉。下面我們開啟它:
執行程式時,程式崩潰時,在程式目錄下會生成core檔案,比如:
除錯core檔案:
gdb test core
3、利用backtrace進行分析#include
#include
#include
#include
void func0(void)
{
printf("This is func0/n");
int *p = NULL;
*p = 1234;
}
void func1(void)
{
printf("This is func1/n");
func0();
}
void func2(void)
{
printf("This is func2/n");
func1();
}
void dump(int signo)
{
void *array[100];
size_t size;
char **strings;
size = backtrace(array, 100);
strings = backtrace_symbols(array, size);
printf("Obtained %zd stacks./n", size);
for(int i = 0; i < size; i )
{
printf("%s/n", strings[i]);
}
free(strings);
exit(0);
}
int main(int argc, char **argv)
{
printf("==================segmentation fault test5==================/n");
signal(SIGSEGV, &dump);
func2();
return 0;
}
當程式發生段錯誤時,核心會向程式傳送SIGSEGV訊號。dump為SIGSEGV訊號處理函式,其實現用到了execinfo.h裡的兩個函式:
int backtrace(void **buffer,int size); char ** backtrace_symbols (void *const *buffer, int size);
backtrace函式用於獲取當前執行緒的呼叫堆疊,獲取的資訊將會被存放在buffer中,它是一個指標列表。引數 size 用來指定buffer中可以儲存多少個void* 元素。函式返回值是實際獲取的指標個數,最大不超過size大小 在buffer中的指標實際是從堆疊中獲取的返回地址,每一個堆疊框架有一個返回地址。
backtrace_symbols將從backtrace函式獲取的資訊轉化為一個字串陣列。引數buffer應該是從backtrace函式獲取的指標陣列,size是該陣列中的元素個數(backtrace的返回值)。函式返回值是一個指向字串陣列的指標,它的大小同buffer相同。
每個字串包含了一個相對於buffer中對應元素的可列印資訊。它包括函式名,函式的偏移地址,和實際的返回地址。
注意:該函式的返回值是通過malloc函式申請的空間,因此呼叫者必須使用free函式來釋放指標。如果不能為字串獲取足夠的空間函式的返回值將會為NULL。
以上就是本次介紹的3種定位段錯誤問題的方法,可以定位不同程度的問題。如果大家覺得文章有幫助,麻煩幫忙點贊、轉發,謝謝!
原文連結:
https://mp.weixin.qq.com/s/6128-vfivS43A8ahO1UMSQ轉載自:嵌入式微處理器
原文連結:C開發中段錯誤的3種除錯方法
本文來源網路,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯絡我進行刪除。