歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 一份簡單的在Linux下編譯及調試C代碼的指南

一份簡單的在Linux下編譯及調試C代碼的指南

日期:2017/3/1 9:20:56   编辑:Linux編程

對於Linux下的C程序員來說,幾乎天天都會和Linux打交道。但在很多人的眼中,Linux是一個易用性極差、靠命令驅動的操作系統,根本無法與有著友好用戶界面的Windows相比。確實是這樣的,即使大家的程序是運行在Linux下,基於以下種種原因,我們的大部分工作還是在Windows下完成的:

第一,除了編譯調試代碼之外,每個程序員還有很多工作要做,像文檔編寫、郵件發送及回復、PPT制作等,這些工作在Windows下做要更方便快捷一些。

第二,公司及項目組的資源有限,一般不會為每個開發人員配備一台安裝有Linux的機器,而是大家共用一台或少許幾台Linux機器。在每台機器上建立多個用戶,需要用來編譯或調試程序的時候,大家用某個用戶登錄上去。


這樣,問題就來了:自己平時是在Windows下面辦公的,而自己編寫的程序的運行環境又是Linux的,如何從Windows切換到Linux呢?是不是要到專門的Linux機器上去編寫代碼呢?我們如何在Linux下調試程序呢?本文將一一道來。

到Linux下去編譯運行程序的步驟
只要在Windows下安裝一個叫做SecureCRT的軟件和一個叫做FileZilla的軟件,便可輕松實現Windows到Linux的切換。
SecureCRT是一款支持SSH(SSH1和SSH2)的終端仿真程序,簡單地說是Windows下登錄Linux服務器主機的軟件。FileZilla是一個免費開源的FTP軟件,分為客戶端版本和服務器版本兩種,具備所有的FTP軟件功能。
在使用SecureCRT和FileZilla之前,要確保有一台安裝了Linux的機器處於運行狀態(一般說來,每個開發小組都會有專門用於測試程序的機器,可以在此機器上安裝Linux)。作者使用的Linux機器的IP地址為xx.xx.xx.xx,用戶名為zxin10,密碼為yyyy。

第一步:使用SecureCRT登錄Linux。
打開SecureCRT軟件,在界面上輸入IP和用戶名,如圖1所示。

圖1 登錄界面

然後,單擊圖1中的“Connect”,在出現的界面上輸入密碼,如圖2所示。

圖2 密碼輸入界面

密碼輸入正確之後,便登錄到了Linux系統下,如圖3所示。

圖3 登錄成功之後的界面

為了編譯自己的程序,我們需要建立自己的文件存放目錄,如圖4所示。

圖4 新建個人目錄

目錄建立成功之後,我們便可以轉到目錄中去看一下,如圖5所示。

圖5 轉到新建目錄

此時,“萬事俱備,只欠東風”,我們接下來要做的工作是利用FileZilla軟件將自己在Windows下編寫的程序傳上去。
示例程序如下:

/**********************************************************************
* 版權所有 (C)2015, Zhou Zhaoxiong。
*
* 文件名稱:Hello.c
* 文件標識:無
* 內容摘要:演示Windows下編寫的程序如何在Linux下執行
* 其它說明:無
* 當前版本:V1.0
* 作 者:Zhou Zhaoxiong
* 完成日期:201501028
*
**********************************************************************/
#include <stdio.h>

/**********************************************************************
* 功能描述:主函數
* 輸入參數:無
* 輸出參數:無
* 返 回 值:0-執行完畢
* 其它說明:無
* 修改日期 版本號 修改人 修改內容
* -------------------------------------------------------------------
* 201501028 V1.0 Zhou Zhaoxiong 創建
***********************************************************************/
int main()
{
printf("Hello, world!\n");

return 0;
}



第二步:使用FileZilla將代碼上傳到Linux。

將該“Hello.c”文件存放在D盤的“Test”文件夾下,並啟動FileZilla,如圖6所示。

圖6 啟動FileZilla之後的界面

在“主機(H)”中輸入IP地址,在“用戶名(U)”中輸入“zxin10”用戶名,在“密碼(W)”中輸入正確的密碼,“端口(P)”可不填寫而使用默認值,則可登錄到Linux機器上去。登上去後,轉到“zhouzx”目錄下,並將“Hello.c”文件傳上去,如圖7所示。

圖7 上傳文件之後的界面

此時,“Hello.c”文件已經傳到了“zhouzx”目錄下,現在可以對該文件進行編譯了。

第三步:在Linux上編譯和運行程序。
使用“gcc -g -o Hello Hello.c”命令對文件進行編譯,如圖8所示。

圖8 編譯之後的結果

可以看到,編譯成功之後,有“Hello”文件生成。緊接著,運行“Hello”命令,便可看到程序的輸出結果,如圖9所示。

圖9 程序的輸出結果

以上便是將Windows下的程序放到Linux下去編譯和運行的全過程。這裡只是示例了簡單的程序,實際軟件開發項目中的程序要復雜很多,但基本操作流程都是類似的。當然,直接在Linux下編寫程序也是可以的,如可以利用VI編輯器來寫程序。但由於易用性的原因,我認為,在Windows下編寫程序要更方便一點。大家要根據自己的習慣及項目組的要求來選擇合理的代碼編寫的方式。


程序調試示例---用gdb分析core文件
在實際的軟件開發項目中,程序出現問題是在所難免的。遙想本人參加工作之後首次遇到程序的情景,至今還歷歷在目。之前的經驗告訴我,我們越是驚慌失措,問題就越是解決不了。我們要先讓自己平靜下來,然後再尋找解決程序問題的辦法。
在Linux下做開發的朋友,想必都與core文件打過交道。當看到自己的程序運行之後出現core時,很多人都慌亂了,仿佛天快要塌下來一樣。其實,我們大可不必如此,只要我們掌握了用gdb調試core文件的辦法,依然可以很快定位程序問題,一舉將bug消滅掉。有關Linux core文件的更多介紹,請閱讀此文:http://www.cnblogs.com/dongzhi ... .html。
這裡以一個實際的程序為例,以用gdb分析core文件為例介紹了Linux下程序調試的方法,同時演示了常見gdb命令的操作方法。
在Linux下執行“ulimit –a”命令查看程序運行出錯時是否會產生core文件,命令執行的結果中有“core file size = 0”表示不會產生core文件,此時要使用“ulimit -c 1000000”命令設置core文件的大小。

示例程序

/**********************************************************************
* 版權所有 (C)2015, Zhou Zhaoxiong。
*
* 文件名稱:GdbDebug.c
* 文件標識:無
* 內容摘要:Gdb命令演示程序
* 其它說明:無
* 當前版本:V1.0
* 作 者:Zhou Zhaoxiong
* 完成日期:20151008
*
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 數據類型重定義
typedef unsigned char UINT8;
typedef signed int INT32;
typedef unsigned int UINT32;


// 函數聲明
void Sleep(UINT32 iCountMs);
void PrintInfo(void);
INT32 main();


/**********************************************************************
* 功能描述:主函數
* 輸入參數:無
* 輸出參數:無
* 返 回 值:無
* 其它說明:無
* 修改日期 版本號 修改人 修改內容
* -------------------------------------------------------------------
* 20151008 V1.0 Zhou Zhaoxiong 創建
***********************************************************************/
INT32 main()
{
PrintInfo(); // 在屏幕上輸出消息

return 0;
}


/**********************************************************************
* 功能描述: 在屏幕上輸出消息
* 輸入參數: 無
* 輸出參數: 無
* 返 回 值: 無
* 其它說明: 無
* 修改日期 版本號 修改人 修改內容
* ----------------------------------------------------------------------
* 20151008 V1.0 Zhou Zhaoxiong 創建
************************************************************************/
void PrintInfo(void)
{
UINT32 iLoopFlag = 0;
UINT32 iSum = 0;
UINT32 iLen = 0;
UINT8 *pCtrStr = NULL;

iLen = strlen(pCtrStr);

for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++) // 打印消息iLen次
{
printf("PrintInfo: hello, world!\n");

iSum = iSum + iLoopFlag;

Sleep(10 * 1000); // 每10s打印一次
}

return;
}


/**********************************************************************
* 功能描述: 程序休眠
* 輸入參數: iCountMs-休眠時間(單位:ms)
* 輸出參數: 無
* 返 回 值: 無
* 其它說明: 無
* 修改日期 版本號 修改人 修改內容
* ------------------------------------------------------------------
* 20151008 V1.0 Zhou Zhaoxiong 創建
********************************************************************/
void Sleep(UINT32 iCountMs)
{
struct timeval t_timeout = {0};

if (iCountMs < 1000)
{
t_timeout.tv_sec = 0;
t_timeout.tv_usec = iCountMs * 1000;
}
else
{
t_timeout.tv_sec = iCountMs / 1000;
t_timeout.tv_usec = (iCountMs % 1000) * 1000;
}
select(0, NULL, NULL, NULL, &t_timeout); // 調用select函數阻塞程序
}


用gdb分析core文件
在Linux上用“gcc -g -o GdbDebug GdbDebug.c”命令對程序進��編譯之後,運行“GdbDebug”命令,發現在當前目錄下出現了core文件。利用gdb命令對core文件進行分析的過程如下所示:

~/zhouzhaoxiong/zzx/GdbDebug> gdb GdbDebug core     -- 啟動gdb對core文件的分析
GNU gdb (GDB) SUSE (7.3-0.6.1)
Copyright (C) 2011 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-suse-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug...done.
Core was generated by `GdbDebug'.
Program terminated with signal 11, Segmentation fault.
#0 0x00007f4a736f9812 in __strlen_sse2 () from /lib64/libc.so.6
(gdb) where -- 查看程序出問題的地方
#0 0x00007f4a736f9812 in __strlen_sse2 () from /lib64/libc.so.6
#1 0x000000000040061a in PrintInfo () at GdbDebug.c:64 -- 可以看到,在GdbDebug.c文件的第64行出的問題
#2 0x00000000004005e5 in main () at GdbDebug.c:41
(gdb) b 41 -- 在GdbDebug.c文件第41行設立斷點
Breakpoint 1 at 0x4005e0: file GdbDebug.c, line 41.
(gdb) b 64 -- 在GdbDebug.c文件第64行設立斷點
Breakpoint 2 at 0x400611: file GdbDebug.c, line 64.
(gdb) info b -- 顯示斷點信息
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004005e0 in main at GdbDebug.c:41
2 breakpoint keep y 0x0000000000400611 in PrintInfo at GdbDebug.c:64
(gdb) r -- 運行GdbDebug
Starting program: /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug

Breakpoint 1, main () at GdbDebug.c:41
41 PrintInfo(); // 在屏幕上輸出消息
(gdb) n -- 執行下一步

Breakpoint 2, PrintInfo () at GdbDebug.c:64
64 iLen = strlen(pCtrStr);
(gdb) p iLen -- 打印(輸出)iLen的值
$1 = 0
(gdb) p iLoopFlag -- 打印(輸出)iLoopFlag的值
$2 = 0
(gdb) c -- 繼續執行
Continuing.

Program received signal SIGSEGV, Segmentation fault. -- 程序core掉了
0x00007ffff7ae9812 in __strlen_sse2 () from /lib64/libc.so.6
(gdb) q -- 退出gdb
A debugging session is active.

Inferior 1 [process 26640] will be killed.

Quit anyway? (y or n) y
~/zhouzhaoxiong/zzx/GdbDebug>


從以上分析可知,執行GdbDebug.c文件的第64行時程序core掉了。此時仔細分析程序,發現pCtrStr指針為空。當對一個不存在的指針取長度時,由於找不到地址,程序便崩潰了。修改的辦法也非常的簡單,只需要讓pCtrStr指針指向具體的地址即可。

常見gdb命令操作示例
修改之後的代碼如下:

/**********************************************************************
* 版權所有 (C)2015, Zhou Zhaoxiong。
*
* 文件名稱:GdbDebug.c
* 文件標識:無
* 內容摘要:Gdb命令演示程序
* 其它說明:無
* 當前版本:V1.0
* 作 者:Zhou Zhaoxiong
* 完成日期:20151008
*
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 數據類型重定義
typedef unsigned char UINT8;
typedef signed int INT32;
typedef unsigned int UINT32;


// 函數聲明
void Sleep(UINT32 iCountMs);
void PrintInfo(void);
INT32 main();


/**********************************************************************
* 功能描述:主函數
* 輸入參數:無
* 輸出參數:無
* 返 回 值:無
* 其它說明:無
* 修改日期 版本號 修改人 修改內容
* -------------------------------------------------------------------
* 20151008 V1.0 Zhou Zhaoxiong 創建
***********************************************************************/
INT32 main()
{
PrintInfo(); // 在屏幕上輸出消息

return 0;
}


/**********************************************************************
* 功能描述: 在屏幕上輸出消息
* 輸入參數: 無
* 輸出參數: 無
* 返 回 值: 無
* 其它說明: 無
* 修改日期 版本號 修改人 修改內容
* ----------------------------------------------------------------------
* 20151008 V1.0 Zhou Zhaoxiong 創建
************************************************************************/
void PrintInfo(void)
{
UINT32 iLoopFlag = 0;
UINT32 iSum = 0;
UINT32 iLen = 0;
UINT8 *pCtrStr = "hello, world!"; // 修改了這行代碼

iLen = strlen(pCtrStr);

for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++) // 打印消息iLen次
{
printf("PrintInfo: hello, world!\n");

iSum = iSum + iLoopFlag;

Sleep(10 * 1000); // 每10s打印一次
}

return;
}


/**********************************************************************
* 功能描述: 程序休眠
* 輸入參數: iCountMs-休眠時間(單位:ms)
* 輸出參數: 無
* 返 回 值: 無
* 其它說明: 無
* 修改日期 版本號 修改人 修改內容
* ------------------------------------------------------------------
* 20151008 V1.0 Zhou Zhaoxiong 創建
********************************************************************/
void Sleep(UINT32 iCountMs)
{
struct timeval t_timeout = {0};

if (iCountMs < 1000)
{
t_timeout.tv_sec = 0;
t_timeout.tv_usec = iCountMs * 1000;
}
else
{
t_timeout.tv_sec = iCountMs / 1000;
t_timeout.tv_usec = (iCountMs % 1000) * 1000;
}
select(0, NULL, NULL, NULL, &t_timeout); // 調用select函數阻塞程序
}


編譯並運行之後,程序正常,說明問題已被我們解決掉。下面是常見的gdb命令的操作示例:

~/zhouzhaoxiong/zzx/GdbDebug> gdb GdbDebug    -- 啟動gdb調試
GNU gdb (GDB) SUSE (7.3-0.6.1)
Copyright (C) 2011 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-suse-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug...done.
(gdb) b 64 -- 在GdbDebug.c文件第64行設立斷點
Breakpoint 1 at 0x400611: file GdbDebug.c, line 64.
(gdb) b 72 -- 在GdbDebug.c文件第72行設立斷點
Breakpoint 2 at 0x400637: file GdbDebug.c, line 72.
(gdb) info b -- 顯示斷點信息
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000400611 in PrintInfo at GdbDebug.c:64
2 breakpoint keep y 0x0000000000400637 in PrintInfo at GdbDebug.c:72
(gdb) r -- 運行GdbDebug
Starting program: /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug

Breakpoint 1, PrintInfo () at GdbDebug.c:64
64 iLen = strlen(pCtrStr);
(gdb) p iLen -- 打印(輸出)iLen的值
$1 = 0
(gdb) n -- 執行下一步
66 for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++) // 打印消息iLen次
(gdb) n -- 執行下一步
68 printf("PrintInfo: hello, world!\n");
(gdb) p iLoopFlag -- 打印(輸出)iLoopFlag的值
$2 = 0
(gdb) p iLen -- 打印(輸出)iLen的值
$3 = 13
(gdb) n -- 執行下一步
PrintInfo: hello, world! -- 程序的輸出結果
70 iSum = iSum + iLoopFlag;
(gdb) p iSum -- 打印(輸出)iSum的值
$4 = 0
(gdb) n -- 執行下一步

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72 Sleep(10 * 1000); // 每10s打印一次
(gdb) n
66 for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++) // 打印消息iLen次
(gdb) p iLoopFlag
$5 = 0
(gdb) n
68 printf("PrintInfo: hello, world!\n");
(gdb) p iLoopFlag
$6 = 1
(gdb) n
PrintInfo: hello, world!
70 iSum = iSum + iLoopFlag;
(gdb) p iSum
$7 = 0
(gdb) n

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72 Sleep(10 * 1000); // 每10s打印一次
(gdb) p iSum
$8 = 1
(gdb) finish -- 一直運行到函數返回
Run till exit from #0 PrintInfo () at GdbDebug.c:72
PrintInfo: hello, world!

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72 Sleep(10 * 1000); // 每10s打印一次
(gdb) c -- 繼續執行
Continuing.
PrintInfo: hello, world!

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72 Sleep(10 * 1000); // 每10s打印一次
(gdb) bt -- 打印當前的函數調用棧的所有信息
#0 PrintInfo () at GdbDebug.c:72
#1 0x00000000004005e5 in main () at GdbDebug.c:41
(gdb) q -- 退出gdb
A debugging session is active.

Inferior 1 [process 26685] will be killed.

Quit anyway? (y or n) y
~/zhouzhaoxiong/zzx/GdbDebug>


作為Linux下調試C/C++程序的工具,大家一定要熟練掌握gdb的用法。


總結
Linux具有免費、可靠、安全、穩定、多平台等特點,因此深受全球各大IT廠商的追捧。Linux操作系統的兩大主要應用領域是服務器領域和嵌入式Linux系統。不管你從事的開發工作是否與Linux有關,掌握Linux下的軟件開發方法總是有好處的。

Copyright © Linux教程網 All Rights Reserved