常用的調試方法(段錯誤產生原因)

C 語言中常用的調試技巧和 demo
C語言中常用的調試方法
打印調試信息
GDB 調試器
編寫單元測試
段錯誤產生原因
初學時兩種常用的段錯誤調試方法

C 語言中常用的調試技巧和 demo

當程序員進行調試時,他們通常會使用一些調試語句或技巧來幫助他們理解代碼的執行過程以及識別問題。以下是一些在 C 語言中常用的調試技巧和 demo:

1. 使用 printf 進行調試

#include <stdio.h>int main() {int a = 5;printf("Value of a: %d\n", a);// ...return 0;
}

2. 使用 assert 進行斷言

#include <assert.h>int divide(int a, int b) {assert(b != 0);return a / b;
}

3. 使用 gdb 進行命令行調試

gcc -g -o my_program my_program.c
gdb ./my_program

在 GDB 中可以使用諸如 break, run, print, step, backtrace 等命令進行調試。

4. 輸出變量地址

#include <stdio.h>int main() {int a = 5;printf("Address of a: %p\n", (void*)&a);// ...return 0;
}

5. 使用 #ifdef 和宏定義進行條件編譯

#include <stdio.h>#define DEBUG 1int main() {#ifdef DEBUGprintf("Debugging information\n");#endif// ...return 0;
}

6. 手動觸發程序崩潰

#include <stdio.h>
#include <stdlib.h>int main() {int* ptr = NULL;*ptr = 5;  // This will cause a segmentation fault// ...return 0;
}

7. 使用 errno 打印錯誤信息

#include <stdio.h>
#include <errno.h>int main() {FILE *file = fopen("nonexistent_file.txt", "r");if (file == NULL) {perror("Error opening file");}// ...return 0;
}

8. 使用 valgrind 進行內存檢查

valgrind ./my_program

Valgrind 可以檢測內存泄漏和其他內存錯誤。

這些都是簡單而有效的調試技巧,可以幫助你更好地理解代碼并找到潛在問題。

C語言中常用的調試方法

在C語言中,常用的調試方法包括打印調試信息、使用調試器和編寫單元測試等。下面是一些用于調試的示例代碼:

  1. 打印調試信息
#include <stdio.h>// 在代碼中插入打印語句
void example_function() {printf("Debug: This is a debug message.\n");// ... 其他代碼 ...
}
  1. 條件打印
#include <stdio.h>// 使用條件打印語句
#ifdef DEBUG
#define DEBUG_PRINT(fmt, args...) printf(fmt, ##args)
#else
#define DEBUG_PRINT(fmt, args...) // 空定義,即不執行任何操作
#endifvoid example_function() {DEBUG_PRINT("Debug: This is a debug message.\n");// ... 其他代碼 ...
}

在編譯時,可以通過定義或未定義 DEBUG 宏來控制是否打印調試信息。

  1. 使用 assert 斷言
#include <assert.h>void example_function(int x) {assert(x > 0 && "x must be greater than 0");// ... 其他代碼 ...
}

assert 宏用于檢查一個表達式是否為真,如果為假,則觸發一個斷言失敗。

  1. 使用調試器(GDB)
#include <stdio.h>int main() {int x = 10;int y = 0;// 計算除法,可能導致除零錯誤int result = x / y;printf("Result: %d\n", result);return 0;
}

在終端中使用 GDB 調試程序:

gcc -g -o my_program my_program.c
gdb my_program

在 GDB 中運行程序,當程序崩潰時,可以使用 backtrace 和其他命令來查看調用棧和變量值。

  1. 編寫單元測試

使用測試框架進行單元測試,例如 C 中的 Unity 框架。編寫測試用例來驗證函數的預期行為。

#include "unity.h"void test_addition() {TEST_ASSERT_EQUAL(5, add(2, 3));
}int main() {UNITY_BEGIN();RUN_TEST(test_addition);return UNITY_END();
}

以上示例代碼提供了一些常見的調試方法。選擇合適的方法取決于問題的性質和復雜性。

打印調試信息

在C語言中,開發者經常使用printf語句進行調試。下面是一些在不同情境下用于調試的printf語句的示例:

  1. 打印變量值

    int x = 42;
    printf("The value of x is: %d\n", x);
    
  2. 調試文件、函數、行號

    printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
    
  3. 打印字符串

    char message[] = "Hello, World!";
    printf("Message: %s\n", message);
    
  4. 打印指針地址

    int *ptr = NULL;
    printf("Pointer address: %p\n", (void*)ptr);
    
  5. 條件打印

    #ifdef DEBUGprintf("Debug information\n");
    #endif
    
  6. 格式化輸出

    double pi = 3.14159265359;
    printf("Value of pi: %.2f\n", pi);  // 保留兩位小數
    
  7. 調試標志

    #define DEBUG 1#if DEBUGprintf("Debugging is enabled\n");
    #endif
    
  8. 打印多個變量

    int a = 10, b = 20;
    printf("Values: a=%d, b=%d\n", a, b);
    
  9. 打印特殊字符

    printf("This is a newline: \n");
    
  10. 查看函數執行流程

    printf("Entering function...\n");
    // Function code
    printf("Leaving function...\n");
    

這些語句可以根據需要添加或組合,根據代碼的結構和特定的調試需求選擇合適的調試語句。在實際的開發中,有時候也會使用專門的調試工具,比如gdb(GNU Debugger)等。

GDB 調試器

GDB(GNU Debugger)是一款強大的調試器,可以用于調試C、C++等程序。以下是一些基本的 GDB 使用方法:

編譯程序以便調試

在編譯程序時,需要包含調試信息。使用 -g 選項告訴編譯器生成調試信息:

gcc -g -o my_program my_program.c

啟動 GDB

gdb ./my_program

基本命令

  1. 運行程序

    run
    

    或者可以帶參數:

    run arg1 arg2
    
  2. 設置斷點

    在函數或者某一行設置斷點:

    break function_name
    break file_name:line_number
    
  3. 執行程序直到斷點

    continue
    
  4. 單步執行

    next      # 執行一行代碼,不進入函數
    step      # 執行一行代碼,進入函數
    
  5. 查看變量值

    print variable_name
    
  6. 查看函數調用棧

    backtrace
    
  7. 跳轉到某一行

    jump line_number
    
  8. 終止程序

    kill
    

示例

考慮以下簡單的程序:

// my_program.c
#include <stdio.h>int main() {int x = 5;int y = 0;int result = x / y;printf("Result: %d\n", result);return 0;
}

使用 GDB 調試:

  1. 編譯程序:

    gcc -g -o my_program my_program.c
    
  2. 啟動 GDB:

    gdb ./my_program
    
  3. 在 GDB 中運行程序:

    run
    

    程序將在除以零的地方崩潰。

  4. 查看變量值:

    print x
    print y
    
  5. 查看函數調用棧:

    backtrace
    
  6. 設置斷點并運行:

    break main
    run
    

    main 函數的入口設置了斷點,程序會在進入 main 函數時停下。

  7. 單步執行:

    step
    

    逐行執行代碼,可以看到在除以零的地方停下。

  8. 退出 GDB:

    quit
    

這只是 GDB 的基本用法。對于更復雜的程序,可能需要更多高級的調試技術。 GDB 的命令和功能非常豐富,可以根據需要查閱 GDB 的文檔。

編寫單元測試

編寫單元測試是一種驗證代碼的方法,以確保每個獨立單元的功能都按預期工作。下面是一份簡要的指南,幫助你編寫 C 語言程序的單元測試。

選擇測試框架

在 C 語言中,一些流行的測試框架包括:

  1. Check: Check Testing Framework
  2. Unity: Unity Testing Framework
  3. Google Test: Google Test

安裝測試框架

下載并按照測試框架的文檔安裝框架。通常,這涉及到將測試框架的頭文件和庫文件添加到你的項目中。

編寫測試用例

使用測試框架編寫測試用例。測試用例是檢查特定函數或模塊的代碼塊,確保其行為符合預期。

示例測試用例(使用 Unity 框架):

#include "unity.h"
#include "my_functions.h"  // 你的代碼文件頭文件void test_addition() {TEST_ASSERT_EQUAL_INT(5, add(2, 3));
}void test_subtraction() {TEST_ASSERT_EQUAL_INT(2, subtract(5, 3));
}int main() {UNITY_BEGIN();RUN_TEST(test_addition);RUN_TEST(test_subtraction);UNITY_END();return 0;
}

編寫被測試的代碼

在編寫測試用例之前,首先編寫要測試的代碼。例如,你可能有一個包含兩個函數的頭文件 my_functions.h

// my_functions.h
#ifndef MY_FUNCTIONS_H
#define MY_FUNCTIONS_Hint add(int a, int b);
int subtract(int a, int b);#endif

對應的實現文件 my_functions.c 可能如下所示:

// my_functions.c
#include "my_functions.h"int add(int a, int b) {return a + b;
}int subtract(int a, int b) {return a - b;
}

構建測試

構建測試時,需要鏈接測試框架和被測試的代碼。

gcc -o my_tests my_tests.c my_functions.c -lunity

運行測試

./my_tests

測試框架將執行所有測試用例,并顯示每個測試用例的結果。

分析測試結果

測試通過時,你會看到一條消息表明所有測試用例都通過。如果有測試失敗,框架通常會提供詳細的輸出,說明哪個測試用例失敗以及失敗的原因。

增加測試覆蓋率

編寫測試用例時,盡量涵蓋不同的輸入情況,以確保代碼的魯棒性。覆蓋率工具(例如 gcov)可以幫助你確定代碼中哪些部分被測試覆蓋。

持續集成

將單元測試集成到你的持續集成(CI)流程中,以確保每次代碼更改都會觸發測試。這有助于捕獲引入錯誤的更改,并確保代碼庫的整體穩定性。

以上是一個簡單的單元測試流程,實際情況可能因項目的復雜性而有所不同。

段錯誤產生原因

1.訪問不存在的內存地址

如下面代碼,ptr沒有申請空間就直接拷貝數據:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{char *ptr = NULL;//This is the wrong implementation:strncpy(ptr, "abc", 3);//ptr沒有申請空間就直接使用//The right way://ptr = (char *)malloc(sizeof(char) * 10);//memset(ptr, 0, 10);//strncpy(ptr, "abc", 3);return 0;
}

2.訪問只讀的內存地址

錯誤做法:往字符串常量空間拷貝新的數據

#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{char *ptr = "test";strcpy(ptr, "TEST1");return 0;
}

3.訪問系統保護的內存地址

如:

#include <stdio.h>int main(int argc, char *argv[])
{int *ptr = (int *)0;*ptr = 100;return 0;
}

4.棧溢出

如:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[])
{main(argc, argv);
}

初學時兩種常用的段錯誤調試方法

1.使用printf輸出調試信息

這個是看似最簡單但往往很多情況下是十分有效的調試方式,也許可以說是程序員用的最多的調試方式。簡單來說,就是在程序的重要代碼附近加上printf輸出信息,這樣可以跟蹤并打印出段錯誤在代碼中最可能出現的位置。

使用案例:
以下面這段錯誤代碼為例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{char *ptr = NULL;//This is the wrong implementation:strncpy(ptr, "abc", 3);//ptr沒有申請空間就直接使用return 0;
}

可以在代碼中添加printf調試信息:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{printf("line:%d\n", __LINE__); //單純打印代碼行數char *ptr = NULL;//This is the wrong implementation:printf("line:%d\n", __LINE__); //單純打印代碼行數strncpy(ptr, "abc", 3);//ptr沒有申請空間就直接使用printf("line:%d\n", __LINE__); //單純打印代碼行數return 0;
}

編譯運行后,會有如下輸出:

line:7
line:10
Segmentation fault (core dumped)

通過日志信息,就可以看到strncpy(ptr, “abc”, 3);語句沒有被執行,我們就可以根據定位到的位置來排除ptr是否是未分配空間或者分配的空間不足引發的問題

2.使用gcc和gdb

調試步驟:
1、為了能夠使用gdb調試程序,在編譯階段加上-g參數,還是以下面這段錯誤代碼為例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{char *ptr = NULL;//This is the wrong implementation:strncpy(ptr, "abc", 3);//ptr沒有申請空間就直接使用return 0;
}

編譯代碼添加-g選項

gcc -g -o test test.c

編譯出test程序,然后用gdb調試:

linux@linux:~/test$ gdb ./test
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...done.
(gdb) r
Starting program: /home/linux/test/test Program received signal SIGSEGV, Segmentation fault.
0x0000555555554611 in main (argc=1, argv=0x7fffffffe528) at test.c:9
9                               strncpy(ptr, "abc", 3);//ptr沒有申請空間就直接使用
(gdb) quit
A debugging session is active.Inferior 1 [process 15829] will be killed.Quit anyway? (y or n) y

從輸出看出,程序到SIGSEGV信號,觸發段錯誤,并提示地址0x0000555555554611以及代碼第9行出錯。

當然,還有利用core文件和gdb配置配合調試、使用objdump以及catchsegv命令的其他更為復雜的調試手段。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/213517.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/213517.shtml
英文地址,請注明出處:http://en.pswp.cn/news/213517.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

跟風申請香港優才計劃的人,很容易進入騙局和被割韭菜!

跟風申請香港優才計劃的人&#xff0c;很容易進入騙局和被割韭菜&#xff01; 不得不承認一個事實就是&#xff0c;越來越多內地人正在搶占申請香港身份的份額&#xff01;就因為這個項目門檻低、投入低&#xff0c;簡單來說就是多一層身份&#xff0c;多一層福利保障。 從目前…

Pyqt python 界面代碼

1、界面拖動代碼 # 拖動 def mousePressEvent(self, event):if event.button() QtCore.Qt.LeftButton and self.isMaximized() False:self.m_flag Trueself.m_Position event.globalPos() - self.pos() # 獲取鼠標相對窗口的位置event.accept()self.setCursor(QtGui.QCur…

go - 計算CIDR的主機數量

在網絡中&#xff0c;CIDR /32 表示該地址只能用作網絡地址本身&#xff0c;不能分配給任何主機。因此&#xff0c;在計算主機數量時&#xff0c;應將 CIDR 地址按照其位掩碼長度進行區分。對于 /32 子網掩碼&#xff0c;主機數量總是為 1&#xff0c;而不是 -1。 以下是修正后…

二.ts基礎類型

ts的基礎類型包含js的基礎類型和ts獨有的基礎類型 我們一般使用[let | const | val] 變量&#xff1a;類型 值的方式聲明一個帶有類型的變量 stringlet val:string 1numberlet val:number 1boolearnlet val:boolaern falseundefindlet val:undefind undefindnulllet val:nul…

Kubernetes實戰(九)-kubeadm安裝k8s集群

1 環境準備 1.1 主機信息 iphostname10.220.43.203master10.220.43.204node1 1.2 系統信息 $ cat /etc/redhat-release Alibaba Cloud Linux (Aliyun Linux) release 2.1903 LTS (Hunting Beagle) 2 部署準備 master/與slave主機均需要設置。 2.1 設置主機名 # master h…

成都工業學院Web技術基礎(WEB)實驗五:CSS3動畫制作

寫在前面 1、基于2022級計算機大類實驗指導書 2、代碼僅提供參考&#xff0c;前端變化比較大&#xff0c;按照要求&#xff0c;只能做到像&#xff0c;不能做到一模一樣 3、圖片和文字僅為示例&#xff0c;需要自行替換 4、如果代碼不滿足你的要求&#xff0c;請尋求其他的…

使用PyTorch II的新特性加快LLM推理速度

Pytorch團隊提出了一種純粹通過PyTorch新特性在的自下而上的優化LLM方法&#xff0c;包括: Torch.compile: PyTorch模型的編譯器 GPU量化:通過降低精度操作來加速模型 推測解碼:使用一個小的“草稿”模型來加速llm來預測一個大的“目標”模型的輸出 張量并行:通過在多個設備…

成都工業學院Web技術基礎(WEB)實驗三:CSS字體等屬性使用

寫在前面 1、基于2022級計算機大類實驗指導書 2、代碼僅提供參考&#xff0c;前端變化比較大&#xff0c;按照要求&#xff0c;只能做到像&#xff0c;不能做到一模一樣 3、圖片和文字僅為示例&#xff0c;需要自行替換 4、如果代碼不滿足你的要求&#xff0c;請尋求其他的…

Oracle 慢查詢排查步驟

1. Oracle 慢查詢排查步驟 1.1. 前言 記錄一次 Oracle 慢查詢的排查過程 , 便于以后直接使用。 看了一些文檔 , Oracle 中優化的方案和 Mysql 基本上是一致的 , 通常包括一下幾個方向 : 基準測試 (吞吐量): 包括 Oracle 本身吞吐量和磁盤 I/O 吞吐量 硬件分析 (資源情況): 包…

rails3 row sql example

refer: https://stackoverflow.com/questions/14824453/rails-raw-sql-example 搜索怎么在Rails3 使用row sql&#xff0c; 打開上面的鏈接&#xff0c;可以找到這樣的答案&#xff0c;如下圖&#xff1a; sql "Select * from ... your sql query here" records_ar…

Nginx的server層外層的常見配置語句的解讀

有下面的Nginx配置: worker_processes auto; worker_rlimit_nofile 51200;events {use epoll;worker_connections 51200;multi_accept on; }http {include mime.types;default_type application/octet-stream;server_names_hash_bucket_size 512;client_max_body_size 50m;cli…

推薦4個優秀的 Python 時間序列分析庫

時間序列分析在金融和醫療保健等領域至關重要&#xff0c;在這些領域&#xff0c;理解隨時間變化的數據模式至關重要。在本文中&#xff0c;我們將介紹四個主要的Python庫——statmodels、tslearn、tssearch和tsfresh——每個庫都針對時間序列分析的不同方面進行了定制。這些庫…

最長子字符串的長度 (一) - 華為OD統一考試(C卷)

OD統一考試&#xff08;C卷&#xff09; 分值&#xff1a; 100分 題解&#xff1a; Java / Python / C 題目描述 給你一個字符串 s&#xff0c;字符串s首尾相連成一個環形&#xff0c;請你在環中找出字符出現了偶數次最長子字符串的長度。 輸入描述 輸入是一串小寫字母組成的…

VS2015編譯GDAL3.2.0+opencl+C#

參考借鑒https://www.cnblogs.com/litou/p/15004877.html 參考借鑒https://www.cnblogs.com/xiaowangba/p/6313903.html 參考借鑒gdal、proj、geos、sqlite等在VS2015下編譯和配置_vs2015編譯sqlite3-CSDN博客 參考借鑒Windows下GDAL3.1.2編譯 (VS2015)_gdal windows編譯-CS…

字符串String

字符串字面量 let s "hello";變量s屬于字符串字面量&#xff0c;它們屬于硬編碼進程序的字符串值&#xff0c;屬于不可變的類型。但并不是所有字符串的值都能夠在編寫代碼時確定。 String類型 String類型會在堆上分配到自己需要的存儲空間&#xff0c;所以它能夠…

Nacos簡介

1.Nacos簡介 1.1.為什么叫Nacos&#xff1f; 前四個字母分別為Naming和Configuration的前兩個字母&#xff0c;最后的s為service。 1.2.Nacos是什么&#xff1f; nacos是第二代微服務SpringCloudAlibaba開源的一款注冊中心和分布式配置中心組件&#xff0c;其功能上為第一代微…

聲明式數據建模、定義簡單易懂:下一代 ORM 助你效率倍增 | 開源日報 No.102

prisma/prisma Stars: 34.0k License: Apache-2.0 Prisma 是一個下一代 ORM&#xff0c;包括以下工具&#xff1a; Prisma Client&#xff1a;為 Node.js 和 TypeScript 自動生成的類型安全查詢構建器Prisma Migrate&#xff1a;聲明式數據建模和遷移系統Prisma Studio&#…

山人求道篇:五、交易中的大道至簡與奧卡姆剃刀

量化的剃刀不是減去因子、減去代碼&#xff0c;而是減去冗余信息量 市面上有的策略對各品種設置了不同參數&#xff0c;每個品種進場不一樣就算了&#xff0c;出場參數還不一樣&#xff0c;那你說他到底賺的是什么利潤呢&#xff1f; 他自己也不知道&#xff0c;主要目的是為…

鴻蒙應用開發(二)環境搭建

開發流程 IDE下載 首先下載HUAWEI DevEco Studio&#xff0c;介紹首次啟動DevEco Studio的配置向導&#xff1a; 運行已安裝的DevEco Studio&#xff0c;首次使用&#xff0c;請選擇Do not import settings&#xff0c;單擊OK。安裝Node.js與ohpm。node.js 是基于 V8 引擎構…

JavaScript常用技巧專題二

文章目錄 一、前言二、生成隨機字符串三、轉義HTML特殊字符四、單詞首字母大寫五、將字符串轉換為小駝峰六、刪除數組中的重復值七、移除數組中的假值八、獲取兩個數字之間的隨機數九、將數字截斷到固定的小數點十、日期10.1、計算兩個日期之間天數10.2、從日期中獲取是一年中的…