Linux的發行版中包含了很多軟件開發工具. 它們中的很多是用于 C 和 C++應用程序開發的. 本文介紹了在 Linux 下能用于 C 應用程序開發和調試的工具. 本文的主旨是介紹如何在 Linux 下使用 C 編譯器和其他 C 編程工具, 而非 C 語言編程的教程. 在本文中你將學到以下知識:
Linux 上可用的 C 編譯器是 GNU C 編譯器, 它建立在自由軟件基金會的編程許可證的基礎上, 因此可以自由發布. 你能在 Linux 的發行光盤上找到它.
gcc [options] [filenames]命令行選項指定的操作將在命令行上每個給出的文件上執行. 下一小節將敘述一些你會最常用到的選項.
gcc -p -g test.c gcc -pg test.c第一條命令告訴 GCC 編譯 test.c 時為 prof 命令建立剖析(profile)信息并且把調試信息加入到可執行的文件里. 第二條命令只告訴 GCC 為 gprof 命令建立剖析信息.
gcc test.c你能用 -o 編譯選項來為將產生的可執行文件指定一個文件名來代替 a.out. 例如, 將一個叫 count.c 的 C 程序編譯為名叫 count 的可執行文件, 你將輸入下面的命令:
gcc -o count count.c
-S 編譯選項告訴 GCC 在為 C 代碼產生了匯編語言文件后停止編譯. GCC 產生的匯編語言文件的缺省擴展名是 .s . -E 選項指示編譯器僅對輸入文件進行預處理. 當這個選項被使用時, 預處理器的輸出被送到標準輸出而不是儲存在文件里.
-O 選項告訴 GCC 對源代碼進行基本優化. 這些優化在大多數情況下都會使程序執行的更快. -O2 選項告訴 GCC 產生盡可能小和盡可能快的代碼. -O2 選項將使編譯的速度比使用 -O 時慢. 但通常產生的代碼執行速度會更快.
除了 -O 和 -O2 優化選項外, 還有一些低級選項用于產生更快的代碼. 這些選項非常的特殊, 而且最好只有當你完全理解這些選項將會對編譯后的代碼產生什么樣的效果時再去使用. 這些選項的詳細描述, 請參考 GCC 的指南頁, 在命令行上鍵入 man gcc .
GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.14 (i486-slakware-linux), Copyright 1995 Free Software Foundation, Inc. (gdb)當你啟動 gdb 后, 你能在命令行上指定很多的選項. 你也可以以下面的方式來運行 gdb :
gdb <fname>當你用這種方式運行 gdb , 你能直接指定想要調試的程序. 這將告訴gdb 裝入名為 fname 的可執行文件. 你也可以用 gdb 去檢查一個因程序異常終止而產生的 core 文件, 或者與一個正在運行的程序相連. 你可以參考 gdb 指南頁或在命令行上鍵入 gdb -h 得到一個有關這些選項的說明的簡單列表.
在編譯時用 -g 選項打開調試選項.
命 令 | 描 述 |
file | 裝入想要調試的可執行文件. |
kill | 終止正在調試的程序. |
list | 列出產生執行文件的源代碼的一部分. |
next | 執行一行源代碼但不進入函數內部. |
step | 執行一行源代碼而且進入函數內部. |
run | 執行當前被調試的程序 |
quit | 終止 gdb |
watch | 使你能監視一個變量的值而不管它何時被改變. |
break | 在代碼里設置斷點, 這將使程序執行到這里時被掛起. |
make | 使你能不退出 gdb 就可以重新產生可執行文件. |
shell | 使你能不離開 gdb 就執行 UNIX shell 命令. |
gdb 支持很多與 UNIX shell 程序一樣的命令編輯特征. 你能象在 bash 或 tcsh里那樣按 Tab 鍵讓 gdb 幫你補齊一個唯一的命令, 如果不唯一的話 gdb 會列出所有匹配的命令. 你也能用光標鍵上下翻動歷史命令.
#include <stdio.h> main () { char my_string[] = "hello there"; my_print (my_string); my_print2 (my_string); } void my_print (char *string) { printf ("The string is %s\n", string); } void my_print2 (char *string) { char *string2; int size, i; size = strlen (string); string2 = (char *) malloc (size + 1); for (i = 0; i < size; i++) string2[size - i] = string[i]; string2[size+1] = `'; printf ("The string printed backward is %s\n", string2); }用下面的命令編譯它:
gcc -o test test.c這個程序執行時顯示如下結果:
The string is hello there The string printed backward is輸出的第一行是正確的, 但第二行打印出的東西并不是我們所期望的. 我們所設想的輸出應該是:
The string printed backward is ereht olleh由于某些原因, my_print2 函數沒有正常工作. 讓我們用 gdb 看看問題究竟出在哪兒, 先鍵入如下命令:
gdb greeting
(gdb) file greeting這個命令將載入 greeting 可執行文件就象你在 gdb 命令行里裝入它一樣.
這時你能用 gdb 的 run 命令來運行 greeting 了. 當它在 gdb 里被運行后結果大約會象這樣:
(gdb) run Starting program: /root/greeting The string is hello there The string printed backward is Program exited with code 041這個輸出和在 gdb 外面運行的結果一樣. 問題是, 為什么反序打印沒有工作? 為了找出癥結所在, 我們可以在 my_print2 函數的 for 語句后設一個斷點, 具體的做法是在 gdb 提示符下鍵入 list 命令三次, 列出源代碼:
(gdb) list (gdb) list (gdb) list
1 #include <stdio.h> 2 3 main () 4 { 5 char my_string[] = "hello there"; 6 7 my_print (my_string); 8 my_print2 (my_string); 9 } 10如果按下回車, gdb 將再執行一次 list 命令, 給出下列輸出:
11 my_print (char *string) 12 { 13 printf ("The string is %s\n", string); 14 } 15 16 my_print2 (char *string) 17 { 18 char *string2; 19 int size, i; 20再按一次回車將列出 greeting 程序的剩余部分:
21 size = strlen (string); 22 string2 = (char *) malloc (size + 1); 23 for (i = 0; i < size; i++) 24 string2[size - i] = string[i]; 25 string2[size+1] = `'; 26 printf ("The string printed backward is %s\n", string2); 27 }根據列出的源程序, 你能看到要設斷點的地方在第24行, 在 gdb 命令行提示符下鍵入如下命令設置斷點:
(gdb) break 24gdb 將作出如下的響應:
Breakpoint 1 at 0x139: file greeting.c, line 24 (gdb)
Starting program: /root/greeting The string is hello there Breakpoint 1, my_print2 (string = 0xbfffdc4 "hello there") at greeting.c :24 24 string2[size-i]=string[i]你能通過設置一個觀察 string2[size - i] 變量的值的觀察點來看出錯誤是怎樣產生的, 做法是鍵入:
(gdb) watch string2[size - i]gdb 將作出如下回應:
Watchpoint 2: string2[size - i]現在可以用 next 命令來一步步的執行 for 循環了:
(gdb) next經過第一次循環后, gdb 告訴我們 string2[size - i] 的值是 `h`. gdb 用如下的顯示來告訴你這個信息:
Watchpoint 2, string2[size - i] Old value = 0 `0' New value = 104 `h' my_print2(string = 0xbfffdc4 "hello there") at greeting.c:23 23 for (i=0; i<size; i++)這個值正是期望的. 后來的數次循環的結果都是正確的. 當 i=10 時, 表達式 string2[size - i] 的值等于 `e`, size - i 的值等于 1, 最后一個字符已經拷到新串里了.
如果你再把循環執行下去, 你會看到已經沒有值分配給 string2[0] 了, 而它是新串的第一個字符, 因為 malloc 函數在分配內存時把它們初始化為空(null)字符. 所以 string2 的第一個字符是空字符. 這解釋了為什么在打印 string2 時沒有任何輸出了.
現在找出了問題出在哪里, 修正這個錯誤是很容易的. 你得把代碼里寫入 string2 的第一個字符的的偏移量改為 size - 1 而不是 size. 這是因為 string2 的大小為 12, 但起始偏移量是 0, 串內的字符從偏移量 0 到 偏移量 10, 偏移量 11 為空字符保留.
為了使代碼正常工作有很多種修改辦法. 一種是另設一個比串的實際大小小 1 的變量. 這是這種解決辦法的代碼:
#include <stdio.h> main () { char my_string[] = "hello there"; my_print (my_string); my_print2 (my_string); } my_print (char *string) { printf ("The string is %s\n", string); } my_print2 (char *string) { char *string2; int size, size2, i; size = strlen (string); size2 = size -1; string2 = (char *) malloc (size + 1); for (i = 0; i < size; i++) string2[size2 - i] = string[i]; string2[size] = `'; printf ("The string printed backward is %s\n", string2); }
xxgdb你能用 gdb 里任何有效的命令行選項來初始化 xxgdb . 此外 xxgdb 也有一些特有的命令行選項, 表 27.2 列出了這些選項.
選 項 | 描 述 |
db_name | 指定所用調試器的名字, 缺省是 gdb. |
db_prompt | 指定調試器提示符, 缺省為 gdb. |
gdbinit | |
nx | 告訴 xxgdb 不執行 .gdbinit 文件. |
bigicon | 使用大圖標. |
/pub/Linux/devel/lang/c/calls.tar.Z
來取得 calls , 一些舊版本的 Linux CD-ROM 發行版里也附帶有. 因為它是一個有用的工具, 我們在這里也介紹一下. 如果你覺得有用的話, 從 BBS, FTP, 或另一張CD-ROM 上弄一個拷貝. calls 調用 GCC 的預處理器來處理給出的源程序文件, 然后輸出這些文件的里的函數調用樹圖.
main [test.c]如果函數并不是向 calls 給出的文件里的, calls 不知道所調用的函數來自哪里, 則只顯示函數的名字:
printfcalls 不對遞歸和靜態函數輸出. 遞歸函數顯示成下面的樣子:
fact <<< recursive in factorial.c >>>靜態函數象這樣顯示:
total [static in calculate.c]作為一個例子, 假設用 calls 處理下面的程序:
#include <stdio.h> main () { char my_string[] = "hello there"; my_print (my_string); my_print2(my_string); } my_print (char *string) { printf ("The string is %s\n", string); } my_print2 (char *string) { char *string2; int size, size2, i; size = strlen (string); size2 = size -1; string2 = (char *) malloc (size + 1); for (i = 0; i < size; i++) string2[size2 - i] = string[i]; string2[size] = `'; printf ("The string printed backward is %s\n", string2); }將產生如下的輸出:
1 main [test.c] 2 my_print [test.c] 3 printf 4 my_print2 [test.c] 5 strlen 6 malloc 7 printfcalls 有很多命令行選項來設置不同的輸出格式, 有關這些選項的更多信息請參考 calls 的指南頁. 方法是在命令行上鍵入 calls -h .
#include <stdio.h> main () { char my_string[] = "hello there"; my_print (my_string); my_print2(my_string); } my_print (char *string) { printf ("The string is %s\n", *string); } my_print2 (char *string) { char *string2; int size, size2, i; size = strlen (string); size2 = size -1; string2 = (char *) malloc (size + 1); for (i = 0; i < size; i++) string2[size2 - i] = string[i]; string2[size] = `'; printf ("The string printed backward is %s\n", string2); }你將得到下面的輸出:
/* test.c */ int main(void); int my_print(char *string); int my_print2(char *string);這個輸出可以重定向到一個定義函數原型的包含文件里.
下面的例子是 indent 的缺省輸出:
運行 indent 以前的 C 代碼:
#include <stdio.h> main () { char my_string[] = "hello there"; my_print (my_string); my_print2(my_string); } my_print (char *string) { printf ("The string is %s\n", *string); } my_print2 (char *string) { char *string2; int size, size2, i; size = strlen (string); size2 = size -1; string2 = (char *) malloc (size + 1); for (i = 0; i < size; i++) string2[size2 - i] = string[i]; string2[size] = `'; printf ("The string printed backward is %s\n", string2); }運行 indent 后的 C 代碼:
#include <stdio.h> main () { char my_string[] = "hello there"; my_print (my_string); my_print2 (my_string); } my_print (char *string) { printf ("The string is %s\n", *string); } my_print2 (char *string) { char *string2; int size, size2, i; size = strlen (string); size2 = size -1; string2 = (char *) malloc (size + 1); for (i = 0; i < size; i++) string2[size2 - i] = string[i]; string2[size] = `'; printf ("The string printed backward is %s\n", string2); }indent 并不改變代碼的實質內容, 而只是改變代碼的外觀. 使它變得更可讀, 這永遠是一件好事.
gprof 將告訴你程序里每個函數被調用的次數和每個函數執行時所占時間的百分比. 你如果想提高你的程序性能的話這些信息非常有用.
為了在你的程序上使用 gprof, 你必須在編譯程序時加上 -pg 選項. 這將使程序在每次執行時產生一個叫 gmon.out 的文件. gprof 用這個文件產生剖析信息.
在你運行了你的程序并產生了 gmon.out 文件后你能用下面的命令獲得剖析信息:
gprof <program_name>參數 program_name 是產生 gmon.out 文件的程序的名字.
如果你有一些用 FORTRAN 或 Pascal 寫的代碼要用 C 重寫的話, f2c 和 p2c 對你非常有用. 這兩個程序產生的 C 代碼一般不用修改就直接能被 GCC 編譯.
如果要轉換的 FORTRAN 或 Pascal 程序比較小的話可以直接使用 f2c 或 p2c 不用加任何選項. 如果要轉換的程序比較龐大, 包含很多文件的話你可能要用到一些命令行選項.
在一個 FORTRAN 程序上使用 f2c , 輸入下面的命令:
f2c my_fortranprog.f
p2c my_pascalprogram.pas這兩個程序產生的 C 源代碼的文件名都和原來的文件名相同, 但擴展名由 .f 或 .pas 變為 .c.
原文轉自:http://www.anti-gravitydesign.com