<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # C 編程,第 5 部分:調試 > 原文:[Processes, Part 1: Introduction](https://github.com/angrave/SystemProgramming/wiki/Processes%2C-Part-1%3A-Introduction) > 校驗:[_stund](https://github.com/hqiwen) > 自豪地采用[谷歌翻譯](https://translate.google.cn/) ## Hitchhiker 調試 C 程序指南 這將成為幫助您調試 C 程序的重要指南。有不同的層次的方法可以檢查錯誤,我們將覆蓋大多數方法。放松地去接受這些在調試 C 程序中有用的方法,包括但不限于調試器使用,識別常見錯誤類型,陷阱和有效的 Google 搜索提示。 ## 代碼內調試 ### 簡化代碼 使用輔助函數使代碼模塊化。如果有重復的任務(例如,獲取指向 MP2 中連續塊的指針),請將它們作為輔助函數。并確保每個函數都能很好地完成一件事,這樣您就不必再調試兩次了。 假設我們通過查找每次迭代的最小元素來進行選擇排序, ```c void selection_sort(int *a, long len){ for(long i = len-1; i > 0; --i){ long max_index = i; for(long j = len-1; j >= 0; --j){ if(a[max_index] < a[j]){ max_index = j; } } int temp = a[i]; a[i] = a[max_index]; a[max_index] = temp; } } ``` 許多人可以看到代碼中的錯誤,但它可以幫助重構上述方法 ```c long max_index(int *a, long start, long end); void swap(int *a, long idx1, long idx2); void selection_sort(int *a, long len); ``` 并且錯誤特別在一個函數中。 最后,我們不是關于重構/調試代碼的類 - 事實上,大多數系統代碼都是如此殘酷,以至于您不想閱讀它。但是為了調試,從長遠來看,采用某些做法可能會對您有所幫助。 ### 斷言! 使用斷言來確保您的代碼在某一點上起作用 - 更重要的是,確保您以后不要破壞它。例如,如果您的數據結構是雙向鏈表,您可以執行`assert(node -> size == node -> next -> prev -> size)`來斷言下一個節點有一個指向當前節點的指針。您還可以檢查指針指向預期的內存地址范圍,而不是 `null`,`->size`是合理的等等。NDEBUG 宏將禁用所有斷言,因此在完成調試后不要忘記設置它。 [http://www.cplusplus.com/reference/cassert/assert/](http://www.cplusplus.com/reference/cassert/assert/) 斷言的一個簡單例子,我正在使用 `memcpy` 編寫代碼 ```c assert(!(src < dest+n && dest < src+n)); // 檢查溢出 memcpy(dest, src, n); ``` 這個檢查可以在編譯時關閉,但會節省你**成噸**的調試問題的時間! ### 用 printfs 當所有其他方法都失敗時,打印是一個好選擇!你的每個函數都應該知道它將要做什么(例如 find_min 更好地找到最小元素)。您希望測試每個函數是否正在執行它要執行的操作,并確切地查看代碼中斷的位置。在有競爭條件的情況下,`tsan` 可能會提供幫助,但讓每個線程在特定時間打印出數據可以幫助您識別競爭條件。 ## Valgrind `Valgrind` 是一系列用來調試和收集信息的工具,可以使你的程序更加正確和發現一些運行時的問題。最有用的工具是`Memcheck`,它可以發現一些內存相關的錯誤,通常在c和c++ 里面常犯的、導致程序崩潰的和不能預測的行為(比如,沒有釋放內存緩存)。 在你的程序上使用`valgrind`: > valgrind --leak-check=yes myprogram arg1 arg2 或者 > valgrind ./myprogram 參數是可選的,默認運行工具是`memcheck`.輸出將用表格的方式展示出來,包括內存的分配大小和釋放內存的大小,錯誤的數量。 這里是一個例子來幫助你解釋這些結果。假設你有一個簡單的像這樣的程序: ```c #include <stdlib.h> void dummy_function() { int* x = malloc(10 * sizeof(int)); x[10] = 0; // error 1:as you can see here we write to an out of bound memory address } // error 2: memory leak the allocated x not freed int main(void) { dummy_function(); return 0; } ``` 讓我們看一下`Valgrind`的輸出(這個程序編譯和運行沒有錯誤)。 ```log ==29515== Memcheck, a memory error detector ==29515== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==29515== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==29515== Command: ./a ==29515== ==29515== Invalid write of size 4 ==29515== at 0x400544: dummy_function (in /home/rafi/projects/exocpp/a) ==29515== by 0x40055A: main (in /home/rafi/projects/exocpp/a) ==29515== Address 0x5203068 is 0 bytes after a block of size 40 alloc'd ==29515== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==29515== by 0x400537: dummy_function (in /home/rafi/projects/exocpp/a) ==29515== by 0x40055A: main (in /home/rafi/projects/exocpp/a) ==29515== ==29515== ==29515== HEAP SUMMARY: ==29515== in use at exit: 40 bytes in 1 blocks ==29515== total heap usage: 1 allocs, 0 frees, 40 bytes allocated ==29515== ==29515== LEAK SUMMARY: ==29515== definitely lost: 40 bytes in 1 blocks ==29515== indirectly lost: 0 bytes in 0 blocks ==29515== possibly lost: 0 bytes in 0 blocks ==29515== still reachable: 0 bytes in 0 blocks ==29515== suppressed: 0 bytes in 0 blocks ==29515== Rerun with --leak-check=full to see details of leaked memory ==29515== ==29515== For counts of detected and suppressed errors, rerun with: -v ==29515== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) ``` **不合格的寫入**:它發現我們的堆塊超過限度了(在分配塊外寫入) **絕對損失**:內存泄露——你可能忘記釋放內存塊了 Valgrind 是一個有效的工具來檢查在運行時的錯誤。C特別存在這種問題,所以在編譯你的程序之后你可以使用Valgrind來修復編譯時未能捕獲的和經常發生在運行時的錯誤。 更多的信息請參考![官網](http://valgrind.org/docs/manual/quick-start.html) ## Tsan ThreadSanitizer 是 Google 的一個工具,內置于 clang(和 gcc)中,可幫助您檢測代碼中的競爭條件。有關該工具的更多信息,請參閱 Github wiki。 請注意,使用 tsan 運行會降低代碼的速度。 ```c #include <pthread.h> #include <stdio.h> int Global; void *Thread1(void *x) { Global++; return NULL; } int main() { pthread_t t[2]; pthread_create(&t[0], NULL, Thread1, NULL); Global = 100; pthread_join(t[0], NULL); } // compile with gcc -fsanitize=thread -pie -fPIC -ltsan -g simple_race.c ``` 我們可以看到變量 Global 存在競爭條件。主線程和使用 pthread_create 創建的線程都會嘗試同時更改該值。但是,ThreadSantizer 能抓住它嗎? ``` $ ./a.out ================== WARNING: ThreadSanitizer: data race (pid=28888) Read of size 4 at 0x7f73ed91c078 by thread T1: #0 Thread1 /home/zmick2/simple_race.c:7 (exe+0x000000000a50) #1 :0 (libtsan.so.0+0x00000001b459) Previous write of size 4 at 0x7f73ed91c078 by main thread: #0 main /home/zmick2/simple_race.c:14 (exe+0x000000000ac8) Thread T1 (tid=28889, running) created by main thread at: #0 :0 (libtsan.so.0+0x00000001f6ab) #1 main /home/zmick2/simple_race.c:13 (exe+0x000000000ab8) SUMMARY: ThreadSanitizer: data race /home/zmick2/simple_race.c:7 Thread1 ================== ThreadSanitizer: reported 1 warnings ``` 如果我們用 debug 標志編譯,那么它也會給我們變量名。 ## GDB 簡介: [http://www.cs.cmu.edu/~gilpin/tutorial/](http://www.cs.cmu.edu/%7Egilpin/tutorial/) ### 以編程方式設置斷點 使用 GDB 調試復雜的 C 程序時,一個非常有用的技巧是在源代碼中設置斷點。 ```c int main() { int val = 1; val = 42; asm("int $3"); // 在這里設置一個斷點 val = 7; } ``` ```source-shell $ gcc main.c -g -o main && ./main (gdb) r [...] Program received signal SIGTRAP, Trace/breakpoint trap. main () at main.c:6 6 val = 7; (gdb) p val $1 = 42 ``` #### 檢查內存內容 [http://www.delorie.com/gnu/docs/gdb/gdb_56.html](http://www.delorie.com/gnu/docs/gdb/gdb_56.html) 例如, ```c int main() { char bad_string[3] = {'C', 'a', 't'}; printf("%s", bad_string); } ``` ```source-shell $ gcc main.c -g -o main && ./main $ Cat ZVQ?? $ ``` ```source-shell (gdb) l 1 #include <stdio.h> 2 int main() { 3 char bad_string[3] = {'C', 'a', 't'}; 4 printf("%s", bad_string); 5 } (gdb) b 4 Breakpoint 1 at 0x100000f57: file main.c, line 4. (gdb) r [...] Breakpoint 1, main () at main.c:4 4 printf("%s", bad_string); (gdb) x/16xb bad_string 0x7fff5fbff9cd: 0x63 0x61 0x74 0xe0 0xf9 0xbf 0x5f 0xff 0x7fff5fbff9d5: 0x7f 0x00 0x00 0xfd 0xb5 0x23 0x89 0xff (gdb) ``` 這里,通過使用帶有參數`16xb`的`x`命令,我們可以看到從內存地址`0x7fff5fbff9c`(`bad_string`的值)開始,printf 實際上會將以下字節序列視為字符串,因為我們提供了格式錯誤的字符串,沒有空終結符。 `0x43 0x61 0x74 0xe0 0xf9 0xbf 0x5f 0xff 0x7f 0x00`
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看