• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            chaosuper85

            C++博客 首頁 新隨筆 聯系 聚合 管理
              118 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks

            #

                C++虛函數探索筆記(1)——虛函數的簡單示例分析

                關注問題:

                虛函數的作用

                虛函數的實現原理

                虛函數表在對象布局里的位置

                虛函數的類的sizeof

                純虛函數的作用

                多級繼承時的虛函數表內容

                虛函數如何執行父類代碼

                多繼承時的虛函數表定位,以及對象布局

                虛析構函數的作用

                虛函數在QT的信號與槽中的應用

                虛函數與inline修飾符,static修飾符

                啰嗦兩句

                虛函數在C++里的作用是在是非常非常的大,很多講述C++的文章都會講到它

            ,要用好C++,就一定要學好虛函數。網絡上可以google到很多很多關于它的文章

            ,這一次的學習,我不準備去只是簡單的閱讀了解那些文章,而是希望通過編寫

            一些測試代碼,來對虛函數的一些實現機制,以及C++對象布局做一下探索。

                虛函數的簡單示例 !

                虛函數常常出現在一些抽象接口類定義里,當然,還有一個更常見的“特例

            ”,那就是虛析構函數,后面會提到這個。

                下面是一段關于虛函數的簡單代碼,演示了使用基類接口操作對象時的效果


             //Source filename: Win32Con.cpp
            #include <iostream>
            using namespace std;
            class parent1
            {
            public:
                virtual int fun1()=0;
            };

            class child1:public parent1
            {
            public:
                virtual int fun1()
                {
                    cout<<"child1::fun1()"<<endl;
                    return 0;
                }
            };

            class child2:public parent1
            {
            public:
                virtual int fun1()
                {
                    cout<<"child2::fun1()"<<endl;
                    return 0;
                }
            };

            void test_func1(parent1 *pp)
            {
                pp->fun1();
            }

            int main(int argc, char* argv[])
            {
                child1 co1;
                child2 co2;
                test_func1(&co1);
                test_func1(&co2);
                return 0;
            }

             


                在上面的代碼里,類parent1是一個只具有純虛函數的接口類,這個類不能被

            實例化,它唯一的用途就是抽象一些特定的接口函數,當然,在這里這個接口函

            數就是純虛函數 parent1::fun1()。

                而類child1和child2則是兩個從parent1繼承的類,我們要使用它定義具體的

            類實例,所以它實現了由parent1繼承得來的fun1接口,并且各自的實現是不同的

                函數 test_func1 的參數是一個parent1類型的指針,它所要完成的功能就是

            調用這個parent1對象的fun1()函數。

                讓我們編譯運行一下上面的代碼,可以看到下面的輸出

                child1::fun1()

                child2::fun1()

                很顯然,在兩次調用test_func1函數的時候,雖然傳入的參數都是一個

            parent1的指針,但是卻都分別執行了child1和child2各自的fun1函數!這就是

            C++里類的多態。然而,這一切是怎么發生的呢?test_func1函數怎么會知道應該

            調用哪個函數的呢?我不準備像其他人一樣畫若干圖來說明,我準備用具體某個

            編譯器產生的對象布局以及相應的匯編代碼來說明這個過程(這個編譯器是

            vs2008里的vc9)。

                我們先打開一個VS2008命令提示窗口,改變目錄到上面的代碼Win32Con.cpp

            所在目錄,輸入下面的命令:

                cl  win32con.cpp  /d1reportSingleClassLayoutchild

                上面的命令可以編譯win32con.cpp源碼,同時生成里面類名包含child 的類

            的對象布局(layout)

                注意:d1reportSingleClassLayout和后面的child是相連的!

                輸入上面的命令后看到的對象布局如下,紅色字為我添加的注釋
             class child1    size(4): 子類child1的對象布局,只包含一個vfptr,大小為

            4字節
                    +---
                    | +--- (base class parent1) 這是被嵌套的父類parent1的對象布局
             0      | | {vfptr}
                    | +---
                    +---
            這是child1的vfptr所指的虛函數表的布局,只包含一個函數的地址,就是child1

            的fun1函數
            child1::$vftable@:
                    | &child1_meta
                    |  0
             0      | &child1::fun1

            child1::fun1 this adjustor: 0

            class child2    size(4): 子類child2的對象布局,只包含一個vfptr,大小為4

            字節
                    +---
                    | +--- (base class parent1) 這是被嵌套的父類parent1的對象布局
             0      | | {vfptr}
                    | +---
                    +---
            這是child2的vfptr所指的虛函數表的布局,只包含一個函數的地址,就是child2

            的fun1函數
            child2::$vftable@:
                    | &child2_meta
                    |  0
             0      | &child2::fun1

            child2::fun1 this adjustor: 0

             


                從上面的對象布局可以知道:

                每個子對象都有一個隱藏的成員變量vfptr(你當然不能用這個名字訪問到它

            ),它的值是指向該子對象的虛函數表,而虛函數表里填寫的函數地址是該子對

            象的fun1函數地址。

                對一個包含有虛函數的類做sizeof操作的時候,除了能直接看到的成員變量,還得增加4字節(在32位機器上),就是vfptr這個指針的大小。

                所以當test_func1進行pp->fun1()調用的時候,會首先取出pp所指的內存地址并按照parent1的內存布局,獲取到vfptr指針(由于pp在兩次調用中分別指向co1和co2所以這里取得的實際上是co1的vfptr和co2的vfptr),然后從vfptr所指的虛函數表第一項(現在也只有 1 項)取出作為將要調用的函數,由于co1和co2在各自的虛函數表里填寫了各自的fun1的地址,于是pp->fun1()最終就調用到了co1和co2各自的fun1,輸出自然也就不同了。

                讓我們看看test_func1的反匯編代碼:
             void test_func1(parent1 *pp)
            {
            001C1530  push        ebp
            001C1531  mov         ebp,esp
            001C1533  sub         esp,0C0h
            001C1539  push        ebx
            001C153A  push        esi
            001C153B  push        edi
            001C153C  lea         edi,[ebp-0C0h]
            001C1542  mov         ecx,30h
            001C1547  mov         eax,0CCCCCCCCh
            001C154C  rep stos    dword ptr es:[edi]
                pp->fun1();
            001C154E  mov         eax,dword ptr [pp] //取得pp的值放到eax,即對象的地址
            //取得對象的vfptr地址放到edx(因為vfptr在對象布局里拍在第一)
            001C1551  mov         edx,dword ptr [eax]
            001C1553  mov         esi,esp
            001C1555  mov         ecx,dword ptr [pp]
            001C1558  mov         eax,dword ptr [edx] //取出vfptr的第一個虛函數的地址到eax
            001C155A  call        eax //調用虛函數,即fun1()

                至此,應該比較清楚虛函數機制的基本實現了。然而,也許你還會有這些問題:

                虛函數表是每個子對象都有的么?

                虛函數是存在一個表里的,表的數據結構是怎樣的,如何定位表里哪個才是我們要調用的虛函數?

                略作變化

                讓我們對前面的代碼做以下修改:

                定義一個普通類

                修改parent類,在fun1前增加虛函數fun2

                在child1里和child2里編寫fun2的具體實現,一個在fun1之前編寫,另外一個在之后編寫修改后的編碼大致如下:

             class parent1
            {
            public:
                virtual int fun2()=0;
                virtual int fun1()=0;
            };

            class child
            {
                int a;
            };

            class child1:public parent1
            {
            public:

                virtual int fun1()
                {
                    cout<<"child1::fun1()"<<endl;
                    return 0;
                }
                virtual int fun2()
                {
                    cout<<"child1::fun2()"<<endl;
                    return 0;
                }
            };

                然后我們再使用cl命令以及/d1reportSingleClassLayout選項輸出相關的類對象布局情況:

             class child     size(4):  //在普通類child里,看不到vfptr的身影!
                    +---
             0      | a
                    +---

            class child1    size(4):    //child1的對象布局,和之前沒有變化!
                    +---
                    | +--- (base class parent1)
             0      | | {vfptr}
                    | +---
                    +---
            //child1的虛函數表多了fun2,并且兩個虛函數在表里的順序相同于在parent類里聲明的順序
            child1::$vftable@:
                    | &child1_meta
                    |  0
             0      | &child1::fun2
             1      | &child1::fun1

            child1::fun1 this adjustor: 0
            child1::fun2 this adjustor: 0

                結論很明顯:

                虛函數表指針vfptr只在類里有虛擬函數的時候才會存在當有多個虛函數的時候,虛函數在虛函數表里的順序由父類里虛函數的定義順序決定并且我們還可以觀察到:

                這個vfptr指針會放在類的起始處(這是必須的,vfptr在父類和子類的對象布局上必須一致!)

                虛函數表是以一個NULL指針標識結束讓我們對這次簡單的示例代碼測試來做個小小總結:

                有虛函數的類,一定會有一個虛函數表指針vfptr這個vfptr指針會放在類的起始處虛函數表里會按基類聲明虛函數的順序在vfptr里存放函數地址虛函數表里存放的是函數地址是具體子類的實現函數的地址調用虛函數的時候,是從vfptr所指的函數表里獲取到函數地址,然后才調用具體的代碼。

             


            posted @ 2009-08-05 17:42 chaosuper 閱讀(338) | 評論 (1)編輯 收藏

            昨天開始就想要升級Redhat Linux 9.0的內核--2.14.20的

            找了個網頁,本來想升成2.6.24的,不成功!

            后來照樣畫葫蘆,升2.6.18的,照網頁一樣的來,成功了!

            不過還是想升級2.6.24的,又不行,有時間再做了!

            下面把網頁的貼出來,供借鑒。

            我的環境是vmware下的Redhat Linux 9.0的


            連不上網頁的同志們見下面:

            一、準備工作
            首先說明,下面帶#號的行都是要輸入的命令行,且本文提到的所有命令行都在終端里輸入。啟動Linux系統,并用根用戶登錄,進入終端模式下。

            1、查看Linux內核版本# uname -a
            如果屏幕顯示的是2.6.x,說明你的已經是2.6的內核,也用不著看下文了,該干什么干什么去吧!如果顯示的是2.4.x,那恭喜你,闖關通過,趕快進行下一步。

            2、下載2.6內核源碼
            下載地址:http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.18.tar.bz2

            3、下載內核升級工具
            (1)下載module-init-tools-3.2.tar.bz2
            http://www.kernel.org/pub/linux/utils/kernel/module-init-tools/module-init-tools-3.2.tar.bz2
            (2)下載mkinitrd-4.1.18-2.i386.rpm
            http://ayo.freshrpms.net/Fedora/linux/3/i386/RPMS.core/mkinitrd-4.1.18-2.i386.rpm
            (3)下載lvm2-2.00.25-1.01.i386.rpm
            http://ayo.freshrpms.net/Fedora/linux/3/i386/RPMS.core/lvm2-2.00.25-1.01.i386.rpm
            (4)下載device-mapper-1.00.19-2.i386.rpm
            http://ayo.freshrpms.net/Fedora/linux/3/i386/RPMS.core/device-mapper-1.00.19-2.i386.rpm

            二、配置工作
            好啦,2.6內核和4個升級工具都下載完了(少一個也不行,如果沒有下載齊全,請不要嘗試下面的步驟,升級是不會成功的),下面回到Linux系統中開始配置工作吧。

            4、將下載好的內核源碼包和4個升級工具都拷貝到/usr/src文件夾下。怎么拷貝就不用我教了吧~~~~不會拷貝的去撞墻吧!~~呵呵!

            5、拷貝完畢,開始解壓新內核,具體操作請依次執行以下命令:

            # cd /usr/src (進入到/usr/src目錄下,如果已經在/usr/src目錄下,可不執行該命令)
            # rm –rf linux (刪除linux文件夾。值得一提的是,如果內核先前從未編譯過,則沒有這個文件夾,此命令行可略過)
            # tar jvxf linux-2.6.18.tar.bz2 (解壓新內核)
            # ln -s linux-2.6.18 linux (重新生成linux文件夾)

            6、安裝module-init-tools工具
            在/usr/src目錄下,依次執行下列命令:
            # tar jvxf module-init-tools-3.2.tar.bz2 (解壓module-init-tools)
            # cd module-init-tools-3.2 (由/usr/src目錄進入module-init-tools目錄下)
            #./configure --prefix=/
            # make moveold
            # make all install
            #./generate-modprobe.conf /etc/modprobe.conf

            7、安裝另外三個升級工具
            回到/usr/src目錄下,依次執行下列3個命令來安裝另外三個升級工具:
            # rpm -ivh --nodeps mkinitrd-4.1.18-2.i386.rpm (注意,這里一定要加入--nodeps參數,下同)
            # rpm -ivh --nodeps lvm2-2.00.25-1.01.i386.rpm
            # rpm -ivh --nodeps device-mapper-1.00.19-2.i386.rpm

            如果不更新以上幾個升級包,在后面編譯內核時會提示以下錯誤:
            mkinitrd failed
            make[1]: *** [install] Error 1
            make: *** [install] Error 2

            8、配置內核選項。有點繁瑣,~~希望一次成功哦~~。
            # cd linux-2.6.18 (進入到/usr/src/linux-2.6.18目錄下)
            # make mrproper (該命令可確保源代碼目錄下沒有不正確的.o文件)
            # make menuconfig (配置內核各選項)

            此時會出現一個圖形界面,列出了所有的內核配置選項,有的選項下還有子選項,你可以用方向鍵來選擇,用Y鍵來確定。經過我多次試驗,大多數選項默認就行,以下幾個選項必須選擇(請認真核對下面每一個選項,否則編譯很有可能前功盡棄):

            (1)Loadable Module support選項中,選上“Module unloading”和“Automatic kernel module loading”這兩項;
            (2)Device Drivers--->Block Devices中選上“Loopback device support”;
            Device Drivers--->Multi-device support(RAID and LVM)處要選上“device mapper support”;
            Device Drivers--->Graphics support,一定要選上“ Support for frame. buffer devices”;
            Device Drivers --->USB support --->選上“USB Mass Storage support”(如果是在實環境中,想要更多USB支持,就全選吧。我的是在虛擬機中,用不著了)
            Device Drivers --->;Network device support --->Ethernet (10 or 100Mbit) ---><*> AMD PCnet32 PCI support
            (3)File system--->(以下9個選項是關于ext2和ext3文件系統配置,全部選上)
            Second extended fs support
            Ext2 extended attributes
            Ext2 POSIX Access Control Lists
            Ext2 Security Labels
            Ext3 journalling file system support
            Ext3 extended attributes
            Ext3 POSIX Access Control Lists
            Ext3 Security Labels
            JBB (ext3) debugging support
            File system--->DOS/FAT/NT Filesystems --->選上“NTFS file system support”;

            注意:
            ext2和ext3文件系統配置很重要,也是必需的,如果對Ext3、Ext2文件的支持直接編譯進內核,在你reboot時機器就會當掉,出現如下錯誤信息:

            kernel panic : no init found ,try passing init = option to kernel.....

            或者是:
            VFS:Cannot open root device "hdxy" or unknow-block(0,0)
            Please append a correct "root=" boot option
            kernel panic:VFS:Unable to mount root fs on unknown-block(0,0)

            或者是:
            mount: error 19 mounting ext3
            pivotroot: pivot_root(/sysroot,/sysroot/initrd) failed: 2
            umount /initrd/proc fail: 2
            Freeing unused kernel memory: 244k freed
            Kernel panic – not syncing: No init found. Try passing init = option to kernel

            (我的機器就是在重啟之后出現第三種錯誤,進不去系統,郁悶死,只好重裝了,如果依照本文做完所有步驟,當你重啟Linux系統后,若不幸進不去2.6.18內核,你會發現你的出錯信息就是上面三種了~~~哈!)

            (4)如果你在vmware下編譯內核,硬盤用的是scsi的,以下三個選項必選:
            Device Drivers ---><*>SCSI device support (此項不選的話,下面兩項就選擇不上)
            Device Drivers ---><*>SCSI device support ---><*>SCSI disk support
            Device Drivers---><8>SCSI device support--->SCSI low-level drivers---><*>; BusLogic SCSI support

            三、編譯工作
            OK,繁雜的配置工作完成了,至此,編譯前的準備工作都做好了!

            9、開始編譯啦……
            在/usr/src/linux-2.6.18目錄下,執行以下命令即可編譯。編譯需要一段時間,給自己倒杯茶耐心等候吧!
            # make dep (建立編譯時所需的從屬文件。注意:如果內核從未編譯過,此步可跳過)
            # make clean (清除內核編譯的目標文件。注意:如果內核從未編譯過,此步可跳過)
            # make bzImage (注意大小寫。這一步才是真正編譯內核)
            內核編譯成功后,會在/usr/src/linux/arch/i386/boot目錄中生成一個新內核的映像文件bzImage。如果用make zImage編譯,內核很大的話,系統會提示你使用make bzImage命令來編譯,所以我直接用make bzImage來編譯。
            # make modules (編譯可加載模塊)
            # make modules_install (安裝可加載模塊)
            安裝成功后,系統會在/lib/modules目錄下生成一個2.6.18子目錄,里面存放著新內核的所有可加載模塊。
            # make install (安裝新內核)

            注意:
            make install的時候可能會出現如下錯誤信息:
            No module BusLogic found for kernel 2.4.12
            mkinitrd failed
            此問題一般只出現在SCSI硬盤+VMWARE+REDHAT架構中,因為BusLogic被編譯進了內核而不是一個module的形式(2.4內核的Buslogic模塊即使靜態編譯進內核也不行)。解決方式是直接將BusLogic.o文件復制過去:
            # cp /usr/src/linux-2.6.18/drivers/scsi/BusLogic.o /lib/modules/2.6.18/kernel/drivers/scsi
            不過別忘記,復制過后再執行一下make install。這一步若卡住了,下面的都無法進行。

            四、啟動新內核
            10、將新內核和System.map文件拷貝到/boot目錄下,依次執行以下命令:
            # cp /usr/src/linux-2.6.18/arch/i386/boot/bzImage /boot/vmlinuz-2.6.18
            # cp /usr/src/linux-2.6.18/System.map /boot/System.map-2.6.18
            # cd /boot (進入boot目錄)

            # rm –rf System.map (刪除原來的連接)
            # ln –s System.map-2.6.18 System.map (重新建立連接)

            11、修改Grub啟動管理器
            如果沒有錯誤的話, 下面開始修改grub配置文件(不要告訴我你用的lilo)
            在/boot目錄下,執行以下命令:
            # new-kernel-pkg --mkinitrd --depmod --install 2.6.18 (這時候你的/boot下會生成一個initrd-2.4.18.img,并且你的grub.conf文件也作了相應更改)

            # df (查看根目錄在那個分區,下一步要用到。注意,這里根分區不時boot的那個50M的分區,而一般是你最大的那個分區,也就是“/”,千萬不要搞錯哦。我的為 /dev/hda2)

            # vi /grub/grub.conf
            進入grub.conf文件,找到如下信息:
            default=1
            timeout=10
            splashimage=(hd0,0)/grub/splash.xpm.gz
            title Red Hat Linux (2.6.18)
            root (hd0,0)
            kernel /vmlinuz-2.6.18 ro root= LABEL=/
            initrd /initrd-2.6.18.img

            做兩處修改:
            (1) 將default=1改為default=0(不改的話也可以,只不過重啟之后會默認進入2.4內核)
            (2) 將kernel行的“LABEL=/”換成根目錄所在的分區(上一步查看的就是)
            此步很重要,修改錯誤將可能導致進不去系統,我把我修改后的grub.conf文件列出來,不明之處,可以對照修改:
            default=0
            timeout=10
            splashimage=(hd0,0)/grub/splash.xpm.gz
            title Red Hat Linux (2.6.18)
            root (hd0,0)
            kernel /vmlinuz-2.6.18 ro root=/dev/hda2
            initrd /initrd-2.6.18.img
            title Red Hat Linux (2.4.20-8)
            root (hd0,0)
            kernel /vmlinuz-2.4.20-8 ro root=LABEL=/
            initrd /initrd-2.4.20-8.img

            12,OK,大功告成!趕快重啟,看看升級后的2.6內核吧。

            根據以上內容重啟后有如下問題:

            A 不能上網

            #vi /etc/modules.config

            修改第一行如下:

            alias eth0 pcnet32

            重新激活網卡就行了。

            posted @ 2009-08-04 19:43 chaosuper 閱讀(285) | 評論 (0)編輯 收藏

             

            1、Harbin 哈爾濱賽區(哈爾濱工業大學)
            網絡選拔賽日期:2009年9月13日 14:00-17:00
            現場賽日期:2009年9月26日~27日
            http://acm.hit.edu.cn/

            2、Dhaka 達卡賽區(孟加拉國 Northsouth University)
            現場賽日期:2009年10月3日
            http://www.northsouth.edu/acm/

            3、Gwalior-Kanpur 瓜廖爾-坎普爾賽區(印度 IIITM Gwalior and Indian Institute of Technology, India)
            現場賽日期:2009年10月3日~4日
            http://www.cse.iitk.ac.in/users/acm/

            4、Hefei 合肥賽區(中國科學技術大學)
            網絡選拔賽日期:2009年9月6日 14:00-17:00
            現場賽日期:2009年10月10日~11日
            http://acm.ustc.edu.cn/

            5、Ningbo 寧波賽區(浙江大學寧波理工學院)
            網絡選拔賽日期:2009年9月12日
            現場賽日期:2009年10月17日~18日
            http://acmasia09.nit.net.cn/

            6、Jakarta 雅加達賽區(印尼 Binus University)
            現場賽日期:2009年10月21日
            http://icpc.ewubd.edu/

            7、Manila 馬尼拉賽區(菲律賓 Ateneo de Manila University)
            現場賽日期:2009年10月22日~23日
            http://www.math.admu.edu.ph/acm/

            8、Shanghai 上海賽區(東華大學)
            網絡選拔賽日期:2009年9月20日(12:00-17:00)
            現場賽日期:2009年10月24日~25日
            http://acm.dhu.edu.cn

            9、Hsinchu 新竹賽區(交通大學)
            報名截止日期:2009年8月19日
            現場賽日期:2009年10月30日~31日
            http://icpc2009.nctu.edu.tw/

            10、Wuhan 武漢賽區(武漢大學)
            網絡選拔賽日期:2009年10月3
            現場賽日期:2009年10月31日~11月1日
            http://acm.whu.edu.cn/

            11、Amritapuri 阿姆里塔普里賽區(印度 Amrita Vishwa Vidyapeetham, Amritapuri Campus)
            現場賽日期:2009年11月1日
            http://icpc.amrita.ac.in/

            12、Phuket 普吉島賽區(泰國 Prince of Songkla University, Phuket Campus)
            報名截止日期:2009年9月30日
            現場賽日期:2009年11月3日~4日
            http://www.acmicpc-thailand.org/

            13、Seoul 首爾賽區(韓國 Korea Advanced Institute of Science and Technology)
            報名截止日期:2009年9月11日
            現場賽日期:2009年11月5日~6日
            http://acm.kaist.ac.kr/

            14、Tehran 德黑蘭賽區(伊朗 Sharif University of Technology)
            現場賽日期:2009年11月6日
            http://sharif.edu/~acmicpc

            15、Tokyo 東京賽區(日本早稻田大學)
            現場賽日期:2009年11月7日~8日
            http://www.waseda.jp/assoc-icpc2009/en/index.html

            參賽報名網址

            http://cm.baylor.edu/welcome.icpc

            亞洲高校可組隊參加全部十五個賽區的預選賽,但每位參賽選手最多只能注冊參加兩個賽區的預選賽。

            posted @ 2009-08-04 19:36 chaosuper 閱讀(249) | 評論 (0)編輯 收藏

            學習站點  http://doc.linuxpk.com/type1091.html 

            Emacs 的命令通常包括控制鍵(就是上面標有 Ctrl或Ctl的那個)或者是META鍵(上面標有EDIT或ALT)。為了方便起見我們將用下面的縮寫來代替這些鍵的全稱:

            C- 意思是當敲入字符 時同時按住控制鍵,因此,C-f表 示:按住控制鍵并且按 f 。 M- 表示當鍵入 時按住META或ALT或EDIT鍵。如果沒有META 或ALT或EDIT鍵,則用ESC鍵代替。 表示ESC鍵

            注意:退出Emacs,按C-x C-c(兩個字符)。在文本左邊區域的“>>”符號表示讓你試著使用一個命令。比如:
            >> 現在鍵入C-v(觀看下一屏)移動到下一屏。(就象前面說的,按v的同時也按住控制鍵)。從現在開始,每當你讀完一屏的時候都可以用它來翻屏。
            注意在翻屏后會保留上屏的最后一行;這是為你繼續閱讀文本提供某些連貫性。
            你所需要知道的第一件事是如何把光標從一個地方移動到另一個地方。你已經知道了如何向前翻一屏--用 C-v。要向后翻一屏,鍵入M-v。
            >> 試著鍵入 M-v 和 C-v 幾次。
            * 摘要(SUMMARY)
            ---------------------------
            下面幾個命令對整屏觀看時有用:

            C-v 向前翻一整屏。
            M-v 向后翻一整屏。
            C-l 清除屏幕并重新顯示所有的文本,然后把光標移動到屏幕的中央。 (注意是Control-L,而不是 Control-1)。
            >> 尋找光標,并且注意它在文本里的位置。然后鍵入C-l。再尋找光標你會注意到光標現在會出現在同樣的文本附近。
            * 基本光標控制(BASIC CURSOR CONTROL)
            -----------------------------------------------------------------
            整屏整屏的移動是很有用的,可是如何把光標移動到屏幕上文本里的一個指定的地方呢?
            有好幾個方法可以實現。最基本的方法是用命令 C-p,C-b,C-f,和C-n。這些命令每個都使光標在屏幕上往特定的方向移動一行或者一列。下面是一個圖表顯示了這四個命令和它們所移動的方向:


            上一行,C-p
            :
            :
            向前,C-b ...... 當前光標的位置 ...... 向后,C-f
            :
            :
            下一行,C-n
            >> 用C-n或C-p把光標移動到圖表中間。然后鍵入C-l會看到整個圖表出現在屏幕的中央。



            你也許會發現這些字母很容易記住:P 代表上面的(previous),N 代表下一個 (next),B 代表向前(backward),F 代表向后(forward)。這些是基本的光標位置命令,你將經常會用到它們。所以現在學習它們很有好處。


            >> 用幾次 C-n 把光標向下移動到這一行。

            >> 用 C-f 把光標移動到行里,再用C-p把光標上移。觀察當光標在行的中間時 C-p做了些什么。
            每一個文本行都以一個換行符結尾,它用來當作行與行之間的分格。你的文件的最后一行的尾部應該有一個換行符(但Emacs并不要求一定要有一個)。
            >> 試著在行的開頭使用C-b。它將會把光標移到上一行的末尾。這是因為它向后移的時候穿過了換行符。
            C-f 也能象 C-b一樣穿過換行符。
            >> 使用幾次C-b,使您能知道光標在哪。然后用C-f移動到行的末尾。然后再用一次C-f,使光標移動到下一行。

            當你移動超過屏幕的頂部或底部,光標回移動到下一屏的中間,這叫做“滾屏 (scrolling)”。它使得Emacs滾屏移動到文本上指定的部位而不是移出屏幕。

            >> 試著用C-n把光標移過屏幕的底部,看看回發生什么。

            如果覺得一個一個字符的移動太緩慢,可以一個單詞一個單詞的移動。M-f(Meta-f) 向前移一個單詞,M-b向后移一個單詞。
            >> 鍵入幾個M-f和M-b。

            當光標在一個單詞的中間,M-f移動到單詞的末尾。當光標在兩個單詞間的空白部分 M-f移動到后一個單詞的末尾。M-b與M-f一樣,只是移動的方向相反。
            >> 鍵入M-f和M-b幾次,中間穿插一些C-f和C-b以使你能觀察到M-f和M-b在單詞中和單詞間的不同行為。

            注意比較C-f,C-b與M-f,M-b。通常情況下Meta鍵用于有關語言單位(詞,句,段落) 的操作;而控制鍵用于編輯時的基本單位(字符,行等)。
            這是句與行的比較:C-a和C-e移動到一行的開頭和末尾,M-a和M-e移動到一個句子的開頭和末尾。
            >> 鍵入一對C-a,再鍵入一對C-e。 鍵入一對M-a,再鍵入一對M-e。

            你會看到重復鍵入的C-a什么也不做,而重復鍵入的M-a則會移動一個以上的句子。
            光標在文本中的位置也叫“點(point)”。在段落里,光標標示出了點在屏幕上文本里的位置。
            下面是簡單的光標移動命令的總結,包括單詞和句子的移動命令:

            C-f 向前移動一個字符。
            C-b 向后移動一個字符。

            M-f 向前移動一個單詞。
            M-b 向后移動一個單詞。

            C-n 移動到下一行。
            C-p 移動到上一行。

            C-a 移動到行首。
            C-e 移動到行尾。

            M-a 向前移動到句子的開頭。
            M-e 向后移動到句子的末尾。

            >> 試著對每一個命令都實踐幾次,它們都是經常要用到的命令。
            另外兩個重要的光標移動命令是M-<(Meta小于),它移動光標到整個文本的開頭,M-> (Meta大于)它移動光標到整個文本的末尾。
            在多數終端上,“<”在逗號的上面,所以你必須用Shift鍵來輸入它。在這些終端上,你也必須用Shift鍵來輸入M-<;沒有Shift鍵,你可以輸入M-逗號。
            >> 現在就試試M-<,移動到本教程的開頭,然后再用C-v移回這里。 現在就試試M->,移動到本教程的末尾,然后再用M-v移回這里。

            你也可以用方向鍵來移動光標,如果你的終端有方向鍵的話。我們建議學習C-b, C-f,C-n和C-p有三個原因。第一,它們能在所有類型的終端上工作。第二,你獲得了使用Emacs的鍛煉,你將會發現輸入這些CTRL加字符比按方向鍵要快(因為你不必把你的手從鍵盤上移開)。第三,一旦你養成了使用這些CTRL加字符的命令的習慣,你就能一樣容易的學習其他高級的光標移動命令。
            大多數Emacs命令接收一個數字參數;對大多數命令來說,這表示命令重復的次數。輸入重復命令次數的方法是在輸入命令之前按C-u和數字。如果你有META(或EDIT或 ALT)鍵,則有另一種方法輸入數字參數:在按住META鍵的時候輸入數字,我們建議學習C-u方法,因為它能在任何終端上工作。
            例如,C-u 8 C-f 向前移動8個字符。
            >> 試著使用帶數字參數的C-n或C-p,只用一個命令就把光標移動到與本行相鄰的 行上。

            絕大多數命令把數字參數當作重復次數,但也有幾個例外。C-v和M-v就是。當給出一個參數,只是上滾或下滾數字指定的行數而不是屏數。比如,C-u 4 C-v滾動4行屏幕。


            >> 現在試試 C-u 8 C-v。
            這將使屏幕滾動8行,如果你想往回滾動的話,鍵入一個帶參數的M-v。
            如果你正在使用X窗口,在Emacs窗口的左手邊有一個叫做滾動條的矩形區域。你能通過用鼠標點擊滾動條來滾動文本。
            >> 試著在滾動條頂部的高亮區域點擊中鍵。這將使文本滾動,滾動的位置取決于 你點擊的長短。

            >> 試著按住鼠標中鍵上移或下移鼠標,你將看到當你移動鼠標時文本會上下滾動。
            * 當EMACS掛起時(WHEN EMACS IS HUNG)
            ------------------------------------------------------------------
            當Emacs停止響應你的命令時,你能用C-g把它安全的停止。當一個命令執行了太長的時間時你可以用C-g把它終止。
            你也可以用C-g來取消數字參數和輸入后又不想執行的命令。
            >> 鍵入C-u 100 產生一個值為100的數字參數,然后按C-g。再按C-f。它只會移動 一個字符,因為你用C-g取消了參數。


            如果錯誤的輸入了一個 ,你能用C-g消掉它。
            * 禁止命令(DISABLED COMMAND)
            ----------------------------------------------------
            一些Emacs命令是“禁止”的,所以新手不會因偶然而執行它。
            如果你鍵入了一個禁止命令,Emacs會顯示一條消息說明這條命令是干什么的,并且問你是否需要執行它。
            如果你真的想要執行,敲空格鍵繼續。通常,如果你不想執行禁止命令,用“n”來回答。

            >> 輸入 :(一條禁止命令),然后用n來回答。

            * 窗口(WINDOWS)
            -----------------------------
            Emacs 能有好幾個窗口,每一個顯示自己的文本。我們將在后面解釋怎樣對多窗口操作。現在我們要解釋怎樣去除多余的窗口屏回到基本的單窗口編輯狀態。這是一個例子:
            C-x 1 一個窗口(也就是除去其他所有的窗口)。
            因為Control-x跟了數字1。C-x 1使包含光標的窗口占滿整個屏幕,屏刪除其他所有窗口。
            >> 把光標移動本行并輸入 C-u 0 C-l。
            >> 鍵入Control-h k Control-f。 看這個窗口如何縮小,并在按Control-f的時候出現了一個新的文檔窗口。
            >> 鍵入C-x 1 并且看到那個文檔窗口消失了。

            * 插入和刪除(INSERTING AND DELETING)
            ---------------------------------------------------------------
            如果你要插入文本,只須輸入文本。輸入的字符你能見到,比如A,7,*等等。Emacs 會立即把它們插入。鍵入 (回車鍵)插入一個換行符。
            你能用 刪除你輸入的最后一個字符。 就是鍵盤上標著“Del”的鍵。在某些情況下,“Backspace”鍵作用和 一樣,但不總是這樣!


            通常, 立即刪除光標前面的那個字符。


            >> 輸入幾個字符,然后用 刪除它們。不必擔心這個文件回被改變;你不會 替換主教程。這只是你的個人拷貝。

            當一行文本太長而超過屏幕寬度時,這一行會在屏幕的下一行被“繼續”。文本的右邊會有一個反斜杠“”表示它被繼續。
            >> 插入文本直到最右邊,然后再插入。你將看到一個繼續了的行。
            >> 使用 刪除文本直到行的長度在屏幕的寬度以內。繼續的行將會消失。

            你能像刪除其他任何字符一樣刪除換行符。刪除兩個行間的換行符會使它們合并為一行。如果這一行很長屏幕顯示不下的話,將會用一個繼續的行來表示。
            >> 把光標移動到一行的開頭按 這將使本行和上一行合為一行。
            >> 按 重新插入你刪除的換行符。

            記住大多數的Emacs命令能接收一個重復次數。這包括文本字符,把一個文本字符重復的插入幾次。
            >> 鍵入這個-- C-u 8 * 來插入 ********

            你現在已經學習了Emacs的大多數輸入和排錯的方法。你也能一樣的刪除單詞或行。這是刪除操作的摘要:
            刪除光標前面的字符
            C-d 刪除光標后面的字符
            M- 除去光標前面的單詞
            M-d 除去光標后面的單詞
            C-k 除去從光標位置到行尾的內容
            M-k 除去到當前句子的末尾
            注意比較 ,C-d與M ,M-d和C-f,M-f( 不是一個控制字符,但不用擔心)。C-k和M-k就象C-e,M-e。
            當你一次除去不止一個字符時,Emacs將保存著這些文本,所以你可以恢復它們。恢復那些被除去的文本稱作“拉(yanking)”。你能在除去文本的同一地方拉回它們,或是在文本的其他地方。你能對文本拉上幾次以產生它們的多個拷貝,拉的命令是 C-y。
            注意“除去(killing)”與“刪除(Deleting)”之間的區別,被除去的東西能被拉回來,而被刪除的不能。通常除去能除去很多的文本屏保存,而刪除只能除去一個字符,或是空行或空格,并且不保存。


            >> 把光標移到一個空行的開頭,鍵入C-k除去這一行。
            >> 按第二次C-k,你將看到剩下的空行也被除去了。



            注意單個的C-k除去行的內容,第二個C-k除去行本身,并且使后面的所有行上移。特別要注意數字參數:它除去很多行和它們的內容,這不僅僅是重復。C-u 2 C-k 除去兩行和它們剩下的空行;而按兩次C-k并不會這樣做。


            要在當前光標處找回上次被除去的文本;按C-y


            >> 試一試,用C-y把文本拉回來。



            把C-y考慮為你把某人從你這里拿走的東西再拿回來。注意你如果在一行上按了幾次 C-y,所有被除去的文本是存在一起的,所以按一次C-y將拉回全部的行。


            >> 現在就試一下,按幾次C-k。 現在找回被除去的文本;


            >> 按C-y。然后把光標下移幾行再按一次C-y,你現在會看到怎樣拷貝這些文本。



            當你要拉回一些被除去的文本該怎樣做呢?C-y只能拉回最近被除去的文本。但以前的文本并沒有消失。你能用M-y來恢復它。當你用C-y拉回最近被除去的文本后,換成 M-y可以拉回以前被除去的文本。鍵入一次又一次的M-y可以拉回更早以前被除去的文本。當你找到要尋找的文本,不必做任何事來保持它,只須離開拉文本的地方繼續你的編輯。


            如果你M-y了足夠多的次數,你會回到開始點(最近被除去的)。


            >> 除掉一行,移開,再除掉另一行。 然后用C-y拉回第二行。 然后換成M-y拉回被除掉的第一行。 再按一次M-y看看得到了什么。繼續按直到拉回被除去的第二行;然后再做幾次。 如果原意的話,你可以給M-y加正的或負的數字參數。





            * 撤銷(UNDO)
            --------------------



            如果你對文本作了一些改動,然后又發現這樣做是錯誤的,你能用撤銷命令,C-x u 撤銷這些改變。


            通常,一次C-x u撤銷一個改變;如果你在一行上重復幾次C-x u,就會重復幾次撤銷操作。


            但有兩個例外:不改變文本的操作(包括光標移動和滾屏命令)不算在內;只能處理20 次。


            >> 用C-k除去這一行,然后按C-x u 它將重現出來。



            C-_是一個可選擇的撤銷命令;它所作的工作和C-x u 完全一樣,只是更容易輸入。 C-_的缺點是有些鍵盤上沒有它,這就是為什么還提供C-x u的原因。在某些終端上你可以按住CTRL的時候再敲/來輸入C-_。C-_或C-x u把數參數字當作重復次數。


            * 文件(FILES)
            -------------------

            為了永久保存你編輯的文本,你必須把它放到一個文件里。否則當你退出Emacs的時候它就會消失。你通過“查找(finding)”文件,把你編輯的內容放到文件里。(也稱為 “訪問(visiting)文件”)。


            (譯注:為了保持與原文的一致性,把find譯為“查找”,但是這里和后面出現的 “查找文件”指的都是打開文件的意思。)


            查找(finding)一個文件意味著你在Emacs里看文件的內容,在多數情況下,也就是你在編輯它。但是,你用Emacs對它作的改變并不是永久行的,除非你“保存(saving)” 它。所以你可以避免把一個改了一半的文件留在系統上。甚至你保存了文件,Emacs也會把原始文件換個名字保留下來,以防過后你發現對文件的改動是錯誤的。


            如果你觀察屏幕的你將看見一個開始和結尾都是破折號的行,并且以“--:**-- TUTORIAL”或之類的東西開始。屏幕的這部分通常顯示你正在訪問的文件的名字。現在,一個叫做“TUTORAL”的文件,它是你的Emacs教程的個人拷貝。當你用Emacs 查找一個文件,文件名會出現在同樣的位置。


            查找和保存文件命令不像前面學的那些命令。它們都以字符Control-x開始。以 Control-x起頭的是一個完整的命令系列;它們中的許多都是對文件,緩沖,和相關的東西進行操作的。這些命令有兩個,三個或四個字符長。


            關于查找文件命令的另一件事是你必須給出你需要的文件的文件名。我們說這個命令 “從終端讀取一個參數”。(在這種情況下,參數是文件的名字);當你鍵入命令C-x C-f后,Emacs會提示你輸入文件的名字。你輸入的文件名會出現在屏幕底部的行上。這個底部的行稱為微型緩沖(minibuffer)用于這類較短的輸入。你能用Emacs本身的編輯命令來編輯文件名。


            當你正在輸入文件名(或其他任何微型緩沖區輸入),你能用命令C-g來取消。


            >> 鍵入命令C-x C-f,然后輸入C-g。這將取消微型緩沖,也取消了C-x C-f命令所使 用的微型緩沖,所以你不查找任何文件。



            當你輸完文件名后用 來結束。然后C-x C-f開始工作,并開始尋找你所選擇的文件。當C-x C-f命令結束后微型緩沖區也消失了。


            過一小會兒文件的內容就會顯示在屏幕上,然后你就能對它進行編輯了。當想永久保留你的改動時用命令:


            C-x C-s 保存文件(save the file)。


            這個操作會把Emacs里的文本拷貝到文件里。在你第一次作的時候,Emacs把原始文 件改為一個新名字以使它不至于丟失。新名字是在原來名字的后面加一個“~”。


            保存結束后,Emacs打印出被寫的文件的文件名。你應當經常的保存,萬一系統崩潰 的話你不至于丟失太多的工作。


            >> 鍵入C-x C-s來保存你的教程的拷貝。屏幕的底部會打印出“Wrote.....TUTORIAL”。



            注意:在某些系統上,輸入C-x C-s 會把屏幕凍結住使你從Emacs看不到更多的輸出。這 表示這個操作系統的“特性”叫做“控制流程”,它攔截了C-x不讓它到達Emacs那里。 要使屏幕解凍,輸入C-q,然后看Emacs手冊里的“Spontaneous Entry to Incremental Search”一節,按上面的建議來對付這種“特性”。


            你能查找一個已存在的文件,來查看它或編輯它。你也可以查找一個尚未存在的文件。這是 Emacs:里創建文件的方法:查找文件,將會出現一個空白,然后插入文件的文本。當你 “保存(saving)”的時候,Emacs將會用你插入的文本創建文件。從那時候起,你可以認為你在編輯一個存在的文件了。




            * (緩沖)BUFFERS
            --------------------------



            如果你用C-x C-f查找第二個文件,第一個文件仍然留在Emacs里。你可以再用C-x C-f查找一次來切換回去。用這種方法你在Emacs里有很多文件。


            >> 輸入C-x C-f foo 來建立一個名為foo的文件。然后插入一些文本,編輯它,并 用C-x C-s來保存“foo”。 最后輸入C-x C-f TUTORIAL 以回到本教程。




            Emacs把每個文件的文本都保存在一個叫“緩沖(buffer)”的東西里。查找(打開)一個文件就會在Emacs里產生一個新的緩沖。要看你當前運行的Emacs里存在的緩沖列表,輸入:


            C-x C-b 列出緩沖(list buffers)


            >> 輸入 C-x C-b



            觀察每個緩沖都有一個名字,它可能也有一個它所保存的文件的文件名。一些緩沖不對應文件。比如,叫“*Buffers List*”的緩沖沒有任何文件。這個緩沖只包含由C-x C-b產生的緩沖列表。你在Emacs窗口里看到的任何文本都是某個緩沖的一部分。


            >> 輸入 C-x 1 消除緩沖列表。



            如果你對一個文件的文本作了改動,然后查找另一個文件,第一個文件并不保存。它的改變保存在Emacs里,在那個文件的緩沖里。被建立或編輯的第二個文件的緩沖并不影響第一個的。這一點很有用,但這也意味著要有一個便捷的方法來保存第一個文件的緩沖。如果要用 C-x C-f切換回去只是為了按C-x C-s保存它將會是一件令人討厭的事。所以我們用


            C-x s 保存緩沖(save the buffer)


            C-x s 向你詢問每個改動過但未存盤的緩沖,對每個這樣的緩沖都詢問是否保存。


            >> 插入一行文本,然后按C-x s。 將會問你是否保存叫TUTORIAL的緩沖。 輸入“y”來回答是。





            * 擴展命令集(EXTENDING THE COMMAND SET)
            -----------------------------------------------------------------------



            有太多的Emacs命令,大大超過了Contorl和meta加上字符所能表示的數量。Emacs用X(擴展 eXtand)命令來解決這個問題。有兩種風格:


            C-x 字符擴展,后跟一個字符。
            M-x 名字命令擴展,后跟一個長名字。


            這些命令通常有用,但不如你已經學過的那些命令使用的頻繁。你已經見過了它們中的兩個:文件命令C-x C-f 用于查找和C-x C-s用于保存。


            另一個例子是結束Emacs的命令C-x C-c(不必擔心你所作的改動會丟失,在退出Emacs 之前,C-x C-c會提示你保存每一個改動過的文件)。


            C-z命令用于*臨時*退出Emacs,所以你能回到原來運行的Emacs里。在允許這樣做的系統上,C-z把Emacs“掛起”;就是說回到外殼(shell)下,但并不破壞運行的Emacs。在大多數外殼上,你能用‘fg'命令或‘%emacs'來繼續Emacs。


            在不支持掛起的系統上,C-z建立一個子外殼(subshell)運行于Emacs下以使你能運行其他程序然后回到Emacs;這并不是真正的“退出”Emacs。在這種情況下,通常從子外殼回到Emacs的外殼命令是‘exit'。 有很多C-x 命令,這是已學過的一個列表:


            C-x C-f 查找文件
            C-x C-s 保存文件
            C-x C-b 緩沖列表
            C-x C-c 退出Emacs


            C-x u 撤銷操作


            被稱作擴展命令的命令的使用頻率都不太高。或者是只在某些模式下使用。一個例子是替換字符串的命令,它在全文里把字符串替換為其他的。當你鍵入M-x, Emacs會在屏幕的底部提示你輸入命令;在這種情況下,是“replace-string”。比如輸入“repl s ”, Emacs會把命令補全。用 來結束命令。


            替換字符串命令要求兩個參數--要被替換的字符串和用來替換的字符串。你必須用 來結束兩個參數。


            >> 把光標移上兩行,然后輸入M-x repl s changed altered 。 注意現在這一行改變了:你把光標初始位置后的所有單詞c-h-a-n-g-e-d替換為了 “altered”





            * 自動保存(AUTO SAVE)
            ------------------------------------



            當你改動了一個文件還未存盤的話,所作的改動也許會由于系統崩潰而丟失。為防止這種情況發生,Emacs在編輯時為每個文件提供了“自動保存(auto save)”。自動保存的文件的文件名前后都有一個#號;例如,如果你編輯的文件名叫“hello.c”,自動保存的文件的文件名就叫“#hello.c#”。當你正常的保存了文件后,Emacs會刪除這個自動保存的文件。如果遇到死機,你能打開那個文件后按M-x recover file 來恢復你的編輯,(是你編輯的文件而不是自動保存的文件)。當提示確認時,輸入yes 來繼續恢復自動保存的數據




            * 回顯區域(ECHO AREA)
            ------------------------------------



            如果Emacs發現你輸入命令的速度很慢的話它會在屏幕底部為你顯示出來,這個區域叫 “回顯區域”。




            * 模式行(MODE LINE)
            ---------------------------------



            回顯區域上面的一行稱為“模式行(mode line)”。模式行顯示與下面類似的東西:


            --**-Emacs: TUTORIAL (Fundamental)--L670--58%----------------


            這一行給出了有關你在編輯的文件和Emacs狀態的有用信息。


            你已經知道了文件名意味著什么。--NN%--指出你現在在文本里的位置;它意味著上面還有NN%的文本。如果是在文件的開頭,會用--Top-- 來代替--0%--。如果是在行的末尾,會顯示--Bot--。如果你正在看的文本內容很少,可以全部顯示在屏幕上,模式行會說 --All--。


            前面的星號表示你已經改動過文本了。一旦你保存了文件或打開了一個新文件,模式行的這部分就不是星號而是破折號了。


            模式行上括號里的部分是現在的編輯模式。現在是缺省的基本(Fundamental)模式。它是 “主模式(major mode)”的一種。


            Emacs有很多不同的主模式。有些意味著不同的語言或不同的文本。如Lisp模式(Lisp mode),文本模式(text mode)等等。在任何時候有且只能有一種主模式被激活。并且它的名字會出現在現在顯示“Fundamental”的位置上。


            每一個主模式都有些自己的命令。就象不同的編程語言的注釋看起來不同一樣。每種主模式插入的注釋也不同。可以用擴展命令切換進某種主模式。例如,M-x fundamental-mode 是切換進基本模式。


            >> 輸入 M-x text-mode



            不必擔心,沒有命令會給Emacs帶來很大改變。但是你可以看到現在M-f和M-b把省略號當作單詞的一部分。而先前,在基本模式里,M-f 和M-b把省略號當成當成分隔符。


            主模式通常作諸如此類微小的變化:大多數命令在每個主模式里作“同樣的工作”,但又有些微小的不同。


            要觀看關于你現在的主模式的文檔,按C-h m。


            >> 鍵入C-u C-v一次和多次使本行接近屏幕的頂端。
            >> 輸入C-h m ,看看文本模式和基本模式有些什么不同。
            >> 按C-x 1 從屏幕上關掉這個文檔。



            主模式之所以叫做主模式是因為也存在從模式,從模式與主模式完全不同。每個從模式可以自己打開或者關閉,獨立于所有其他從模式,也獨立于你的主模式。所以你可以不用從模式或者同時用很多種從模式。


            有一種從模式很有用,特別是在編輯英文文本時。它是自動填充模式(auto fill mode)。當這個模式打開的時候,當輸入的文本過寬的時候就會自動折行。


            你能用M-x auto-fill-mode 來打開自動填充模式。如果此模式已經打開M-x auto- fill-mode 則把它關閉。我們把這叫做切換開關。


            >> 輸入M-x auto-fill-mode 。然后插入一些“asdf”直到看到這行被分為兩行。你必須在中間放一些空格,只有到空格的時候才會換行。



            通常邊界寬度是70,但你能用帶數字參數的C-x f 命令來改變它。


            >> 鍵入帶參數20的C-x。(C-u 20 C-x f) 然后輸入一些文本看現在每行只有20個字符了。然后用C-x f把它改回70。



            如果你在一個段落的中間產生了改變,自動填充模式將不會重新填充。要想重新填充段落,當光標在段落里的時候按M-q。


            >> 把光標移到上一段按 M-q。

            * 搜索(SEARCHING)
            -----------------------------

            Emacs 能朝前和朝后搜索字符串(指相鄰的一些字符或單詞)。搜索是一個移動光標的操作,它把光標移動到字符串出現的下一個地方。


            Emacs 的搜索命令和其他大多數編輯器不同,它是“增量式(incremental)”的,這意味著搜索在你鍵入字符串時就開始了。


            開始一個向前搜索的命令是C-s,C-r是往回搜索。但等等,先別忙。


            當你輸入C-s是你將注意到在回顯區域會出現一個字符串“I-search”。這告訴你Emacs開始了一個增量搜索,并在等待你輸入要搜索的東西。 結束查詢。


            >> 現在鍵入C-s開始一個搜索。慢慢的輸入單詞‘cousor',在輸入每一個字母的時候停頓一 下,注意看光標發生了什么。
            >> 再輸入一次C-s,來搜索“cursor”出現的下一個地方。
            >> 現在輸入 四次看看光標移到了哪里。
            >> 輸入 結束搜索。


            看到發生什么了嗎?在Emacs的增量搜索里,你輸入多少字符串它就試著搜索這些字符出現的地方。到字符串出現的下一個地方,只須再按一次C-s。要搜索的字符串不存在的話,Emacs 會發出蜂鳴并告訴你當前的搜索“失敗(failing)”,按 C-g 也是終止搜索。


            注意:在某些系統上,輸入 C-s 會把屏幕凍結住使你從Emacs看不到更多的輸出。這 表示這個操作系統的“特性”叫做“控制流程”,它攔截了C-s不讓它到達Emacs那里。 要使屏幕解凍,輸入C-q,然后看Emacs手冊里的“Spontaneous Entry to Incremental Search”一節,按上面的建議來對付這種“特性”。
            如果你在搜索的過程里按了 ,你將注意到要搜索的字符串的最后一個字符會被刪除并且光標會回到上一個被搜索到的地方。比如,假設你鍵入了“c”,將會搜索“c”第一次出現的地方。然后如果你鍵入“u”,光標將移到“ cu”第一次出現的地方。現在鍵入 。這將從搜索的字符串里把“u”刪掉,這時光標回到“c”第一次出現的地方。


            如果你在搜索時按了Control或meta鍵加字符(少數幾個少數命令例外,如C-s和C-r),搜索將被終止。


            C-s向當前光標的后面搜索字符串出現的地方。如果你需要搜索前面文本里的東西,用C-r來代替。我們所介紹的C-s的每個特性C-r也支持,除了方向相反。


            * 多窗口(MULTIPLE WINDOWS)
            ------------------------------------------------


            Emacs有一個非常好的特性是能同時在屏幕上顯示不止一個的窗口。


            >> 把光標移到本行上按C-u 0 C-l。


            >> 現在按C-x 2,它把屏幕分裂成兩個窗口,每個窗口都顯示本教程。光標在上面的窗口里。


            >> 按C-M-v 滾動到下面的窗口里。(如果你沒有一個真正的Meta鍵,那么按ESC C-v)

            >> 按 C-x o (“o” 指 “其他(other)”) 把光標移到到下面的窗口里。
            >> 用 C-v 和 M-v 滾動下面窗口里的文本。 在上面的窗口里看本教程。


            >> 再次按 C-x o 使光標回到上面的窗口里。 現在光標象以前一樣在上面的窗口里了。



            你能一直用C-x o在窗口間切換。每個窗口都有它自己的光標位置,但僅有一個窗口能顯示活動的光標。所有的編輯命令都發生在那個顯示光標的窗口上。我們把這個窗口叫做“選中窗口( selected window)”。


            當你在一個窗口里編輯文本,而用另一個窗口作參考時命令C-M-v非常有用。你總是能把光標留在所編輯的地方,而用C-M-v來翻閱另一窗口。


            C-M-v 是 CONTROL-META 加字符的一個例子。 如果你有一個真正的META 鍵,你能同時按住 CTRL 和 META 再按“v”來輸入C-M-v。CTRL 和 META 誰先按誰后按無所謂。


            如果你沒有一個真正的META 鍵, 你可以用 ESC 來代替。這時候次序是有關系的: 你必須讓 ESC 跟在 CTRL-v后面; 否則 CTRL-ESC v 將不工作。 這是因為 ESC 是一個有意義的字符而不是一個修飾字符。


            >> 輸入 C-x 1 (在上面的窗口里) 以消除下面的窗口。



            (如果你在下面的窗口里鍵入C-x 1,將會把上面的窗口去掉。可以把這個命令看作是“只保留你現在在的那個窗口。)


            你不必一定要在兩個窗口里顯示同樣的緩沖。如果你在一個窗口里鍵入C-x C-f查找文件,另一個窗口的內容不會改變。你能獨立的在每個窗口里查找文件。


            這是讓兩個窗口顯示不同內容的另一種方法:


            >> 在你輸入的文件名后再輸入C-x 4 C-f,然后用 結束。會看到指定的文件出現在下面 的窗口里。光標也在那里面。


            >> 鍵入C-x o 回到上面的窗口,然后輸入C-x 1刪掉下面的窗口。



            * 遞歸編輯層(RECURSIVE EDITING LEVELS)
            ----------------------------------------------------------------

            有時候你會進入“遞歸編輯層(recursive editing level)”。由模式行上的方括號指示。它在主模式名的括號外面。例如你也許會看到(Fundamental)變成了[(Fundamental)]。


            要退出遞歸編輯層,按ESC ESC ESC。這是一個通用的退出命令,你也可以用它除去額外的窗口,或者退出微型緩沖。


            >> 輸入 M-x 進入一個微型緩沖; 然后用 ESC ESC ESC 離開。

            你不能用C-g來退出遞歸編輯層。這是因為C-g只能取消在遞歸編輯層里面的命令。


            * 獲取更多的幫助(GETTING MORE HELP)
            --------------------------------------------------------------

            在本教程里我們試著為你開始使用Emacs提供了足夠多的信息。但是有關Emacs的信息實在是太多以至于不能全部都在這里說明。但是,你還應該學習更多有關Emacs的東西,因為它另外還有很多有用的特性。Emacs提供了很多讀取有關命令的文檔的命令。這些“幫助”命令都以 Control-h開頭,叫做“幫助字符”。


            為了使用幫助特性,輸入字符C-h,然后再輸入一個字符來說明你需要哪種幫助。如果你真的不知道,輸入C-h ? 然后 Emacs會告訴你它能給你什么樣的幫助。如果你輸入了C-h 又覺得不需要任何幫助,你可以用C-g來取消它。


            (在有的地方,C-h的作用被改變了。如果按C-h在屏幕的底部沒有出現有關幫助的信息的話,試試用F1和M-x help RET來代替。)


            最基本的幫助特性是C-h c。輸入C-h,然后是字符 c,然后輸入一個命令字符和序列;然后 Emacs 會顯示這個命令的簡潔的描述。


            >> 輸入 C-h c Control-p.

            顯示的消息看起來會象這樣:


            C-p runs the command previous-line


            這告訴你“功能的名字”。功能的名字主要用于對Emacs的功能擴充和定制。但因為功能的名字指出了命令的用途,所以最好不要改動它。


            C-h c后面可跟多字符命令,比如 C-x C-s 和 (如果你沒有 META 或者 EDIT 或者 ALT 鍵) v 。


            要獲取有關命令的更多信息,用C-h k 代替 C-h c。


            >> 輸入 C-h k Control-p.



            這將在一個Emacs窗口里顯示命令的文檔。當你讀完后可以用C-x 1除去幫助文本。如果不想馬上離開,你可以一邊編輯一邊參考幫助文本,然后再按C-x 1。


            這是一些有用的 C-h 選項:


            C-h f 描述一個功能,在你輸入了這個功能的名字后。

            >> 輸入 C-h f previous-line 。 將打印出C-p命令所實現的所有功能。



            C-h a 命令查找。輸入一個關鍵字,Emacs將列出所有名字里有這個關鍵字的命令。 包括所有以Meta-x開始的命令。對有些命令,C-h a 也將列出實現同一功能的 幾個命令序列。


            >> 輸入 C-h a file .




            這將在窗口里顯示所有名字里有單詞“file”的M-x命令。


            >> 輸入 C-M-v 來滾動幫助窗口,做上幾次。


            >> 輸入 C-x 1 來刪除幫助窗口。





            * 總結(CONCLUSION)
            --------------------------------

            記住,永遠都用C-x C-c來退出Emacs。用C-z來退到一個臨時的外殼里,以使你過后還能回到 Emacs。
            本教程盡量讓所有的初學者都能理解,如果你發現有些東西不清楚的話,別責備你自己-抱怨吧!

            posted @ 2009-08-04 09:02 chaosuper 閱讀(342) | 評論 (0)編輯 收藏

              1.引言

              Linux操作系統在服務器領域的應用和普及已經有較長的歷史,這源于它的開源特點以及其超越Windows的安全性和穩定性。而近年來,Linux操作系統在嵌入式系統領域的延伸也可謂是如日中天,許多版本的嵌入式Linux系統被開發出來,如ucLinux、RTLinux、ARM-Linux等等。在嵌入式操作系統方面,Linux的地位是不容懷疑的,它開源、它包含TCP/IP協議棧、它易集成GUI。

              鑒于Linux操作系統在服務器和嵌入式系統領域愈來愈廣泛的應用,社會上越來越需要基于Linux操作系統進行編程的開發人員。

              瀏覽許多論壇,經常碰到這樣的提問:“現在是不是很流行unix/linux下的c編程?所以想學習一下!但是不知道該從何學起,如何下手!有什么好的建議嗎?各位高手!哪些書籍比較合適初學者?在深入淺出的過程中應該看哪些不同層次的書?比如好的網站、論壇請大家賜教!不慎感激!”

              鑒于讀者的需求,在本文中,筆者將對Linux平臺下C編程的幾個方面進行實例講解,并力求回答讀者們關心的問題,以與讀者朋友們進行交流,共同提高。在本文的連載過程中,有任何問題或建議,您可以給筆者發送email:21cnbao@21cn.com,您也可以進入筆者的博客參與討論:http://blog.donews.com/21cnbao

              筆者建議在PC內存足夠大的情況下,不要直接安裝Linux操作系統,最好把它安裝在運行VMWare虛擬機軟件的Windows平臺上,如下圖:

             

              在Linux平臺下,可用任意一個文本編輯工具編輯源代碼,但筆者建議使用emacs軟件,它具備語法高亮、版本控制等附帶功能,如下圖:

             

              2.GCC編譯器

              GCC是Linux平臺下最重要的開發工具,它是GNU的C和C++編譯器,其基本用法為:

            gcc [options] [filenames]

              options為編譯選項,GCC總共提供的編譯選項超過100個,但只有少數幾個會被頻繁使用,我們僅對幾個常用選項進行介紹。

              假設我們編譯一輸出“Hello World”的程序:

            /* Filename:helloworld.c */
            main()
            {
            printf("Hello World\n");
            }

              最簡單的編譯方法是不指定任何編譯選項:

            gcc helloworld.c

              它會為目標程序生成默認的文件名a.out,我們可用-o編譯選項來為將產生的可執行文件指定一個文件名來代替a.out。例如,將上述名為helloworld.c的C程序編譯為名叫helloworld的可執行文件,需要輸入如下命令:

            gcc –o helloworld helloworld.c

              -c選項告訴GCC僅把源代碼編譯為目標代碼而跳過匯編和連接的步驟;

              -S 編譯選項告訴GCC 在為 C代碼產生了匯編語言文件后停止編譯。GCC 產生的匯編語言文件的缺省擴展名是.s,上述程序運行如下命令:

            gcc –S helloworld.c

              將生成helloworld.c的匯編代碼,使用的是AT&T匯編。用emacs打開匯編代碼如下圖:

             

             


              -E選項指示編譯器僅對輸入文件進行預處理。當這個選項被使用時,預處理器的輸出被送到標準輸出(默認為屏幕)而不是儲存在文件里。

              -O選項告訴GCC對源代碼進行基本優化從而使得程序執行地更快;而-O2選項告訴GCC產生盡可能小和盡可能快的代碼。使用-O2選項編譯的速度比使用-O時慢,但產生的代碼執行速度會更快。

              -g選項告訴GCC產生能被GNU調試器使用的調試信息以便調試你的程序,可喜的是,在GCC里,我們能聯用-g和-O (產生優化代碼)。

              -pg選項告訴GCC在你的程序里加入額外的代碼,執行時,產生gprof用的剖析信息以顯示你的程序的耗時情況。

              3.GDB調試器

              GCC用于編譯程序,而Linux的另一個GNU工具gdb則用于調試程序。gdb是一個用來調試C和C++程序的強力調試器,我們能通過它進行一系列調試工作,包括設置斷點、觀查變量、單步等。
            其最常用的命令如下:

              file:裝入想要調試的可執行文件。
              kill:終止正在調試的程序。
              list:列表顯示源代碼。
              next:執行一行源代碼但不進入函數內部。
              step:執行一行源代碼而且進入函數內部。
              run:執行當前被調試的程序
              quit:終止gdb
              watch:監視一個變量的值
              break:在代碼里設置斷點,程序執行到這里時掛起
              make:不退出gdb而重新產生可執行文件
              shell:不離開gdb而執行shell

              下面我們來演示怎樣用GDB來調試一個求0+1+2+3+…+99的程序:

            /* Filename:sum.c */
            main()
            {
            int i, sum;
            sum = 0;
            for (i = 0; i < 100; i++)
            {
            sum + = i;
            }

            printf("the sum of 1+2+...+ is %d", sum);
            }

              執行如下命令編譯sum.c(加-g選項產生debug信息):

            gcc –g –o sum sum.c

              在命令行上鍵入gdb sum并按回車鍵就可以開始調試sum了,再運行run命令執行sum,屏幕上將看到如下內容:

             

              list命令:

              list命令用于列出源代碼,對上述程序兩次運行list,將出現如下畫面(源代碼被標行號):

             

              根據列出的源程序,如果我們將斷點設置在第5行,只需在gdb 命令行提示符下鍵入如下命令設置斷點:(gdb) break 5,執行情況如下圖:

             

              這個時候我們再run,程序會停止在第5行,如下圖:

             

              設置斷點的另一種語法是 break <function>,它在進入指定函數(function)時停住。

              相反的,clear用于清除所有的已定義的斷點,clear <function>清除設置在函數上的斷點, clear <linenum>則清除設置在指定行上的斷點。

              watch命令:
               
              watch命令用于觀查變量或表達式的值,我們觀查sum變量只需要運行watch sum:

             

               watch <expr>為表達式(變量)expr設置一個觀察點,一量表達式值有變化時,程序會停止執行。

              要觀查當前設置的watch,可以使用info watchpoints命令。

              next、step命令:

               next、step用于單步執行,在執行的過程中,被watch變量的變化情況將實時呈現(分別顯示Old value和New value),如下圖:

             

               next、step命令的區別在于step遇到函數調用,會跳轉到到該函數定義的開始行去執行,而next則不進入到函數內部,它把函數調用語句當作一條普通語句執行。

              4.Make

              make是所有想在Linux系統上編程的用戶必須掌握的工具,對于任何稍具規模的程序,我們都會使用到make,幾乎可以說不使用make的程序不具備任何實用價值。

              在此,我們有必要解釋編譯和連接的區別。編譯器使用源碼文件來產生某種形式的目標文件(object files),在編譯過程中,外部的符號參考并沒有被解釋或替換(即外部全局變量和函數并沒有被找到)。因此,在編譯階段所報的錯誤一般都是語法錯誤。而連接器則用于連接目標文件和程序包,生成一個可執行程序。在連接階段,一個目標文件中對別的文件中的符號的參考被解釋,如果有符號不能找到,會報告連接錯誤。

              編譯和連接的一般步驟是:第一階段把源文件一個一個的編譯成目標文件,第二階段把所有的目標文件加上需要的程序包連接成一個可執行文件。這樣的過程很痛苦,我們需要使用大量的gcc命令。

              而make則使我們從大量源文件的編譯和連接工作中解放出來,綜合為一步完成。GNU Make的主要工作是讀進一個文本文件,稱為makefile。這個文件記錄了哪些文件(目的文件,目的文件不一定是最后的可執行程序,它可以是任何一種文件)由哪些文件(依靠文件)產生,用什么命令來產生。Make依靠此makefile中的信息檢查磁盤上的文件,如果目的文件的創建或修改時間比它的一個依靠文件舊的話,make就執行相應的命令,以便更新目的文件。

              假設我們寫下如下的三個文件,add.h用于聲明add函數,add.c提供兩個整數相加的函數體,而main.c中調用add函數:

            /* filename:add.h */
            extern int add(int i, int j);

            /* filename:add.c */
            int add(int i, int j)
            {
            return i + j;
            }

            /* filename:main.c */
            #include "add.h"
            main()
            {
            int a, b;
            a = 2;
            b = 3;
            printf("the sum of a+b is %d", add(a + b));
            }

              怎樣為上述三個文件產生makefile呢?如下:

            test : main.o add.o
            gcc main.o add.o -o test

            main.o : main.c add.h
            gcc -c main.c -o main.o

            add.o : add.c add.h
            gcc -c add.c -o add.o 

              上述makefile利用add.c和add.h文件執行gcc -c add.c -o add.o命令產生add.o目標代碼,利用main.c和add.h文件執行gcc -c main.c -o main.o命令產生main.o目標代碼,最后利用main.o和add.o文件(兩個模塊的目標代碼)執行gcc main.o add.o -o test命令產生可執行文件test。

              我們可在makefile中加入變量,另外。環境變量在make過程中也被解釋成make的變量。這些變量是大小寫敏感的,一般使用大寫字母。Make變量可以做很多事情,例如:

              i) 存儲一個文件名列表;
              ii) 存儲可執行文件名;
              iii) 存儲編譯器選項。

              要定義一個變量,只需要在一行的開始寫下這個變量的名字,后面跟一個=號,再跟變量的值。引用變量的方法是寫一個$符號,后面跟(變量名)。我們把前面的 makefile 利用變量重寫一遍(并假設使用-Wall -O –g編譯選項):

            OBJS = main.o add.o
            CC = gcc
            CFLAGS = -Wall -O -g

            test : $(OBJS)
            $(CC) $(OBJS) -o test

            main.o : main.c add.h
            $(CC) $(CFLAGS) -c main.c -o main.o

            add.o : add.c add.h
            $(CC) $(CFLAGS) -c add.c -o add.o 

              makefile 中還可定義清除(clean)目標,可用來清除編譯過程中產生的中間文件,例如在上述makefile文件中添加下列代碼:

            clean:
            rm -f *.o

              運行make clean時,將執行rm -f *.o命令,刪除所有編譯過程中產生的中間文件。

              不管怎么說,自己動手編寫makefile仍然是很復雜和煩瑣的,而且很容易出錯。因此,GNU也為我們提供了Automake和Autoconf來輔助快速自動產生makefile,讀者可以參閱相關資料。

              5.小結

              本章主要闡述了Linux程序的編寫、編譯、調試方法及make,實際上就是引導讀者學習怎樣在Linux下編程,為后續章節做好準備。

            posted @ 2009-08-04 09:01 chaosuper 閱讀(525) | 評論 (0)編輯 收藏

            %eax
            0x8048473 : add %eax,0xfffffffc(%ebp)
            0x8048476 : incl 0xfffffff8(%ebp)
            0x8048479 : jmp 0x8048464
            0x804847b : nop
            0x804847c : lea 0x0(%esi,1),%esi
            0x8048480 : mov 0xfffffffc(%ebp),%edx
            0x8048483 : mov %edx,%eax
            0x8048485 : jmp 0x8048487
            0x8048487 : mov %ebp,%esp
            0x8048489 : pop %ebp
            0x804848a : ret
            End of assembler dump.


            查看運行時數據
            ———————

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

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


            一、表達式

            print和許多GDB的命令一樣,可以接受一個表達式,GDB會根據當前的程序運行的數
            據來計算這個表達式,既然是表達式,那么就可以是當前程序運行中的const常量、
            變量、函數等內容。可惜的是GDB不能使用你在程序中所定義的宏。

            表達式的語法應該是當前所調試的語言的語法,由于C/C++是一種大眾型的語言,所
            以,本文中的例子都是關于C/C++的。(而關于用GDB調試其它語言的章節,我將在后
            面介紹)

            在表達式中,有幾種GDB所支持的操作符,它們可以用在任何一種語言中。

            @
            是一個和數組有關的操作符,在后面會有更詳細的說明。

            ::
            指定一個在文件或是一個函數中的變量。

            {}
            表示一個指向內存地址的類型為type的一個對象。


            二、程序變量

            在GDB中,你可以隨時查看以下三種變量的值:
            1、全局變量(所有文件可見的)
            2、靜態全局變量(當前文件可見的)
            3、局部變量(當前Scope可見的)

            如果你的局部變量和全局變量發生沖突(也就是重名),一般情況下是局部變量會隱
            藏全局變量,也就是說,如果一個全局變量和一個函數中的局部變量同名時,如果當
            前停止點在函數中,用print顯示出的變量的值會是函數中的局部變量的值。如果
            此時你想查看全局變量的值時,你可以使用“::”操作符:

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

            gdb) p 'f2.c'::x

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

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

            三、數組

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

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

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

            p *array@len

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

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

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


            四、輸出格式

            一般來說,GDB會根據變量的類型輸出變量的值。但你也可以自定義GDB的輸出的格
            式。例如,你想輸出一個整數的十六進制,或是二進制來查看這個整型變量的中的
            位的情況。要做到這樣,你可以使用GDB的數據顯示格式:

            x 按十六進制格式顯示變量。
            d 按十進制格式顯示變量。
            u 按十六進制格式顯示無符號整型。
            o 按八進制格式顯示變量。
            t 按二進制格式顯示變量。
            a 按十六進制格式顯示變量。
            c 按字符格式顯示變量。
            f 按浮點數格式顯示變量。

            (gdb) p i
            $21 = 101

            (gdb) p/a i
            $22 = 0x65

            (gdb) p/c i
            $23 = 101 'e'

            (gdb) p/f i
            $24 = 1.41531145e-43

            (gdb) p/x i
            $25 = 0x65

            (gdb) p/t i
            $26 = 1100101


            五、查看內存

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

            x/

            n、f、u是可選的參數。

            n 是一個正整數,表示顯示內存的長度,也就是說從當前地址向后顯示幾個地址的內容。
            f 表示顯示的格式,參見上面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i。
            u 表示從當前地址往后請求的字節數,如果不指定的話,GDB默認是4個bytes。
              u參數可以用下面的字符來代替,b表示單字節,h表示雙字節,w表示四字節,g表示八字節。
              當我們指定了字節長度后,GDB會從指內存定的內存地址開始,讀寫指定字節,并把其當作一個值取出來。

            表示一個內存地址。

            n/f/u三個參數可以一起使用。例如:

            命令:x/3uh 0x54320 表示,從內存地址0x54320讀取內容,h表示以雙字節為一個單位,3表示三個單位,u表示按十六進制顯示。


            六、自動顯示

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

            display
            display/
            display/

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

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

            display/i $pc

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

            下面是一些和display相關的GDB命令:

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

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

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

            七、設置顯示選項

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

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

            (gdb) f
            #0 set_quotes (lq=0x34c78 "<<", rq=0x34c88 ">>")
            at input.c:530
            530 if (lquote != def_lquote)


            set print address off
            關閉函數的參數地址顯示,如:

            (gdb) set print addr off
            (gdb) f
            #0 set_quotes (lq="<<", rq=">>") at input.c:530
            530 if (lquote != def_lquote)

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

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

            set print array off
            show print array

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

            show print elements
            查看print elements的選項信息。

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

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

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

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

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

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


            set print sevenbit-strings
            設置字符顯示,是否按“\nnn”的格式顯示,如果打開,則字符串或字符數據按\nnn顯示,如“\065”。

            show print sevenbit-strings
            查看字符顯示開關是否打開。

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

            typedef enum {Tree, Bug} Species;
            typedef enum {Big_tree, Acorn, Seedling} Tree_forms;
            typedef enum {Caterpillar, Cocoon, Butterfly}
            Bug_forms;

            struct thing {
            Species it;
            union {
            Tree_forms tree;
            Bug_forms bug;
            } form;
            };

            struct thing foo = {Tree, {Acorn}};

            當打開這個開關時,執行 p foo 命令后,會如下顯示:
            $1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}

            當關閉這個開關時,執行 p foo 命令后,會如下顯示:
            $1 = {it = Tree, form = {...}}

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

            set print object
            在C++中,如果一個對象指針指向其派生類,如果打開這個選項,GDB會自動按照虛方法調用的規則顯示輸出,

            如果關閉這個選項的話,GDB就不管虛函數表了。
            這個選項默認是off。

            show print object
            查看對象選項的設置。

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

            show print static-members
            查看靜態數據成員選項設置。

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

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


            八、歷史記錄

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


            九、GDB環境變量

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

            set $foo = *object_ptr

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

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

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

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

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


            十、查看寄存器

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

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

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

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

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

            改變程序的執行
            ———————

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


            一、修改變量值

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

            (gdb) print x=4

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

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

            (gdb) whatis width
            type = double
            (gdb) p width
            $4 = 13
            (gdb) set width=47
            Invalid syntax in expression.

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

            (gdb) set var width=47

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

            二、跳轉執行

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

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

            jump

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

            注意,jump命令不會改變當前的程序棧中的內容,所以,當你從一個函數跳到另一個
            函數時,當函數運行完返回時進行彈棧操作時必然會發生錯誤,可能結果還是非常
            奇怪的,甚至于產生程序Core Dump。所以最好是同一個函數中進行跳轉。

            熟悉匯編的人都知道,程序運行時,有一個寄存器用于保存當前代碼所在的內存地
            址。所以,jump命令也就是改變了這個寄存器中的值。于是,你可以使用“set
            $pc”來更改跳轉執行的地址。如:

            set $pc = 0x485


            三、產生信號量

            使用singal命令,可以產生一個信號量給被調試的程序。如:中斷信號Ctrl+C。這
            非常方便于程序的調試,可以在程序運行的任意位置設置斷點,并在該斷點用GDB產
            生一個信號量,這種精確地在某處產生信號非常有利程序的調試。

            語法是:signal ,UNIX的系統信號量通常從1到15。所以取值也在這個范圍。

            single命令和shell的kill命令不同,系統的kill命令發信號給被調試程序時,是由
            GDB截獲的,而single命令所發出一信號則是直接發給被調試程序的。

            四、強制函數返回

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

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


            五、強制調用函數

            call

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

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

            在不同語言中使用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語言環境的命令:

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

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

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

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

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

            (gdb) set language
            The currently understood settings are:

            local or auto Automatic setting based on source file
            c Use the C language
            c++ Use the C++ language
            asm Use the Asm language
            chill Use the Chill language
            fortran Use the Fortran language
            java Use the Java language
            modula-2 Use the Modula-2 language
            pascal Use the Pascal language
            scheme Use the Scheme language

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

             

            posted @ 2009-08-04 08:39 chaosuper 閱讀(2428) | 評論 (0)編輯 收藏

            或是直接就是b func
            (gdb) b func
            Breakpoint 1 at 0x8048458: file hello.c, line 10.

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

            示例三:只記得函數的前綴,可以這樣:
            (gdb) b make_ <按TAB鍵>
            (再按下一次TAB鍵,你會看到:)
            make_a_section_from_file make_environ
            make_abs_section make_function_type
            make_blockvector make_pointer_type
            make_cleanup make_reference_type
            make_command make_symbol_completion_list
            (gdb) b make_
            GDB把所有make開頭的函數全部例出來給你查看。

            示例四:調試C++的程序時,有可以函數名一樣。如:
            (gdb) b 'bubble( M-?
            bubble(double,double) bubble(int,int)
            (gdb) b 'bubble(
            你可以查看到C++中的所有的重載函數及參數。(注:M-?和“按兩次TAB鍵”是一個意思)

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

            GDB中運行UNIX的shell程序
            ————————————

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

            shell

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

            還有一個gdb命令是make:
            make
            可以在gdb中執行make命令來重新build自己的程序。這個命令等價于“shell make ”。

            在GDB中運行程序
            ————————

            當以gdb 方式啟動gdb后,gdb會在PATH路徑和當前目錄中搜索的源文件。如要確認gdb是否讀到源文件,可使用l或list命令,看看gdb是否能列出源代碼。

            在gdb中,運行程序使用r或是run命令。程序的運行,你有可能需要設置下面四方面的事。

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

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

            3、工作目錄。
            cd
            相當于shell的cd命令。
            pwd 顯示當前的所在目錄。

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


            調試已運行的程序
            ————————

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

            暫停 / 恢復程序運行
            —————————

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

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

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

            一、設置斷點(BreakPoint)

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

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

            break
            在指定行號停住。

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

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

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

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

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

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

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


            二、設置觀察點(WatchPoint)

            觀察點一般來觀察某個表達式(變量也是一種表達式)的值是否有變化了,如果有變化,馬上停住程序。我們有下面的幾種方法來設置觀察點:

            watch
            為表達式(變量)expr設置一個觀察點。一量表達式值有變化時,馬上停住程序。

            rwatch
            當表達式(變量)expr被讀時,停住程序。

            awatch
            當表達式(變量)的值被讀或被寫時,停住程序。

            info watchpoints
            列出當前所設置了的所有觀察點。

            三、設置捕捉點(CatchPoint)

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

            catch
            當event發生時,停住程序。event可以是下面的內容:
            1、throw 一個C++拋出的異常。(throw為關鍵字)
            2、catch 一個C++捕捉到的異常。(catch為關鍵字)
            3、exec 調用系統調用exec時。(exec為關鍵字,目前此功能只在HP-UX下有用)
            4、fork 調用系統調用fork時。(fork為關鍵字,目前此功能只在HP-UX下有用)
            5、vfork 調用系統調用vfork時。(vfork為關鍵字,目前此功能只在HP-UX下有用)
            6、load 或 load 載入共享庫(動態鏈接庫)時。(load為關鍵字,目前此功能只在HP-UX下有用)
            7、unload 或 unload 卸載共享庫(動態鏈接庫)時。(unload為關鍵字,目前此功能只在HP-UX下有用)

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

            四、維護停止點

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

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

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

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

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

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

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

            enable [breakpoints] [range...]
            enable所指定的停止點,breakpoints為停止點號。

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

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

            五、停止條件維護

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

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

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


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

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

            六、為停止點設定運行命令

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


            commands [bnum]
            ... command-list ...
            end

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

            例如:

            break foo if x>0
            commands
            printf "x is %d\n",x
            continue
            end
            斷點設置在函數foo中,斷點條件是x>0,如果程序被斷住后,也就是,一旦x的值在foo函數中大于0,GDB會自動打印出x的值,并繼續運行程序。

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

            七、斷點菜單

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

            (gdb) b String::after
            [0] cancel
            [1] all
            [2] file:String.cc; line number:867
            [3] file:String.cc; line number:860
            [4] file:String.cc; line number:875
            [5] file:String.cc; line number:853
            [6] file:String.cc; line number:846
            [7] file:String.cc; line number:735
            > 2 4 6
            Breakpoint 1 at 0xb26c: file String.cc, line 867.
            Breakpoint 2 at 0xb344: file String.cc, line 875.
            Breakpoint 3 at 0xafcc: file String.cc, line 846.
            Multiple breakpoints were set.
            Use the "delete" command to delete unwanted
            breakpoints.
            (gdb)

            可見,GDB列出了所有after的重載函數,你可以選一下列表編號就行了。

            0表示放棄設置斷點,1表示所有函數都設置斷點。

            八、恢復程序運行和單步調試

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

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


            step

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

            next

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

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

            set step-mod off
            關閉step-mode模式。

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

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

            stepi 或 si
            nexti 或 ni

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

            九、信號(Signals)

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

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

            handle

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

            nostop
            當被調試的程序收到信號時,GDB不會停住程序的運行,但會打出消息告訴你收到這種信號。
            stop
            當被調試的程序收到信號時,GDB會停住你的程序。
            print
            當被調試的程序收到信號時,GDB會顯示出一條信息。
            noprint
            當被調試的程序收到信號時,GDB不會告訴你收到信號的信息。
            pass
            noignore
            當被調試的程序收到信號時,GDB不處理信號。這表示,GDB會把這個信號交給被調試程序會處理。
            nopass
            ignore
            當被調試的程序收到信號時,GDB不會讓被調試程序來處理這個信號。


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

            十、線程(Thread Stops)

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

            break thread
            break thread if ...

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

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

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

            查看棧信息
            —————

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

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

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

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

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


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

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

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

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

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

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

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

            select-frame 對應于 frame 命令。
            up-silently 對應于 up 命令。
            down-silently 對應于 down 命令。


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

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

            info frame
            info f

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

            (gdb) info f
            Stack level 0, frame at 0xbffff5d4:
            eip = 0x804845d in func (tst.c:6); saved eip 0x8048524
            called by frame at 0xbffff60c
            source language c.
            Arglist at 0xbffff5d4, args: n=250
            Locals at 0xbffff5d4, Previous frame's sp is 0x0
            Saved registers:
            ebp at 0xbffff5d4, eip at 0xbffff5d8

            info args
            打印出當前函數的參數名及其值。

            info locals
            打印出當前函數中所有局部變量及其值。

            info catch
            打印出當前的函數中的異常處理信息。


            查看源程序
            —————

            一、顯示源代碼

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

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

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

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

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

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

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

            show listsize
            查看當前listsize的設置。

            list命令還有下面的用法:

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

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

            list +
            往后顯示源代碼。

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

            行號。
            <+offset> 當前行號的正偏移量。
            <-offset> 當前行號的負偏移量。
            哪個文件的哪一行。
            函數名。
            哪個文件中的哪個函數。
            <*address> 程序運行時的語句在內存中的地址。

            二、搜索源代碼

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

            forward-search
            search
            向前面搜索。

            reverse-search
            全部搜索。

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


            三、指定源文件的路徑

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

            directory
            dir
            加一個源文件路徑到當前路徑的前面。如果你要指定多個路徑,UNIX下你可以使用“:”,Windows下你可以使用“;”。
            directory
            清除所有的自定義的源文件搜索路徑信息。

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

            四、源代碼的內存

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

            (gdb) info line tst.c:func
            Line 5 of "tst.c" starts at address 0x8048456 and ends at 0x804845d .

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

            (gdb) disassemble func
            Dump of assembler code for function func:
            0x8048450 : push %ebp
            0x8048451 : mov %esp,%ebp
            0x8048453 : sub $0x18,%esp
            0x8048456 : movl $0x0,0xfffffffc(%ebp)
            0x804845d : movl $0x1,0xfffffff8(%ebp)
            0x8048464 : mov 0xfffffff8(%ebp),%eax
            0x8048467 : cmp 0x8(%ebp),%eax
            0x804846a : jle 0x8048470
            0x804846c : jmp 0x8048480
            0x804846e : mov %esi,%esi
            0x8048470 : mov 0xfffffff8(%ebp),


             

            posted @ 2009-08-04 08:39 chaosuper 閱讀(466) | 評論 (0)編輯 收藏

            簡述

                一 列文件清單
                二:執行程序
                三:顯示數據
                四:斷點(breakpoint)
                五.斷點的管理
                六.變量的檢查和賦值
                七. 單步執行
                八.函數的調用
                九.機器語言工具
                十.信號

            GDB的使用方法

            簡述
            一 列文件清單

                * List

                  (gdb) list line1,line2

            二:執行程序
            要想運行準備調試的程序,可使用run命令,在它后面可以跟隨發給該程序的任何參數,包括標準輸入和標準輸出說明符(<和> )和外殼通配符(*、?、[、])在內。如果你使用不帶參數的run命令,gdb就再次使用你給予前一條run命令的參數,這是很有用的。利用set args 命令就可以修改發送給程序的參數,而使用show args 命令就可以查看其缺省參數的列表。

            (gdb)set args –b –x
            (gdb) show args
            backtrace命令為堆棧提供向后跟蹤功能。
            Backtrace 命令產生一張列表,包含著從最近的過程開始的所以有效過程和調用這些過程的參數。

            三:顯示數據

                * 利用print 命令可以檢查各個變量的值。

                  (gdb) print p (p為變量名)

                  print 是gdb的一個功能很強的命令,利用它可以顯示被調試的語言中任何有效的表達式。表達式除了包含你程序中的變量外,還可以包含以下內容:

               1. 對程序中函數的調用

                  (gdb) print find_entry(1,0)

               2. 數據結構和其他復雜對象

                  (gdb) print *table_start
                  $8={e=reference=’\000’,location=0x0,next=0x0}

               3. 值的歷史成分

                  (gdb)print $1 ($1為歷史記錄變量,在以后可以直接引用 $1 的值)

               4. 人為數組
                  人為數組提供了一種去顯示存儲器塊(數組節或動態分配的存儲區)內容的方法。早期的調試程序沒有很好的方法將任意的指針換成一個數組。就像對待參數一樣,讓我們查看內存中在變量h后面的10個整數,一個動態數組的語法如下所示:
                  base@length
                  因此,要想顯示在h后面的10個元素,可以使用h@10

                  (gdb)print h@10
                  $13=(-1,345,23,-234,0,0,0,98,345,10)

                * whatis 命令可以顯示某個變量的類型

                  (gdb) whatis p
                  type = int *

            四:斷點(breakpoint)
            break命令(可以簡寫為b)可以用來在調試的程序中設置斷點,該命令有如下四種形式:

                * break line-number 使程序恰好在執行給定行之前停止。
                * break function-name 使程序恰好在進入指定的函數之前停止。
                * break line-or-function if condition 如果condition(條件)是真,程序到達指定行或函數時停止。
                * break routine-name 在指定例程的入口處設置斷點

            如果該程序是由很多原文件構成的,你可以在各個原文件中設置斷點,而不是在當前的原文件中設置斷點,其方法如下:

            (gdb) break filename:line-number
            (gdb) break filename:function-name

            要想設置一個條件斷點,可以利用break if命令,如下所示:

            (gdb) break line-or-function if expr
            例:
            (gdb) break 46 if testsize==100

            從斷點繼續運行:countinue 命令
            五.斷點的管理

            1.顯示當前gdb的斷點信息:

            (gdb) info break

            他會以如下的形式顯示所有的斷點信息:

            Num Type Disp Enb Address What
            1 breakpoint keep y 0x000028bc in init_random at qsort2.c:155
            2 breakpoint keep y 0x0000291c in init_organ at qsort2.c:168

               1. 刪除指定的某個斷點:

                  (gdb) delete breakpoint 1

                  該命令將會刪除編號為1的斷點,如果不帶編號參數,將刪除所有的斷點

                  (gdb) delete breakpoint

               2. 禁止使用某個斷點

                  (gdb) disable breakpoint 1

                  該命令將禁止斷點 1,同時斷點信息的 (Enb)域將變為 n
               3. 允許使用某個斷點

                  (gdb) enable breakpoint 1

                  該命令將允許斷點 1,同時斷點信息的 (Enb)域將變為 y
               4. 清除原文件中某一代碼行上的所有斷點

                  (gdb)clean number

                  注:number 為原文件的某個代碼行的行號

            六.變量的檢查和賦值

                * whatis:識別數組或變量的類型
                * ptype:比whatis的功能更強,他可以提供一個結構的定義
                * set variable:將值賦予變量
                * print 除了顯示一個變量的值外,還可以用來賦值

            七. 單步執行

                * next 不進入的單步執行
                * step 進入的單步執行如果已經進入了某函數,而想退出該函數返回到它的調用函數中,可使用命令finish

            八.函數的調用

                * call name 調用和執行一個函數

                  (gdb) call gen_and_sork( 1234,1,0 )
                  (gdb) call printf(“abcd”)
                  $1=4

                * finish 結束執行當前函數,顯示其返回值(如果有的話)

            九.機器語言工具
            有一組專用的gdb變量可以用來檢查和修改計算機的通用寄存器,gdb提供了目前每一臺計算機中實際使用的4個寄存器的標準名字:

                * $pc : 程序計數器
                * $fp : 幀指針(當前堆棧幀)
                * $sp : 棧指針
                * $ps : 處理器狀態

            十.信號
            gdb 通常可以捕捉到發送給它的大多數信號,通過捕捉信號,它就可決定對于正在運行的進程要做些什么工作。例如,按CTRL-C將中斷信號發送給gdb,通常就會終止gdb。但是你或許不想中斷gdb,真正的目的是要中斷gdb正在運行的程序,因此,gdb要抓住該信號并停止它正在運行的程序,這樣就可以執行某些調試操作。

            Handle命令可控制信號的處理,他有兩個參數,一個是信號名,另一個是接受到信號時該作什么。幾種可能的參數是:

                * nostop 接收到信號時,不要將它發送給程序,也不要停止程序。
                * stop 接受到信號時停止程序的執行,從而允許程序調試;顯示一條表示已接受到信號的消息(禁止使用消息除外)
                * print 接受到信號時顯示一條消息
                * noprint 接受到信號時不要顯示消息(而且隱含著不停止程序運行)
                * pass 將信號發送給程序,從而允許你的程序去處理它、停止運行或采取別的動作。
                * nopass 停止程序運行,但不要將信號發送給程序。

            例如,假定你截獲SIGPIPE信號,以防止正在調試的程序接受到該信號,而且只要該信號一到達,就要求該程序停止,并通知你。要完成這一任務,可利用如下命令:

            (gdb) handle SIGPIPE stop print

            請注意,UNIX的信號名總是采用大寫字母!你可以用信號編號替代信號名如果你的程序要執行任何信號處理操作,就需要能夠測試其信號處理程序,為此,就需要一種能將信號發送給程序的簡便方法,這就是signal命令的任務。該命令的參數是一個數字或者一個名字,如SIGINT。假定你的程序已將一個專用的 SIGINT(鍵盤輸入,或CTRL-C;信號2)信號處理程序設置成采取某個清理動作,要想測試該信號處理程序,你可以設置一個斷點并使用如下命令:

            (gdb) signal 2
            continuing with signal SIGINT(2)

            該程序繼續執行,但是立即傳輸該信號,而且處理程序開始運行.
            GDB的使用方法

            GDB是一個強大的命令行調試工具。大家知道命令行的強大就是在于,其可以形成
            執行序列,形成腳本。UNIX下的軟件全是命令行的,這給程序開發提代供了極大的
            便利,命令行軟件的優勢在于,它們可以非常容易的集成在一起,使用幾個簡單的已
            有工具的命令,就可以做出一個非常強大的功能。

            于是UNIX下的軟件比Windows下的軟件更能有機地結合,各自發揮各自的長處,組合
            成更為強勁的功能。而Windows下的圖形軟件基本上是各自為營,互相不能調用,很
            不利于各種軟件的相互集成。在這里并不是要和Windows做個什么比較,所謂“寸有
            所長,尺有所短”,圖形化工具還是有不如命令行的地方。

             

            用GDB調試程序

            GDB概述
            ————

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

            所謂“寸有所
            長,尺有所短”就是這個道理。

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

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

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

            一個調試示例
            ——————

            源程序:tst.c

            1 #include
            2
            3 int func(int n)
            4 {
            5 int sum=0,i;
            6 for(i=0; i
            7 {
            8 sum+=i;
            9 }
            10 return sum;
            11 }
            12
            13
            14 main()
            15 {
            16 int i;
            17 long result = 0;
            18 for(i=1; i<=100; i++)
            19 {
            20 result += i;
            21 }
            22
            23 printf("result[1-100] = %d \n", result );
            24 printf("result[1-250] = %d \n", func(250) );
            25 }

            編譯生成執行文件:(Linux下)
            hchen/test> cc -g tst.c -o tst

            使用GDB調試:

            hchen/test> gdb tst <---------- 啟動GDB
            GNU gdb 5.1.1
            Copyright 2002 Free Software Foundation, Inc.
            GDB is free software, covered by the GNU General Public License, and you are
            welcome to change it and/or 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.
            This GDB was configured as "i386-suse-linux"...
            (gdb) l <-------------------- l命令相當于list,從第一行開始例出原碼。
            1 #include
            2
            3 int func(int n)
            4 {
            5 int sum=0,i;
            6 for(i=0; i
            7 {
            8 sum+=i;
            9 }
            10 return sum;
            (gdb) <-------------------- 直接回車表示,重復上一次命令
            11 }
            12
            13
            14 main()
            15 {
            16 int i;
            17 long result = 0;
            18 for(i=1; i<=100; i++)
            19 {
            20 result += i;
            (gdb) break 16 <-------------------- 設置斷點,在源程序第16行處。
            Breakpoint 1 at 0x8048496: file tst.c, line 16.
            (gdb) break func <-------------------- 設置斷點,在函數func()入口處。
            Breakpoint 2 at 0x8048456: file tst.c, line 5.
            (gdb) info break <-------------------- 查看斷點信息。
            Num Type Disp Enb Address What
            1 breakpoint keep y 0x08048496 in main at tst.c:16
            2 breakpoint keep y 0x08048456 in func at tst.c:5
            (gdb) r <--------------------- 運行程序,run命令簡寫
            Starting program: /home/hchen/test/tst

            Breakpoint 1, main () at tst.c:17 <---------- 在斷點處停住。
            17 long result = 0;
            (gdb) n <--------------------- 單條語句執行,next命令簡寫。
            18 for(i=1; i<=100; i++)
            (gdb) n
            20 result += i;
            (gdb) n
            18 for(i=1; i<=100; i++)
            (gdb) n
            20 result += i;
            (gdb) c <--------------------- 繼續運行程序,continue命令簡寫。
            Continuing.
            result[1-100] = 5050 <----------程序輸出。

            Breakpoint 2, func (n=250) at tst.c:5
            5 int sum=0,i;
            (gdb) n
            6 for(i=1; i<=n; i++)
            (gdb) p i <--------------------- 打印變量i的值,print命令簡寫。
            $1 = 134513808
            (gdb) n
            8 sum+=i;
            (gdb) n
            6 for(i=1; i<=n; i++)
            (gdb) p sum
            $2 = 1
            (gdb) n
            8 sum+=i;
            (gdb) p i
            $3 = 2
            (gdb) n
            6 for(i=1; i<=n; i++)
            (gdb) p sum
            $4 = 3
            (gdb) bt <--------------------- 查看函數堆棧。
            #0 func (n=250) at tst.c:5
            #1 0x080484e4 in main () at tst.c:24
            #2 0x400409ed in __libc_start_main () from /lib/libc.so.6
            (gdb) finish <--------------------- 退出函數。
            Run till exit from #0 func (n=250) at tst.c:5
            0x080484e4 in main () at tst.c:24
            24 printf("result[1-250] = %d \n", func(250) );
            Value returned is $6 = 31375
            (gdb) c <--------------------- 繼續運行。
            Continuing.
            result[1-250] = 31375 <----------程序輸出。

            Program exited with code 027. <--------程序退出,調試結束。
            (gdb) q <--------------------- 退出gdb。
            hchen/test>

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

            使用GDB
            ————

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

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

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

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

            1、gdb
            program也就是你的執行文件,一般在當然目錄下。

            2、gdb core
            用gdb同時調試一個運行程序和core文件,core是程序非法執行后core dump后產生的文件。

            3、gdb

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

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

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

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

            -core
            -c
            調試時core dump的core文件。

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

            GDB的命令概貌
            ———————

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

            /home/hchen> gdb
            GNU gdb 5.1.1
            Copyright 2002 Free Software Foundation, Inc.
            GDB is free software, covered by the GNU General Public License, and you are
            welcome to change it and/or 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.
            This GDB was configured as "i386-suse-linux".
            (gdb) help
            List of classes of commands:

            aliases -- Aliases of other commands
            breakpoints -- Making program stop at certain points
            data -- Examining data
            files -- Specifying and examining files
            internals -- Maintenance commands
            obscure -- Obscure features
            running -- Running the program
            stack -- Examining the stack
            status -- Status inquiries
            support -- Support facilities
            tracepoints -- Tracing of program execution without stopping the program
            user-defined -- User-defined commands

            Type "help" followed by a class name for a list of commands in that class.
            Type "help" followed by command name for full documentation.
            Command name abbreviations are allowed if unambiguous.
            (gdb)

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


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

            示例一:在進入函數func時,設置一個斷點。可以敲入break func,

             

            posted @ 2009-08-04 08:38 chaosuper 閱讀(288) | 評論 (0)編輯 收藏

            Perl 語言編程學習. 最近玩Linux ,發現很多腳本語言如 Perl Python Ruby. 有時間便自學,多學點東西總是有好處的. 由于Perl是用純C語言些成的,學習起來相當的簡單. 幾個小時基本熟悉了Perl腳本語言.
            posted @ 2009-08-04 07:57 chaosuper 閱讀(103) | 評論 (0)編輯 收藏

            1 用編輯器編輯包含所有操作的.sh 文件 2 修改文件的權限為可讀可執行 3 運行當前的腳本
            posted @ 2009-08-04 06:56 chaosuper 閱讀(117) | 評論 (0)編輯 收藏

            僅列出標題
            共12頁: First 4 5 6 7 8 9 10 11 12 
            性欧美大战久久久久久久| 国内精品久久久久久久久电影网 | 91精品国产综合久久精品| 亚洲国产成人精品无码久久久久久综合| 久久久久一区二区三区| 2021最新久久久视精品爱| 中文无码久久精品| 国产精品嫩草影院久久| 精品久久久久久无码专区不卡| 999久久久免费精品国产| 久久精品国产亚洲AV影院| 亚洲国产成人精品女人久久久| 国产成人香蕉久久久久| 久久夜色精品国产噜噜噜亚洲AV | 精品久久久久久久久免费影院| 久久精品国产亚洲沈樵| 91秦先生久久久久久久| 精品国产99久久久久久麻豆| 国产精品99久久不卡| 伊人久久大香线蕉av一区| 国产V综合V亚洲欧美久久| 精品久久人人妻人人做精品| 久久超碰97人人做人人爱| 精品久久久久久| 狠狠色噜噜狠狠狠狠狠色综合久久 | 国产精品久久毛片完整版| 一本久久a久久精品综合香蕉| 中文精品99久久国产 | 亚洲AV无码久久精品狠狠爱浪潮| 亚洲国产日韩欧美综合久久| 国产精品久久永久免费| 亚洲精品高清一二区久久| 国产成人久久精品区一区二区| 国内精品久久久久久久亚洲| 久久91这里精品国产2020| 精品国产综合区久久久久久 | 久久久青草青青国产亚洲免观| 久久国产福利免费| 色偷偷偷久久伊人大杳蕉| 色综合久久最新中文字幕| 欧美精品丝袜久久久中文字幕|