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種除錯方法

本文來源網路,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯絡我進行刪除。