亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

GDB教程詳解

2024-06-28 14:32:39
字體:
來源:轉載
供稿:網友

  GDB是GNU開源組織發布的一個強大的UNIX下的程序調試工具。或許,各位比較喜歡那種圖形界面方式的,像VC、BCB等IDE的調試,但如果你是在UNIX平臺下做軟件,你會發現GDB這個調試工具有比VC、BCB的圖形化調試器更強大的功能。所謂“寸有所長,尺有所短”就是這個道理。 

一般來說,GDB主要幫忙你完成下面四個方面的功能:

    A.啟動你的程序,可以按照你的自定義的要求隨心所欲的運行程序。
    B.可讓被調試的程序在你所指定的調置的斷點處停住。(斷點可以是條件表達式)
    C.當程序被停住時,可以檢查此時你的程序中所發生的事。
    D.動態的改變你程序的執行環境。

  從上面看來,GDB和一般的調試工具沒有什么兩樣,基本上也是完成這些功能,不過在細節上,你會發現GDB這個調試工具的強大,大家可能比較習慣了圖形化的調試工具,但有時候,命令行的調試工具卻有著圖形化工具所不能完成的功能。讓我們一一看來。

1.一個調試示例

  源程序:tst.c

 1 #include <stdio.h> 2 3 int func(int n) 4 { 5       int sum=0,i; 6       for(i=0; i<n; i++) 7      { 8            sum+=i; 9      }10      return sum;11 }1213 main()14 {15      int i;16      long result = 0;17      for(i=1; i<=100; i++)18      {19           result += i;20      }2122      PRintf("result[1-100] = %d /n", result );23      printf("result[1-250] = %d /n", func(250) );24 }

   編譯生成執行文件:(linux下)

1 hchen/test> cc -g tst.c -o tst

   使用GDB調試:

 1 hchen/test> gdb tst                                           <---------- 啟動GDB 2 GNU gdb 5.1.1 3 Copyright 2002 Free Software Foundation, Inc. 4 GDB is free software, covered by the GNU General Public License, and you are 5 welcome to change it and/or distribute copies of it under certain conditions. 6 Type "show copying" to see the conditions. 7 There is absolutely no warranty for GDB. Type "show warranty" for details. 8 This GDB was configured as "i386-suse-linux"... 9 (gdb) l                                                   <-------------------- l命令相當于list,從第一行開始例出原碼。10 1        #include <stdio.h>11 212 3        int func(int n)13 4        {14 5                int sum=0,i;15 6                for(i=0; i<n; i++)16 7                {17 8                        sum+=i;18 9                }19 10               return sum;20 (gdb)                                                     <-------------------- 直接回車表示,重復上一次命令21 11       }22 1223 1324 14       main()25 15       {26 16               int i;27 17               long result = 0;28 18               for(i=1; i<=100; i++)29 19               {30 20                       result += i;   31 (gdb) break 16                                                <-------------------- 設置斷點,在源程序第16行處。32 Breakpoint 1 at 0x8048496: file tst.c, line 16.33 (gdb) break func                                            <-------------------- 設置斷點,在函數func()入口處。34 Breakpoint 2 at 0x8048456: file tst.c, line 5.35 (gdb) info break                                            <-------------------- 查看斷點信息。36 Num Type           Disp Enb Address    What37 1   breakpoint     keep y   0x08048496 in main at tst.c:1638 2   breakpoint     keep y   0x08048456 in func at tst.c:539 (gdb) r                                                     <--------------------- 運行程序,run命令簡寫40 Starting program: /home/hchen/test/tst4142 Breakpoint 1, main () at tst.c:17                                   <---------- 在斷點處停住。43 17               long result = 0;44 (gdb) n                                                     <--------------------- 單條語句執行,next命令簡寫。45 18               for(i=1; i<=100; i++)46 (gdb) n47 20                       result += i;48 (gdb) n49 18               for(i=1; i<=100; i++)50 (gdb) n51 20                       result += i;52 (gdb) c                                                    <--------------------- 繼續運行程序,continue命令簡寫。53 Continuing.54 result[1-100] = 5050                                           <----------程序輸出。55 56 Breakpoint 2, func (n=250) at tst.c:557 5                int sum=0,i;58 (gdb) n59 6                for(i=1; i<=n; i++)60 (gdb) p i                                                  <--------------------- 打印變量i的值,print命令簡寫。61 $1 = 13451380862 (gdb) n63 8                        sum+=i;64 (gdb) n65 6                for(i=1; i<=n; i++)66 (gdb) p sum67 $2 = 168 (gdb) n69 8                        sum+=i;70 (gdb) p i71 $3 = 272 (gdb) n73 6                for(i=1; i<=n; i++)74 (gdb) p sum75 $4 = 376 (gdb) bt                                                  <--------------------- 查看函數堆棧。77 #0 func (n=250) at tst.c:578 #1 0x080484e4 in main () at tst.c:2479 #2 0x400409ed in __libc_start_main () from /lib/libc.so.680 (gdb) finish                                               <--------------------- 退出函數。81 Run till exit from #0 func (n=250) at tst.c:582 0x080484e4 in main () at tst.c:2483 24              printf("result[1-250] = %d /n", func(250) );84 Value returned is $6 = 3137585 (gdb) c                                                 <--------------------- 繼續運行。86 Continuing.87 result[1-250] = 31375                                        <----------程序輸出。8889 Program exited with code 027.                                   <--------程序退出,調試結束。90 (gdb) q                                                 <--------------------- 退出gdb。91 hchen/test> 

  好了,有了以上的感性認識,還是讓我們來系統地認識一下gdb吧。

2.使用GDB

  一般來說GDB主要調試的是C/C++的程序。要調試C/C++的程序,首先在編譯時,我們必須要把調試信息加到可執行文件中。使用編譯器(cc/gcc/g++)的 -g 參數可以做到這一點。如:

1 > cc -g hello.c -o hello2 > g++ -g hello.cpp -o hello

   如果沒有-g,你將看不見程序的函數名、變量名,所代替的全是運行時的內存地址。當你用-g把調試信息加入之后,并成功編譯目標代碼以后,讓我們來看看如何用gdb來調試他。

  啟動GDB的方法有以下幾種:

    A.gdb <program>
       program也就是你的執行文件,一般在當然目錄下。

    B.gdb <program> core
       用gdb同時調試一個運行程序和core文件,core是程序非法執行后core dump后產生的文件。

    C.gdb <program> <PID>
       如果你的程序是一個服務程序,那么你可以指定這個服務程序運行時的進程ID。gdb會自動attach上去,并調試他。program應該在PATH環境變量中搜索得到。

  GDB啟動時,可以加上一些GDB的啟動開關,詳細的開關可以用gdb -help查看。我在下面只例舉一些比較常用的參數:

1 -symbols <file>2 -s <file>

    從指定文件中讀取符號表。

1 -se file

    從指定文件中讀取符號表信息,并把他用在可執行文件中。

1 -core <file>2 -c <file>

    調試時core dump的core文件。

1 -directory <directory>2 -d <directory>

    加入一個源文件的搜索路徑。默認搜索路徑是環境變量中PATH所定義的路徑。

3.GDB的命令概貌

  啟動gdb后,就你被帶入gdb的調試環境中,就可以使用gdb的命令開始調試程序了,gdb的命令可以使用help命令來查看,如下所示:

 1 /home/hchen> gdb 2     GNU gdb 5.1.1 3     Copyright 2002 Free Software Foundation, Inc. 4     GDB is free software, covered by the GNU General Public License, and you are 5 welcome to change it and/or distribute copies of it under certain conditions. 6     Type "show copying" to see the conditions. 7     There is absolutely no warranty for GDB. Type "show warranty" for details. 8     This GDB was configured as "i386-suse-linux". 9     (gdb) help10     List of classes of commands:1112     aliases -- Aliases of other commands13     breakpoints -- Making program stop at certain points14     data -- Examining data15     files -- Specifying and examining files16     internals -- Maintenance commands17     obscure -- Obscure features18     running -- Running the program19     stack -- Examining the stack20     status -- Status inquiries21     support -- Support facilities22     tracepoints -- Tracing of program execution without stopping the program23     user-defined -- User-defined commands2425     Type "help" followed by a class name for a list of commands in that class.26     Type "help" followed by command name for full documentation.27     Command name abbreviations are allowed if unambiguous.28     (gdb)

  gdb的命令很多,gdb把它分成許多個種類。help命令只是例出gdb的命令種類,如果要看種類中的命令,可以使用help <class> 命令,如:help breakpoints,查看設置斷點的所有命令。也可以直接help <command>來查看命令的幫助。

  gdb中,輸入命令時,可以不用打全命令,只用打命令的前幾個字符就可以了,當然,命令的前幾個字符應該要標志著一個唯一的命令,在Linux下,你可以敲擊兩次TAB鍵來補齊命令的全稱,如果有重復的,那么gdb會把其例出來。

    示例一:在進入函數func時,設置一個斷點??梢郧萌隻reak func,或是直接就是b func

1 (gdb) b func2 Breakpoint 1 at 0x8048458: file hello.c, line 10.

    示例二:敲入b按兩次TAB鍵,你會看到所有b打頭的命令:

1 (gdb) b2 backtrace break      bt3 (gdb)

    示例三:只記得函數的前綴,可以這樣:

1 (gdb) b make_ <按TAB鍵>

    (再按下一次TAB鍵,你會看到:)

1 make_a_section_from_file     make_environ2 make_abs_section             make_function_type3 make_blockvector             make_pointer_type4 make_cleanup                 make_reference_type5 make_command                 make_symbol_completion_list6 (gdb) b make_

    GDB把所有make開頭的函數全部例出來給你查看。

    示例四:調試C++的程序時,有可以函數名一樣。如:

1 (gdb) b 'bubble( M-?2 bubble(double,double)    bubble(int,int)3 (gdb) b 'bubble(

    你可以查看到C++中的所有的重載函數及參數。(注:M-?和“按兩次TAB鍵”是一個意思)

  要退出gdb時,只用發quit或命令簡稱q就行了。

4.GDB中運行UNIX的shell程序

  在gdb環境中,你可以執行UNIX的shell的命令,使用gdb的shell命令來完成:

1 shell <command string>

    調用UNIX的shell來執行<command string>,環境變量SHELL中定義的UNIX的shell將會被用來執行<command string>,如果SHELL沒有定義,那就使用UNIX的標準shell:/bin/sh。(在Windows中使用Command.com或cmd.exe)

  還有一個gdb命令是make:

1 make <make-args>
    可以在gdb中執行make命令來重新build自己的程序。這個命令等價于“shell make <make-args>”。

5.在GDB中運行程序

  當以gdb <program>方式啟動gdb后,gdb會在PATH路徑和當前目錄中搜索<program>的源文件。如要確認gdb是否讀到源文件,可使用l或list命令,看看gdb是否能列出源代碼。
  在gdb中,運行程序使用r或是run命令。程序的運行,你有可能需要設置下面四方面的事。

5.1 程序運行參數

   set args 可指定運行時參數。(如:set args 10 20 30 40 50)
  show args 命令可以查看設置好的運行參數。

5.2 運行環境

  path <dir> 可設定程序的運行路徑。
  show paths 查看程序的運行路徑。
  set environment varname [=value] 設置環境變量。如:set env USER=hchen
  show environment [varname] 查看環境變量

5.3 工作目錄

  cd <dir> 相當于shell的cd命令。
  pwd 顯示當前的所在目錄。

5.4 程序的輸入輸出

  info terminal 顯示你程序用到的終端的模式。
  使用重定向控制程序輸出。如:run > outfile
  tty命令可以指寫輸入輸出的終端設備。如:tty /dev/ttyb

6.調試已運行的程序

  兩種方法:
    A.在UNIX下用ps查看正在運行的程序的PID(進程ID),然后用gdb <program> PID格式掛接正在運行的程序。
    B.先用gdb <program>關聯上源代碼,并進行gdb,在gdb中用attach命令來掛接進程的PID。并用detach來取消掛接的進程。

7.暫停/恢復程序運行

  調試程序中,暫停程序運行是必須的,GDB可以方便地暫停程序的運行。你可以設置程序的在哪行停住,在什么條件下停住,在收到什么信號時停往等等。以便于你查看運行時的變量,以及運行時的流程。

  當進程被gdb停住時,你可以使用info program 來查看程序的是否在運行,進程號,被暫停的原因。

  在gdb中,我們可以有以下幾種暫停方式:斷點(BreakPoint)、觀察點(WatchPoint)、捕捉點(CatchPoint)、信號(Signals)、線程停止(Thread Stops)。如果要恢復程序運行,可以使用c或是continue命令。

7.1 設置斷點(BreakPoint)

     我們用break命令來設置斷點。正面有幾點設置斷點的方法:

1 break <function>

     在進入指定函數時停住。C++中可以使用class::function或function(type,type)格式來指定函數名。

1 break <linenum>

     在指定行號停住。

1 break +offset2 break -offset

    在當前行號的前面或后面的offset行停住。offiset為自然數。

1 break filename:linenum

    在源文件filename的linenum行處停住。

1 break filename:function

    在源文件filename的function函數的入口處停住。

1 break *address

    在程序運行的內存地址處停住。

1 break

    break命令沒有參數時,表示在下一條指令處停住。

1 break ... if <condition>

    ...可以是上述的參數,condition表示條件,在條件成立時停住。比如在循環境體中,可以設置break if i=100,表示當i為100時停住程序。

    查看斷點時,可使用info命令,如下所示:(注:n表示斷點號)

1 info breakpoints [n]2 info break [n] 

7.2 設置捕捉點(CatchPoint)

    你可設置捕捉點來補捉程序運行時的一些事件。如:載入共享庫(動態鏈接庫)或是C++的異常。設置捕捉點的格式為:

1 catch <event>

    當event發生時,停住程序。event可以是下面的內容:

        A.throw 一個C++拋出的異常。(throw為關鍵字)
        B.catch 一個C++捕捉到的異常。(catch為關鍵字)
        C.exec 調用系統調用exec時。(exec為關鍵字,目前此功能只在HP-UX下有用)
        D.fork 調用系統調用fork時。(fork為關鍵字,目前此功能只在HP-UX下有用)
        E.vfork 調用系統調用vfork時。(vfork為關鍵字,目前此功能只在HP-UX下有用)
        F.load 或 load <libname> 載入共享庫(動態鏈接庫)時。(load為關鍵字,目前此功能只在HP-UX下有用)
        G.unload 或 unload <libname> 卸載共享庫(動態鏈接庫)時。(unload為關鍵字,目前此功能只在HP-UX下有用)

1 tcatch <event>

       只設置一次捕捉點,當程序停住以后,應點被自動刪除。

7.3 維護停止點

  上面說了如何設置程序的停止點,GDB中的停止點也就是上述的三類。在GDB中,如果你覺得已定義好的停止點沒有用了,你可以使用delete、clear、disable、enable這幾個命令來進行維護。

1 clear

    清除所有的已定義的停止點。

1 clear <function>2 clear <filename:function>

    清除所有設置在函數上的停止點。

1 clear <linenum>2 clear <filename:linenum>

    清除所有設置在指定行上的停止點。

1 delete [breakpoints] [range...]

    刪除指定的斷點,breakpoints為斷點號。如果不指定斷點號,則表示刪除所有的斷點。range表示斷點號的范圍(如:3-7)。其簡寫命令為d。

  比刪除更好的一種方法是disable停止點,disable了的停止點,GDB不會刪除,當你還需要時,enable即可,就好像回收站一樣。

1 disable [breakpoints] [range...]

    disable所指定的停止點,breakpoints為停止點號。如果什么都不指定,表示disable所有的停止點。簡寫命令是dis. 

1 enable [breakpoints] [range...]

    enable所指定的停止點,breakpoints為停止點號。

1 enable [breakpoints] once range...

   enable所指定的停止點一次,當程序停止后,該停止點馬上被GDB自動disable。

1 enable [breakpoints] delete range...

   enable所指定的停止點一次,當程序停止后,該停止點馬上被GDB自動刪除。

7.4 停止條件維護

  前面在說到設置斷點時,我們提到過可以設置一個條件,當條件成立時,程序自動停止,這是一個非常強大的功能,這里,我想專門說說這個條件的相關維護命令。一般來說,為斷點設置一個條件,我們使用if關鍵詞,后面跟其斷點條件。并且,條件設置好后,我們可以用condition命令來修改斷點的條件。(只有break和watch命令支持if,catch目前暫不支持if)

1 condition <bnum> <expression>

    修改斷點號為bnum的停止條件為expression。

1 condition <bnum>

    清除斷點號為bnum的停止條件。

  還有一個比較特殊的維護命令ignore,你可以指定程序運行時,忽略停止條件幾次。

1 ignore <bnum> <count>

    表示忽略斷點號為bnum的停止條件count次。

7.5 為停止點設定運行命令

  我們可以使用GDB提供的command命令來設置停止點的運行命令。也就是說,當運行的程序在被停止住時,我們可以讓其自動運行一些別的命令,這很有利行自動化調試。對基于GDB的自動化調試是一個強大的支持。

1 commands [bnum]2 ... command-list ...3 end

    為斷點號bnum指寫一個命令列表。當程序被該斷點停住時,gdb會依次運行命令列表中的命令。

    例如:

1 break foo if x>02 commands3 printf "x is %d/n",x4 continue5 end

     斷點設置在函數foo中,斷點條件是x>0,如果程序被斷住后,也就是,一旦x的值在foo函數中大于0,GDB會自動打印出x的值,并繼續運行程序。

  如果你要清除斷點上的命令序列,那么只要簡單的執行一下commands命令,并直接在打個end就行了。

7.6 斷點菜單

  在C++中,可能會重復出現同一個名字的函數若干次(函數重載),在這種情況下,break <function>不能告訴GDB要停在哪個函數的入口。當然,你可以使用break <function(type)>也就是把函數的參數類型告訴GDB,以指定一個函數。否則的話,GDB會給你列出一個斷點菜單供你選擇你所需要的斷點。你只要輸入你菜單列表中的編號就可以了。如:    

 1 (gdb) b String::after 2 [0] cancel 3 [1] all 4 [2] file:String.cc; line number:867 5 [3] file:String.cc; line number:860 6 [4] file:String.cc; line number:875 7 [5] file:String.cc; line number:853 8 [6] file:String.cc; line number:846 9 [7] file:String.cc; line number:73510 > 2 4 611 Breakpoint 1 at 0xb26c: file String.cc, line 867.12 Breakpoint 2 at 0xb344: file String.cc, line 875.13 Breakpoint 3 at 0xafcc: file String.cc, line 846.14 Multiple breakpoints were set.15 Use the "delete" command to delete unwanted16 breakpoints.17 (gdb)

  可見,GDB列出了所有after的重載函數,你可以選一下列表編號就行了。0表示放棄設置斷點,1表示所有函數都設置斷點。

7.7 恢復程序運行和單步調試

  當程序被停住了,你可以用continue命令恢復程序的運行直到程序結束,或下一個斷點到來。也可以使用step或next命令單步跟蹤程序。

1 continue [ignore-count]2 c [ignore-count]3 fg [ignore-count]

    恢復程序運行,直到程序結束,或是下一個斷點到來。ignore-count表示忽略其后的斷點次數。continue,c,fg三個命令都是一樣的意思。

1 step <count>

    單步跟蹤,如果有函數調用,他會進入該函數。進入函數的前提是,此函數被編譯有debug信息。很像VC等工具中的step in。后面可以加count也可以不加,不加表示一條條地執行,加表示執行后面的count條指令,然后再停住。

1 next <count>

   同樣單步跟蹤,如果有函數調用,他不會進入該函數。很像VC等工具中的step over。后面可以加count也可以不加,不加表示一條條地執行,加表示執行后面的count條指令,然后再停住。

1 set step-mode2 set step-mode on

   打開step-mode模式,于是,在進行單步跟蹤時,程序不會因為沒有debug信息而不停住。這個參數有很利于查看機器碼。

1 set step-mod off

   關閉step-mode模式。

1 finish

    運行程序,直到當前函數完成返回。并打印函數返回時的堆棧地址和返回值及參數值等信息。

1 until 或 u

    當你厭倦了在一個循環體內單步跟蹤時,這個命令可以運行程序直到退出循環體。

1 stepi 或 si2 nexti 或 ni

    單步跟蹤一條機器指令!一條程序代碼有可能由數條機器指令完成,stepi和nexti可以單步執行機器指令。與之一樣有相同功能的命令是“display/i $pc” ,當運行完這個命令后,單步跟蹤會在打出程序代碼的同時打出機器指令(也就是匯編代碼)

7.8 信號(Signals)

  信號是一種軟中斷,是一種處理異步事件的方法。一般來說,操作系統都支持許多信號。尤其是UNIX,比較重要應用程序一般都會處理信號。UNIX定義了許多信號,比如SIGINT表示中斷字符信號,也就是Ctrl+C的信號,SIGBUS表示硬件故障的信號;SIGCHLD表示子進程狀態改變信號;SIGKILL表示終止程序運行的信號,等等。信號量編程是UNIX下非常重要的一種技術。

  GDB有能力在你調試程序的時候處理任何一種信號,你可以告訴GDB需要處理哪一種信號。你可以要求GDB收到你所指定的信號時,馬上停住正在運行的程序,以供你進行調試。你可以用GDB的handle命令來完成這一功能。

1 handle <signal> <keyWords...>

      在GDB中定義一個信號處理。信號<signal>可以以SIG開頭或不以SIG開頭,可以用定義一個要處理信號的范圍(如:SIGIO-SIGKILL,表示處理從SIGIO信號到SIGKILL的信號,其中包括SIGIO,SIGIOT,SIGKILL三個信號),也可以使用關鍵字all來標明要處理所有的信號。一旦被調試的程序接收到信號,運行程序馬上會被GDB停住,以供調試。其<keywords>可以是以下幾種關鍵字的一個或多個。

1 nostop

      當被調試的程序收到信號時,GDB不會停住程序的運行,但會打出消息告訴你收到這種信號。

1 stop

     當被調試的程序收到信號時,GDB會停住你的程序。

1 print

      當被調試的程序收到信號時,GDB會顯示出一條信息。

1 noprint

     當被調試的程序收到信號時,GDB不會告訴你收到信號的信息。

1 pass2 noignore

    當被調試的程序收到信號時,GDB不處理信號。這表示,GDB會把這個信號交給被調試程序會處理。

1 nopass2 ignore

    當被調試的程序收到信號時,GDB不會讓被調試程序來處理這個信號。

1 info signals2 info handle

    查看有哪些信號在被GDB檢測中。

7.9 線程(Thread Stops)

  如果你程序是多線程的話,你可以定義你的斷點是否在所有的線程上,或是在某個特定的線程。GDB很容易幫你完成這一工作。

1 break <linespec> thread <threadno>2 break <linespec> thread <threadno> if ...

     linespec指定了斷點設置在的源程序的行號。threadno指定了線程的ID,注意,這個ID是GDB分配的,你可以通過“info threads”命令來查看正在運行程序中的線程信息。如果你不指定thread <threadno>則表示你的斷點設在所有線程上面。你還可以為某線程指定斷點條件。如:     

1  (gdb) break frik.c:13 thread 28 if bartab > lim

    當你的程序被GDB停住時,所有的運行線程都會被停住。這方便你你查看運行程序的總體情況。而在你恢復程序運行時,所有的線程也會被恢復運行。那怕是主進程在被單步調試時。

8.查看棧信息

  當程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的。當你的程序調用了一個函數,函數的地址,函數參數,函數內的局部變量都會被壓入“棧”(Stack)中。你可以用GDB命令來查看當前的棧中的信息。

  下面是一些查看函數調用棧信息的GDB命令:

1 backtrace2 bt

     打印當前的函數調用棧的所有信息。如:

1 (gdb) bt2 #0 func (n=250) at tst.c:63 #1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:304 #2 0x400409ed in __libc_start_main () from /lib/libc.so.6

    從上可以看出函數的調用棧信息:__libc_start_main --> main() --> func() 

1 backtrace <n>2 bt <n>

    n是一個正整數,表示只打印棧頂上n層的棧信息。

1 backtrace <-n>2 bt <-n>

    -n表一個負整數,表示只打印棧底下n層的棧信息。

  如果你要查看某一層的信息,你需要在切換當前的棧,一般來說,程序停止時,最頂層的棧就是當前棧,如果你要查看棧下面層的詳細信息,首先要做的是切換當前棧。

1 frame <n>2 f <n>

    n是一個從0開始的整數,是棧中的層編號。比如:frame 0,表示棧頂,frame 1,表示棧的第二層。

1 up <n>

    表示向棧的上面移動n層,可以不打n,表示向上移動一層。

1 down <n>

    表示向棧的下面移動n層,可以不打n,表示向下移動一層。

    上面的命令,都會打印出移動到的棧層的信息。如果你不想讓其打出信息。你可以使用這三個命令:

1 select-frame <n> 對應于 frame 命令。2 up-silently <n> 對應于 up 命令。3 down-silently <n> 對應于 down 命令。

  查看當前棧層的信息,你可以用以下GDB命令: 

1 frame 或 f

     會打印出這些信息:棧的層編號,當前的函數名,函數參數值,函數所在文件及行號,函數執行到的語句。

1 info frame2 info f

     這個命令會打印出更為詳細的當前棧層的信息,只不過,大多數都是運行時的內內地址。比如:函數地址,調用函數的地址,被調用函數的地址,目前的函數是由什么樣的程序語言寫成的、函數參數地址及值、局部變量的地址等等。如:

1 (gdb) info f2 Stack level 0, frame at 0xbffff5d4:3 eip = 0x804845d in func (tst.c:6); saved eip 0x80485244 called by frame at 0xbffff60c5 source language c.6 Arglist at 0xbffff5d4, args: n=2507 Locals at 0xbffff5d4, Previous frame's sp is 0x08 Saved registers:9 ebp at 0xbffff5d4, eip at 0xbffff5d8

    info args

        打印出當前函數的參數名及其值。
    info locals
        打印出當前函數中所有局部變量及其值。
    info catch
        打印出當前的函數中的異常處理信息。

9.查看源程序

9.1 顯示源代碼

    GDB 可以打印出所調試程序的源代碼,當然,在程序編譯時一定要加上-g的參數,把源程序信息編譯到執行文件中。不然就看不到源程序了。當程序停下來以后,GDB會報告程序停在了那個文件的第幾行上。你可以用list命令來打印程序的源代碼。還是來看一看查看源代碼的GDB命令吧。

1 list <linenum>

       顯示程序第linenum行的周圍的源程序。

1 list <function>

       顯示函數名為function的函數的源程序。

1 list

       顯示當前行后面的源程序。

1 list -

      顯示當前行前面的源程序。

  一般是打印當前行的上5行和下5行,如果顯示函數是是上2行下8行,默認是10行,當然,你也可以定制顯示的范圍,使用下面命令可以設置一次顯示源程序的行數。

1 set listsize <count>

     設置一次顯示源代碼的行數。

1 show listsize

     查看當前listsize的設置。

  list命令還有下面的用法:

1 list <first>, <last>

     顯示從first行到last行之間的源代碼。

1 list , <last>

    顯示從當前行到last行之間的源代碼。

1 list +

    往后顯示源代碼。

  一般來說在list后面可以跟以下這們的參數:

1 <linenum>   行號。2 <+offset>   當前行號的正偏移量。3 <-offset>   當前行號的負偏移量。4 <filename:linenum> 哪個文件的哪一行。5 <function> 函數名。6 <filename:function> 哪個文件中的哪個函數。7 <*address> 程序運行時的語句在內存中的地址。

9.2 搜索源代碼

  不僅如此,GDB還提供了源代碼搜索的命令:

1 forward-search <regexp>2 search <regexp>

     向前面搜索。

1 reverse-search <regexp>

    全部搜索。

  其中,<regexp>就是正則表達式,也主一個字符串的匹配模式,關于正則表達式,我就不在這里講了,還請各位查看相關資料。

9.3 指定源文件的路徑

 某些時候,用-g編譯過后的執行程序中只是包括了源文件的名字,沒有路徑名。GDB提供了可以讓你指定源文件的路徑的命令,以便GDB進行搜索。

1 directory <dirname ... >2 dir <dirname ... >

    加一個源文件路徑到當前路徑的前面。如果你要指定多個路徑,UNIX下你可以使用“:”,Windows下你可以使用“;”。

1 directory

    清除所有的自定義的源文件搜索路徑信息。

1 show directories

    顯示定義了的源文件搜索路徑。

9.4 源代碼的內存

  你可以使用info line命令來查看源代碼在內存中的地址。info line后面可以跟“行號”,“函數名”,“文件名:行號”,“文件名:函數名”,這個命令會打印出所指定的源碼在運行時的內存地址,如:

1 (gdb) info line tst.c:func2 Line 5 of "tst.c" starts at address 0x8048456 <func+6> and ends at 0x804845d <func+13>.

 還有一個命令(disassemble)你可以查看源程序的當前執行時的機器碼,這個命令會把目前內存中的指令dump出來。如下面的示例表示查看函數func的匯編代碼。

 1 (gdb) disassemble func 2 Dump of assembler code for function func: 3 0x8048450 <func>:       push   %ebp 4 0x8048451 <func+1>:     mov    %esp,%ebp 5 0x8048453 <func+3>:     sub    $0x18,%esp 6 0x8048456 <func+6>:     movl   $0x0,0xfffffffc(%ebp) 7 0x804845d <func+13>:    movl   $0x1,0xfffffff8(%ebp) 8 0x8048464 <func+20>:    mov    0xfffffff8(%ebp),%eax 9 0x8048467 <func+23>:    cmp    0x8(%ebp),%eax10 0x804846a <func+26>:    jle    0x8048470 <func+32>11 0x804846c <func+28>:    jmp    0x8048480 <func+48>12 0x804846e <func+30>:    mov    %esi,%esi13 0x8048470 <func+32>:    mov    0xfffffff8(%ebp),%eax14 0x8048473 <func+35>:    add    %eax,0xfffffffc(%ebp)15 0x8048476 <func+38>:    incl   0xfffffff8(%ebp)16 0x8048479 <func+41>:    jmp    0x8048464 <func+20>17 0x804847b <func+43>:    nop18 0x804847c <func+44>:    lea    0x0(%esi,1),%esi19 0x8048480 <func+48>:    mov    0xfffffffc(%ebp),%edx20 0x8048483 <func+51>:    mov    %edx,%eax21 0x8048485 <func+53>:    jmp    0x8048487 <func+55>22 0x8048487 <func+55>:    mov    %ebp,%esp23 0x8048489 <func+57>:    pop    %ebp24 0x804848a <func+58>:    ret25 End of assembler dump.

10.查看運行時數據

   在你調試程序時,當程序被停住時,你可以使用print命令(簡寫命令為p),或是同義命令inspect來查看當前程序的運行數據。print命令的格式是: 

1 print <expr>2 print /<f> <expr>

  <expr>是表達式,是你所調試的程序的語言的表達式(GDB可以調試多種編程語言),<f>是輸出的格式,比如,如果要把表達式按16進制的格式輸出,那么就是/x。

10.1 表達式

  print和許多GDB的命令一樣,可以接受一個表達式,GDB會根據當前的程序運行的數據來計算這個表達式,既然是表達式,那么就可以是當前程序運行中的const常量、變量、函數等內容??上У氖荊DB不能使用你在程序中所定義的宏。
  表達式的語法應該是當前所調試的語言的語法,由于C/C++是一種大眾型的語言,所以,本文中的例子都是關于C/C++的。(而關于用GDB調試其它語言的章節,我將在后面介紹)
  在表達式中,有幾種GDB所支持的操作符,它們可以用在任何一種語言中
  @
    是一個和數組有關的操作符,在后面會有更詳細的說明。
  ::
    指定一個在文件或是一個函數中的變量。

1 {<type>} <addr>

  表示一個指向內存地址<addr>的類型為type的一個對象。 

10.2 程序變量

  在GDB中,你可以隨時查看以下三種變量的值
    A.全局變量(所有文件可見的)
    B.靜態全局變量(當前文件可見的)
    C.局部變量(當前Scope可見的)
  如果你的局部變量和全局變量發生沖突(也就是重名),一般情況下是局部變量會隱藏全局變量,也就是說,如果一個全局變量和一個函數中的局部變量同名時,如
  果當前停止點在函數中,用print顯示出的變量的值會是函數中的局部變量的值。如果此時你想查看全局變量的值時,你可以使用“::”操作符:

1 file::variable2 function::variable

  可以通過這種形式指定你所想查看的變量,是哪個文件中的或是哪個函數中的。例如,查看文件f2.c中的全局變量x的值:

1 gdb) p 'f2.c'::x

  當然,“::”操作符會和C++中的發生沖突,GDB能自動識別“::” 是否C++的操作符,所以你不必擔心在調試C++程序時會出現異常。

  另外,需要注意的是,如果你的程序編譯時開啟了優化選項,那么在用GDB調試被優化過的程序時,可能會發生某些變量不能訪問,或是取值錯誤碼的情況。這個是很正常的,因為優化程序會刪改你的程序,整理你程序的語句順序,剔除一些無意義的變量等,所以在GDB調試這種程序時,運行時的指令和你所編寫指令就有不一樣,也就會出現你所想象不到的結果。對付這種情況時,需要在編譯程序時關閉編譯優化。一般來說,幾乎所有的編譯器都支持編譯優化的開關,例如,GNU的C/C++編譯器GCC,你可以使用“-gstabs”選項來解決這個問題。關于編譯器的參數,還請查看編譯器的使用說明文檔。

10.3 數組

  有時候,你需要查看一段連續的內存空間的值。比如數組的一段,或是動態分配的數據的大小。你可以使用GDB的“@”操作符,“@”的左邊是第一個內存的地址的值 ,“@”的右邊則你你想查看內存的長度。例如,你的程序中有這樣的語句:

1 int *array = (int *) malloc (len * sizeof (int));

  于是,在GDB調試過程中,你可以以如下命令顯示出這個動態數組的取值:

1 p *array@len 

  @的左邊是數組的首地址的值,也就是變量array所指向的內容,右邊則是數據的長度,其保存在變量len中,其輸出結果,大約是下面這個樣子的:

1 (gdb) p *array@len
1 $1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40} 

  如果是靜態數組的話,可以直接用print數組名,就可以顯示數組中所有數據的內容了。

10.4 輸出格式

  一般來說,GDB會根據變量的類型輸出變量的值。但你也可以自定義GDB的輸出的格式。例如,你想輸出一個整數的十六進制,或是二進制來查看這個整型變量的中的位的情況。要做到這樣,你可以使用GDB的數據顯示格式:
    x 按十六進制格式顯示變量。
    d 按十進制格式顯示變量。
    u 按十六進制格式顯示無符號整型。
    o 按八進制格式顯示變量。
    t 按二進制格式顯示變量。
    a 按十六進制格式顯示變量。
    c 按字符格式顯示變量。
    f 按浮點數格式顯示變量。

 1 (gdb) p i 2 $21 = 101 3  4 (gdb) p/a i 5 $22 = 0x65 6  7 (gdb) p/c i 8 $23 = 101 'e' 9 10 (gdb) p/f i11 $24 = 1.41531145e-4312 13 (gdb) p/x i14 $25 = 0x6515 16 (gdb) p/t i17 $26 = 1100101

10.5 查看內存

  你可以使用examine命令(簡寫是x)來查看內存地址中的值。x命令的語法如下所示:

1 x/<n/f/u> <addr>

  n、f、u是可選的參數。 n 是一個正整數,表示顯示內存的長度,也就是說從當前地址向后顯示幾個地址的內容。

  f 表示顯示的格式,參見上面。如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i。
  u 表示從當前地址往后請求的字節數,如果不指定的話,GDB默認是4個bytes。u參數可以用下面的字符來代替,b表示單字節,h表示雙字節,w表示四字節,g表示八字節。當我們指定了字節長度后,GDB會從指內存定的內存地址開始,讀寫指定字節,并把其當作一個值取出來。
  <addr>表示一個內存地址。 n/f/u三個參數可以一起使用。例如:
  命令:x/3uh 0x54320 表示,從內存地址0x54320讀取內容,h表示以雙字節為一個單位,3表示三個單位,u表示按十六進制顯示。

10.6 自動顯示

  你可以設置一些自動顯示的變量,當程序停住時,或是在你單步跟蹤時,這些變量會自動顯示。相關的GDB命令是display。

1 display <expr>2 display/<fmt> <expr>3 display/<fmt> <addr>

  expr是一個表達式,fmt表示顯示的格式,addr表示內存地址,當你用display設定好了一個或多個表達式后,只要你的程序被停下來,GDB會自動顯示你所設置的這些表達式的值。

  格式i和s同樣被display支持,一個非常有用的命令是:

1 display/i $pc

  $pc是GDB的環境變量,表示著指令的地址,/i則表示輸出格式為機器指令碼,也就是匯編。于是當程序停下后,就會出現源代碼和機器指令碼相對應的情形,這是一個很有意思的功能。下面是一些和display相關的GDB命令:

1 undisplay <dnums...>2 delete display <dnums...>

  刪除自動顯示,dnums意為所設置好了的自動顯式的編號。如果要同時刪除幾個,編號可以用空格分隔,如果要刪除一個范圍內的編號,可以用減號表示(如:2-5)

1 disable display <dnums...>2 enable display <dnums...>

  disable和enalbe不刪除自動顯示的設置,而只是讓其失效和恢復。

1 info display

  查看display設置的自動顯示的信息。GDB會打出一張表格,向你報告當然調試中設置了多少個自動顯示設置,其中包括,設置的編號,表達式,是否enable。

10.7 設置顯示選項 

    GDB中關于顯示的選項比較多,這里我只例舉大多數常用的選項。 

1 set print address2 set print address on 

     打開地址輸出,當程序顯示函數信息時,GDB會顯出函數的參數地址。系統默認為打開的,如: 

1 (gdb) f2 #0 set_quotes (lq=0x34c78 "<<", rq=0x34c88 ">>")3       at input.c:5304 530         if (lquote != def_lquote) 
1 set print address off 

   關閉函數的參數地址顯示,如: 

1 (gdb) set print addr off2 (gdb) f3 #0 set_quotes (lq="<<", rq=">>") at input.c:5304 530         if (lquote != def_lquote) 
1 show print address 

   查看當前地址顯示選項是否打開。 

1 set print array2 set print array on 

  打開數組顯示,打開后當數組顯示時,每個元素占一行,如果不打開的話,每個元素則以逗號分隔。這個選項默認是關閉的。與之相關的兩個命令如下,我就不再多說了。 

1 set print array off2 show print array 
1 set print elements <number-of-elements> 

   這個選項主要是設置數組的,如果你的數組太大了,那么就可以指定一個<number-of-elements>來指定數據顯示的最大長度,當到達這個長度時,GDB就不再往下顯示了。如果設置為0,則表示不限制。 

1 show print elements 

    查看print elements的選項信息。 

1 set print null-stop <on/off> 

     如果打開了這個選項,那么當顯示字符串時,遇到結束符則停止顯示。這個選項默認為off。 

1 set print pretty on 

     如果打開printf pretty這個選項,那么當GDB顯示結構體時會比較漂亮。如: 

 1 $1 = { 2       next = 0x0, 3       flags = { 4       sweet = 1, 5       sour = 1 6       }, 7       meat = 0x54 "Pork" 8 } 
1 set print pretty off 

  關閉printf pretty這個選項,GDB顯示結構體時會如下顯示: 

1 $1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 "Pork"} 
1 show print pretty 

    查看GDB是如何顯示結構體的。 

1 set print sevenbit-strings <on/off> 

    設置字符顯示,是否按“/nnn”的格式顯示,如果打開,則字符串或字符數據按/nnn顯示,如“/065”。 

1 show print sevenbit-strings 

     查看字符顯示開關是否打開。 

1 set print union <on/off> 

    設置顯示結構體時,是否顯式其內的聯合體數據。例如有以下數據結構: 

 1 typedef enum {Tree, Bug} Species; 2 typedef enum {Big_tree, Acorn, Seedling} Tree_forms; 3 typedef enum {Caterpillar, Cocoon, Butterfly} 4 Bug_forms; 5  6 struct thing { 7      Species it; 8       union { 9           Tree_forms tree;10           Bug_forms bug;11       } form;12 };1314 struct thing foo = {Tree, {Acorn}}; 

    當打開這個開關時,執行 p foo 命令后,會如下顯示: 

1 $1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}} 

    當關閉這個開關時,執行 p foo 命令后,會如下顯示: 

1 $1 = {it = Tree, form = {...}} 
1 show print union 

     查看聯合體數據的顯示方式     

1 set print object <on/off> 

    在C++中,如果一個對象指針指向其派生類,如果打開這個選項,GDB會自動按照虛方法調用的規則顯示輸出,如果關閉這個選項的話,GDB就不管虛函數表了。這個選項默認是off。 

1 show print object 

    查看對象選項的設置。 

1 set print static-members <on/off> 

    這個選項表示,當顯示一個C++對象中的內容是,是否顯示其中的靜態數據成員。默認是on。 

1 show print static-members 

    查看靜態數據成員選項設置。 

1 set print vtbl <on/off> 

   當此選項打開時,GDB將用比較規整的格式來顯示虛函數表時。其默認是關閉的。 

1 show print vtbl 

   查看虛函數顯示格式的選項。 

10.8 歷史記錄

    當你用GDB的print查看程序運行時的數據時,你每一個print都會被GDB記錄下來。GDB會以$1, $2, $3 .....這樣的方式為你每一個print命令編上號。于是,你可以使用這個編號訪問以前的表達式,如$1。這個功能所帶來的好處是,如果你先前輸入了一個比較長的表達式,如果你還想查看這個表達式的值,你可以使用歷史記錄來訪問,省去了重復輸入。 

10.9 GDB環境變量 

    你可以在GDB的調試環境中定義自己的變量,用來保存一些調試程序中的運行數據。要定義一個GDB的變量很簡單只需。使用GDB的set命令。GDB的環境變量和UNIX一樣,也是以$起頭。如: 

1 set $foo = *object_ptr 

   使用環境變量時,GDB會在你第一次使用時創建這個變量,而在以后的使用中,則直接對其賦值。環境變量沒有類型,你可以給環境變量定義任一的類型。包括結構體和數組。 

1 show convenience 

    該命令查看當前所設置的所有的環境變量。 

    這是一個比較強大的功能,環境變量和程序變量的交互使用,將使得程序調試更為靈活便捷。例如: 

1 set $i = 02 print bar[$i++]->contents 

    于是,當你就不必,print bar[0]->contents, print bar[1]->contents地輸入命令了。輸入這樣的命令后,只用敲回車,重復執行上一條語句,環境變量會自動累加,從而完成逐個輸出的功能。 

10.10 查看寄存器 

    要查看寄存器的值,很簡單,可以使用如下命令: 

1 info registers 

    查看寄存器的情況。(除了浮點寄存器) 

1 info all-registers 

    查看所有寄存器的情況。(包括浮點寄存器) 

1 info registers <regname ...> 

    查看所指定的寄存器的情況。 

    寄存器中放置了程序運行時的數據,比如程序當前運行的指令地址(ip),程序的當前堆棧地址(sp)等等。你同樣可以使用print命令來訪問寄存器的情況,只需要在寄存器名字前加一個$符號就可以了。如:p $eip。 

11.改變程序的執行

    一旦使用GDB掛上被調試程序,當程序運行起來后,你可以根據自己的調試思路來動態地在GDB中更改當前被調試程序的運行線路或是其變量的值,這個強大的功能能夠讓你更好的調試你的程序,比如,你可以在程序的一次運行中走遍程序的所有分支。

11.1 修改變量值

    修改被調試程序運行時的變量值,在GDB中很容易實現,使用GDB的print命令即可完成。如:

1 (gdb) print x=4

    x=4這個表達式是C/C++的語法,意為把變量x的值修改為4,如果你當前調試的語言是Pascal,那么你可以使用Pascal的語法:x:=4。

    在某些時候,很有可能你的變量和GDB中的參數沖突,如:

1 (gdb) whatis width2 type = double3 (gdb) p width4 $4 = 135 (gdb) set width=476 Invalid syntax in expression.

    因為,set width是GDB的命令,所以,出現了“Invalid syntax in expression”的設置錯誤,此時,你可以使用set var命令來告訴GDB,width不是你GDB的參數,而是程序的變量名,如:

1 (gdb) set var width=47

    另外,還可能有些情況,GDB并不報告這種錯誤,所以保險起見,在你改變程序變量取值時,最好都使用set var格式的GDB命令。

11.2 跳轉執行

    一般來說,被調試程序會按照程序代碼的運行順序依次執行。GDB提供了亂序執行的功能,也就是說,GDB可以修改程序的執行順序,可以讓程序執行隨意跳躍。這個功能可以由GDB的jump命令來完:

1 jump <linespec>

    指定下一條語句的運行點。<linespce>可以是文件的行號,可以是file:line格式,可以是+num這種偏移量格式。表式著下一條運行語句從哪里開始。

1 jump <address>

    這里的<address>是代碼行的內存地址。

    注意,jump命令不會改變當前的程序棧中的內容,所以,當你從一個函數跳到另一個函數時,當函數運行完返回時進行彈棧操作時必然會發生錯誤,可能結果還是非常奇怪的,甚至于產生程序Core Dump。所以最好是同一個函數中進行跳轉。
    熟悉匯編的人都知道,程序運行時,有一個寄存器用于保存當前代碼所在的內存地址。所以,jump命令也就是改變了這個寄存器中的值。于是,你可以使用“set $pc”來更改跳轉執行的地址。如:

1 set $pc = 0x485

11.3 產生信號量

    使用singal命令,可以產生一個信號量給被調試的程序。如:中斷信號Ctrl+C。這非常方便于程序的調試,可以在程序運行的任意位置設置斷點,并在該斷點用GDB產生一個信號量,這種精確地在某處產生信號非常有利程序的調試。
    語法是:signal <singal>,UNIX的系統信號量通常從1到15。所以<singal>取值也在這個范圍。
    single命令和shell的kill命令不同,系統的kill命令發信號給被調試程序時,是由GDB截獲的,而single命令所發出一信號則是直接發給被調試程序的。

11.4 強制函數返回

    如果你的調試斷點在某個函數中,并還有語句沒有執行完。你可以使用return命令強制函數忽略還沒有執行的語句并返回。

1 return2 return <expression>

    使用return命令取消當前函數的執行,并立即返回,如果指定了<expression>,那么該表達式的值會被認作函數的返回值。

11.5 強制調用函數

1 call <expr>

    表達式中可以一是函數,以此達到強制調用函數的目的。并顯示函數的返回值,如果函數返回值是void,那么就不顯示。

    另一個相似的命令也可以完成這一功能——print,print后面可以跟表達式,所以也可以用他來調用函數,print和call的不同是,如果函數返回void,call則不顯示,print則顯示函數返回值,并把該值存入歷史數據中。

12.在不同語言中使用GDB

  GDB支持下列語言:C, C++, Fortran, PASCAL, java, Chill, assembly, 和 Modula-2。一般說來,GDB會根據你所調試的程序來確定當然的調試語言,比如:發現文件名后綴為“.c”的,GDB會認為是C程序。文件名后綴為“.C, .cc, .cp, .cpp, .cxx, .c++”的,GDB會認為是C++程序。而后綴是“.f, .F”的,GDB會認為是Fortran程序,還有,后綴為如果是“.s, .S”的會認為是匯編語言。

  也就是說,GDB會根據你所調試的程序的語言,來設置自己的語言環境,并讓GDB的命令跟著語言環境的改變而改變。比如一些GDB命令需要用到表達式或變量時,這些表達式或變量的語法,完全是根據當前的語言環境而改變的。例如C/C++中對指針的語法是*p,而在Modula-2中則是p^。并且,如果你當前的程序是由幾種不同語言一同編譯成的,那到在調試過程中,GDB也能根據不同的語言自動地切換語言環境。這種跟著語言環境而改變的功能,真是體貼開發人員的一種設計。


  下面是幾個相關于GDB語言環境的命令:

1 show language

     查看當前的語言環境。如果GDB不能識為你所調試的編程語言,那么,C語言被認為是默認的環境。

1 info frame

     查看當前函數的程序語言。

1 info source

     查看當前文件的程序語言。  

  如果GDB沒有檢測出當前的程序語言,那么你也可以手動設置當前的程序語言。使用set language命令即可做到。

     當set language命令后什么也不跟的話,你可以查看GDB所支持的語言種類:

 1 (gdb) set language 2 The currently understood settings are: 3 4 local or auto    Automatic setting based on source file 5 c                Use the C language 6 c++              Use the C++ language 7 asm              Use the Asm language 8 chill            Use the Chill language 9 fortran          Use the Fortran language10 java             Use the Java language11 modula-2         Use the Modula-2 language12 pascal           Use the Pascal language13 scheme           Use the Scheme language

    于是你可以在set language后跟上被列出來的程序語言名,來設置當前的語言環境。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日本精品视频在线播放| 日韩精品视频在线观看免费| 成人在线精品视频| 国产精品一久久香蕉国产线看观看| 国产精品视频白浆免费视频| 亚洲国产精品久久久久秋霞不卡| 欧洲成人免费aa| 中文字幕精品一区二区精品| 欧美高清在线播放| 欧美视频不卡中文| 91精品视频在线免费观看| 欧美大片免费观看在线观看网站推荐| 亚洲精品国精品久久99热| 亚洲欧洲国产精品| 国产精品老牛影院在线观看| 亚洲男人av电影| 中文欧美在线视频| 欧美午夜精品久久久久久久| 国产精品99蜜臀久久不卡二区| 91禁国产网站| 亚洲男人天堂视频| 欧美在线观看日本一区| 成人免费看片视频| 久久综合久中文字幕青草| 日韩二区三区在线| 国产一区二区三区在线播放免费观看| 日韩中文字幕不卡视频| 欧洲永久精品大片ww免费漫画| 亚洲综合精品一区二区| 热久久美女精品天天吊色| 国产精品69久久久久| 不卡av在线播放| 国产大片精品免费永久看nba| 日韩大陆欧美高清视频区| 亚洲影视九九影院在线观看| 中文字幕欧美日韩在线| 亚洲有声小说3d| 国产精国产精品| 久久亚洲影音av资源网| 精品国产91久久久| 亚洲美女av在线播放| 91成人国产在线观看| 免费99精品国产自在在线| 亚洲日本aⅴ片在线观看香蕉| 国产精品专区第二| 亚洲国产精品久久久久秋霞不卡| www.亚洲成人| 亚洲a∨日韩av高清在线观看| 国产亚洲精品一区二区| 欧美老妇交乱视频| 91产国在线观看动作片喷水| 日韩性xxxx爱| 久久久999精品| 久久99视频免费| 在线播放精品一区二区三区| 91精品久久久久久久久不口人| 国产成人亚洲精品| 国产精品aaaa| 欧美精品在线免费播放| 欧美激情2020午夜免费观看| 欧美成人精品在线| 亚洲国内精品在线| xvideos亚洲人网站| 成人精品视频久久久久| 91欧美精品成人综合在线观看| 成人激情视频免费在线| 97在线免费视频| 日韩在线一区二区三区免费视频| 成人在线激情视频| 亚洲成年人在线播放| 欧美激情按摩在线| 97在线视频观看| 国产精品高清在线观看| 中文字幕欧美日韩在线| 欧美专区在线播放| 欧美在线视频播放| 91九色视频导航| 这里只有精品在线观看| 亚洲欧美成人在线| 久久精品最新地址| 91亚洲永久免费精品| 在线观看欧美日韩国产| 免费91在线视频| 日韩欧美亚洲成人| 国内精品小视频| 精品美女永久免费视频| 国产亚洲成av人片在线观看桃| 亚洲成人免费网站| 久久精品成人欧美大片古装| 亚洲天堂免费在线| 国产在线拍偷自揄拍精品| 91精品久久久久久久久中文字幕| 欧美性生交xxxxxdddd| 欧美电影在线免费观看网站| 久久伊人精品视频| 国产精品99蜜臀久久不卡二区| 亚洲最大av在线| 亚洲激情在线观看视频免费| 国产精品视频内| 91在线|亚洲| 久久久免费在线观看| 亚洲男女自偷自拍图片另类| 久久精品国产成人| 欧美俄罗斯乱妇| 亚洲人成五月天| 精品视频9999| 啪一啪鲁一鲁2019在线视频| 亚洲iv一区二区三区| 66m—66摸成人免费视频| 97在线视频免费播放| 亚洲iv一区二区三区| 91国偷自产一区二区三区的观看方式| 久久精品欧美视频| 狠狠做深爱婷婷久久综合一区| 欧美精品在线免费| 日韩精品免费在线播放| 欧美电影电视剧在线观看| 日韩av在线导航| 久久久久久av| 久久天天躁夜夜躁狠狠躁2022| 九九精品在线播放| 日韩亚洲欧美中文高清在线| 日韩电影在线观看中文字幕| 久久九九有精品国产23| 国产在线播放不卡| 国产精品综合久久久| 日韩精品在线观看一区| 亚洲福利视频专区| 日韩av综合网站| 热久久99这里有精品| 久久精品久久精品亚洲人| 亚洲国产精品va在看黑人| 亚洲一区二区福利| 热re99久久精品国产66热| 亚洲精品欧美极品| 日韩免费在线视频| 欧美日本高清视频| 91av中文字幕| 九九久久久久99精品| 欧美高跟鞋交xxxxxhd| 精品国内自产拍在线观看| 国产精品流白浆视频| 国产精品av免费在线观看| 欧美精品日韩www.p站| 欧美日韩国产丝袜另类| 色悠悠国产精品| 亚洲欧美日韩精品久久奇米色影视| 亚洲一区二区三区四区视频| 久久久精品免费视频| 日韩在线中文字| 疯狂做受xxxx高潮欧美日本| 国产精品第七十二页| 久久免费成人精品视频| 国产精品9999| 亚洲理论电影网| 亚洲女人天堂成人av在线| 欧美精品电影在线| 俺去亚洲欧洲欧美日韩| 成人欧美一区二区三区黑人| 久久久精品久久久| 国产经典一区二区| 久久综合伊人77777蜜臀| 亚洲精品99久久久久| 91性高湖久久久久久久久_久久99|