青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

chaosuper85

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

#

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

    關(guān)注問題:

    虛函數(shù)的作用

    虛函數(shù)的實現(xiàn)原理

    虛函數(shù)表在對象布局里的位置

    虛函數(shù)的類的sizeof

    純虛函數(shù)的作用

    多級繼承時的虛函數(shù)表內(nèi)容

    虛函數(shù)如何執(zhí)行父類代碼

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

    虛析構(gòu)函數(shù)的作用

    虛函數(shù)在QT的信號與槽中的應(yīng)用

    虛函數(shù)與inline修飾符,static修飾符

    啰嗦兩句

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

,要用好C++,就一定要學(xué)好虛函數(shù)。網(wǎng)絡(luò)上可以google到很多很多關(guān)于它的文章

,這一次的學(xué)習(xí),我不準(zhǔn)備去只是簡單的閱讀了解那些文章,而是希望通過編寫

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

    虛函數(shù)的簡單示例 !

    虛函數(shù)常常出現(xiàn)在一些抽象接口類定義里,當(dāng)然,還有一個更常見的“特例

”,那就是虛析構(gòu)函數(shù),后面會提到這個。

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


 //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是一個只具有純虛函數(shù)的接口類,這個類不能被

實例化,它唯一的用途就是抽象一些特定的接口函數(shù),當(dāng)然,在這里這個接口函

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

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

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

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

調(diào)用這個parent1對象的fun1()函數(shù)。

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

    child1::fun1()

    child2::fun1()

    很顯然,在兩次調(diào)用test_func1函數(shù)的時候,雖然傳入的參數(shù)都是一個

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

C++里類的多態(tài)。然而,這一切是怎么發(fā)生的呢?test_func1函數(shù)怎么會知道應(yīng)該

調(diào)用哪個函數(shù)的呢?我不準(zhǔn)備像其他人一樣畫若干圖來說明,我準(zhǔn)備用具體某個

編譯器產(chǎn)生的對象布局以及相應(yīng)的匯編代碼來說明這個過程(這個編譯器是

vs2008里的vc9)。

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

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

    cl  win32con.cpp  /d1reportSingleClassLayoutchild

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

的對象布局(layout)

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

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

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

的fun1函數(shù)
child1::$vftable@:
        | &child1_meta
        |  0
 0      | &child1::fun1

child1::fun1 this adjustor: 0

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

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

的fun1函數(shù)
child2::$vftable@:
        | &child2_meta
        |  0
 0      | &child2::fun1

child2::fun1 this adjustor: 0

 


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

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

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

象的fun1函數(shù)地址。

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

    所以當(dāng)test_func1進(jìn)行pp->fun1()調(diào)用的時候,會首先取出pp所指的內(nèi)存地址并按照parent1的內(nèi)存布局,獲取到vfptr指針(由于pp在兩次調(diào)用中分別指向co1和co2所以這里取得的實際上是co1的vfptr和co2的vfptr),然后從vfptr所指的虛函數(shù)表第一項(現(xiàn)在也只有 1 項)取出作為將要調(diào)用的函數(shù),由于co1和co2在各自的虛函數(shù)表里填寫了各自的fun1的地址,于是pp->fun1()最終就調(diào)用到了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的第一個虛函數(shù)的地址到eax
001C155A  call        eax //調(diào)用虛函數(shù),即fun1()

    至此,應(yīng)該比較清楚虛函數(shù)機制的基本實現(xiàn)了。然而,也許你還會有這些問題:

    虛函數(shù)表是每個子對象都有的么?

    虛函數(shù)是存在一個表里的,表的數(shù)據(jù)結(jié)構(gòu)是怎樣的,如何定位表里哪個才是我們要調(diào)用的虛函數(shù)?

    略作變化

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

    定義一個普通類

    修改parent類,在fun1前增加虛函數(shù)fun2

    在child1里和child2里編寫fun2的具體實現(xiàn),一個在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選項輸出相關(guān)的類對象布局情況:

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

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

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

    結(jié)論很明顯:

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

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

    虛函數(shù)表是以一個NULL指針標(biāo)識結(jié)束讓我們對這次簡單的示例代碼測試來做個小小總結(jié):

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

 


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

昨天開始就想要升級Redhat Linux 9.0的內(nèi)核--2.14.20的

找了個網(wǎng)頁,本來想升成2.6.24的,不成功!

后來照樣畫葫蘆,升2.6.18的,照網(wǎng)頁一樣的來,成功了!

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

下面把網(wǎng)頁的貼出來,供借鑒。

我的環(huán)境是vmware下的Redhat Linux 9.0的


連不上網(wǎng)頁的同志們見下面:

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

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

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

3、下載內(nèi)核升級工具
(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內(nèi)核和4個升級工具都下載完了(少一個也不行,如果沒有下載齊全,請不要嘗試下面的步驟,升級是不會成功的),下面回到Linux系統(tǒng)中開始配置工作吧。

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

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

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

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

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

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

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

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

(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”(如果是在實環(huán)境中,想要更多USB支持,就全選吧。我的是在虛擬機中,用不著了)
Device Drivers --->;Network device support --->Ethernet (10 or 100Mbit) ---><*> AMD PCnet32 PCI support
(3)File system--->(以下9個選項是關(guān)于ext2和ext3文件系統(tǒng)配置,全部選上)
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文件系統(tǒng)配置很重要,也是必需的,如果對Ext3、Ext2文件的支持直接編譯進(jìn)內(nèi)核,在你reboot時機器就會當(dāng)?shù)簦霈F(xiàn)如下錯誤信息:

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

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

(4)如果你在vmware下編譯內(nèi)核,硬盤用的是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,繁雜的配置工作完成了,至此,編譯前的準(zhǔn)備工作都做好了!

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

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

四、啟動新內(nèi)核
10、將新內(nèi)核和System.map文件拷貝到/boot目錄下,依次執(zhí)行以下命令:
# 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 (進(jìn)入boot目錄)

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

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

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

# vi /grub/grub.conf
進(jìn)入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(不改的話也可以,只不過重啟之后會默認(rèn)進(jìn)入2.4內(nèi)核)
(2) 將kernel行的“LABEL=/”換成根目錄所在的分區(qū)(上一步查看的就是)
此步很重要,修改錯誤將可能導(dǎo)致進(jìn)不去系統(tǒng),我把我修改后的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內(nèi)核吧。

根據(jù)以上內(nèi)容重啟后有如下問題:

A 不能上網(wǎng)

#vi /etc/modules.config

修改第一行如下:

alias eth0 pcnet32

重新激活網(wǎng)卡就行了。

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

 

1、Harbin 哈爾濱賽區(qū)(哈爾濱工業(yè)大學(xué))
網(wǎng)絡(luò)選拔賽日期:2009年9月13日 14:00-17:00
現(xiàn)場賽日期:2009年9月26日~27日
http://acm.hit.edu.cn/

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

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

4、Hefei 合肥賽區(qū)(中國科學(xué)技術(shù)大學(xué))
網(wǎng)絡(luò)選拔賽日期:2009年9月6日 14:00-17:00
現(xiàn)場賽日期:2009年10月10日~11日
http://acm.ustc.edu.cn/

5、Ningbo 寧波賽區(qū)(浙江大學(xué)寧波理工學(xué)院)
網(wǎng)絡(luò)選拔賽日期:2009年9月12日
現(xiàn)場賽日期:2009年10月17日~18日
http://acmasia09.nit.net.cn/

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

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

8、Shanghai 上海賽區(qū)(東華大學(xué))
網(wǎng)絡(luò)選拔賽日期:2009年9月20日(12:00-17:00)
現(xiàn)場賽日期:2009年10月24日~25日
http://acm.dhu.edu.cn

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

10、Wuhan 武漢賽區(qū)(武漢大學(xué))
網(wǎng)絡(luò)選拔賽日期:2009年10月3
現(xiàn)場賽日期:2009年10月31日~11月1日
http://acm.whu.edu.cn/

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

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

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

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

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

參賽報名網(wǎng)址

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

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

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

學(xué)習(xí)站點  http://doc.linuxpk.com/type1091.html 

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

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

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

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


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



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


>> 用幾次 C-n 把光標(biāo)向下移動到這一行。

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

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

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

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

當(dāng)光標(biāo)在一個單詞的中間,M-f移動到單詞的末尾。當(dāng)光標(biāo)在兩個單詞間的空白部分 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鍵用于有關(guān)語言單位(詞,句,段落) 的操作;而控制鍵用于編輯時的基本單位(字符,行等)。
這是句與行的比較:C-a和C-e移動到一行的開頭和末尾,M-a和M-e移動到一個句子的開頭和末尾。
>> 鍵入一對C-a,再鍵入一對C-e。 鍵入一對M-a,再鍵入一對M-e。

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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


通常, 立即刪除光標(biāo)前面的那個字符。


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

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

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

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

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


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



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


要在當(dāng)前光標(biāo)處找回上次被除去的文本;按C-y


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



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


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


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



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


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


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





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



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


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


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


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



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


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

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


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


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


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


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


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


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


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



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


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


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


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


保存結(jié)束后,Emacs打印出被寫的文件的文件名。你應(yīng)當(dāng)經(jīng)常的保存,萬一系統(tǒng)崩潰 的話你不至于丟失太多的工作。


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



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


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




* (緩沖)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里產(chǎn)生一個新的緩沖。要看你當(dāng)前運行的Emacs里存在的緩沖列表,輸入:


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


>> 輸入 C-x C-b



觀察每個緩沖都有一個名字,它可能也有一個它所保存的文件的文件名。一些緩沖不對應(yīng)文件。比如,叫“*Buffers List*”的緩沖沒有任何文件。這個緩沖只包含由C-x C-b產(chǎn)生的緩沖列表。你在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加上字符所能表示的數(shù)量。Emacs用X(擴展 eXtand)命令來解決這個問題。有兩種風(fēng)格:


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


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


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


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


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


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


C-x u 撤銷操作


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


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


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





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



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




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



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




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



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


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


這一行給出了有關(guān)你在編輯的文件和Emacs狀態(tài)的有用信息。


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


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


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


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


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


>> 輸入 M-x text-mode



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


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


要觀看關(guān)于你現(xiàn)在的主模式的文檔,按C-h m。


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



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


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


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


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



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


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



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


>> 把光標(biāo)移到上一段按 M-q。

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

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


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


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


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


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


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


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


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


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


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


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


>> 把光標(biāo)移到本行上按C-u 0 C-l。


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


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

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


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



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


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


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


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


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



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


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


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


>> 在你輸入的文件名后再輸入C-x 4 C-f,然后用 結(jié)束。會看到指定的文件出現(xiàn)在下面 的窗口里。光標(biāo)也在那里面。


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



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

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


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


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

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


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

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


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


(在有的地方,C-h的作用被改變了。如果按C-h在屏幕的底部沒有出現(xiàn)有關(guān)幫助的信息的話,試試用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 。


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


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



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


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


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

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



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


>> 輸入 C-h a file .




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


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


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





* 總結(jié)(CONCLUSION)
--------------------------------

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

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

  1.引言

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

  鑒于Linux操作系統(tǒng)在服務(wù)器和嵌入式系統(tǒng)領(lǐng)域愈來愈廣泛的應(yīng)用,社會上越來越需要基于Linux操作系統(tǒng)進(jìn)行編程的開發(fā)人員。

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

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

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

 

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

 

  2.GCC編譯器

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

gcc [options] [filenames]

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

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

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

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

gcc helloworld.c

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

gcc –o helloworld helloworld.c

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

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

gcc –S helloworld.c

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

 

 


  -E選項指示編譯器僅對輸入文件進(jìn)行預(yù)處理。當(dāng)這個選項被使用時,預(yù)處理器的輸出被送到標(biāo)準(zhǔn)輸出(默認(rèn)為屏幕)而不是儲存在文件里。

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

  -g選項告訴GCC產(chǎn)生能被GNU調(diào)試器使用的調(diào)試信息以便調(diào)試你的程序,可喜的是,在GCC里,我們能聯(lián)用-g和-O (產(chǎn)生優(yōu)化代碼)。

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

  3.GDB調(diào)試器

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

  file:裝入想要調(diào)試的可執(zhí)行文件。
  kill:終止正在調(diào)試的程序。
  list:列表顯示源代碼。
  next:執(zhí)行一行源代碼但不進(jìn)入函數(shù)內(nèi)部。
  step:執(zhí)行一行源代碼而且進(jìn)入函數(shù)內(nèi)部。
  run:執(zhí)行當(dāng)前被調(diào)試的程序
  quit:終止gdb
  watch:監(jiān)視一個變量的值
  break:在代碼里設(shè)置斷點,程序執(zhí)行到這里時掛起
  make:不退出gdb而重新產(chǎn)生可執(zhí)行文件
  shell:不離開gdb而執(zhí)行shell

  下面我們來演示怎樣用GDB來調(diào)試一個求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);
}

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

gcc –g –o sum sum.c

  在命令行上鍵入gdb sum并按回車鍵就可以開始調(diào)試sum了,再運行run命令執(zhí)行sum,屏幕上將看到如下內(nèi)容:

 

  list命令:

  list命令用于列出源代碼,對上述程序兩次運行l(wèi)ist,將出現(xiàn)如下畫面(源代碼被標(biāo)行號):

 

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

 

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

 

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

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

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

 

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

  要觀查當(dāng)前設(shè)置的watch,可以使用info watchpoints命令。

  next、step命令:

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

 

   next、step命令的區(qū)別在于step遇到函數(shù)調(diào)用,會跳轉(zhuǎn)到到該函數(shù)定義的開始行去執(zhí)行,而next則不進(jìn)入到函數(shù)內(nèi)部,它把函數(shù)調(diào)用語句當(dāng)作一條普通語句執(zhí)行。

  4.Make

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

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

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

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

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

/* 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));
}

  怎樣為上述三個文件產(chǎn)生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文件執(zhí)行g(shù)cc -c add.c -o add.o命令產(chǎn)生add.o目標(biāo)代碼,利用main.c和add.h文件執(zhí)行g(shù)cc -c main.c -o main.o命令產(chǎn)生main.o目標(biāo)代碼,最后利用main.o和add.o文件(兩個模塊的目標(biāo)代碼)執(zhí)行g(shù)cc main.o add.o -o test命令產(chǎn)生可執(zhí)行文件test。

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

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

  要定義一個變量,只需要在一行的開始寫下這個變量的名字,后面跟一個=號,再跟變量的值。引用變量的方法是寫一個$符號,后面跟(變量名)。我們把前面的 makefile 利用變量重寫一遍(并假設(shè)使用-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)目標(biāo),可用來清除編譯過程中產(chǎn)生的中間文件,例如在上述makefile文件中添加下列代碼:

clean:
rm -f *.o

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

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

  5.小結(jié)

  本章主要闡述了Linux程序的編寫、編譯、調(diào)試方法及make,實際上就是引導(dǎo)讀者學(xué)習(xí)怎樣在Linux下編程,為后續(xù)章節(jié)做好準(zhǔn)備。

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

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

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

示例三:只記得函數(shù)的前綴,可以這樣:
(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開頭的函數(shù)全部例出來給你查看。

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

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

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

在gdb環(huán)境中,你可以執(zhí)行UNIX的shell的命令,使用gdb的shell命令來完成:

shell

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

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

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

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

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

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

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

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

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


調(diào)試已運行的程序
————————

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

暫停 / 恢復(fù)程序運行
—————————

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

當(dāng)進(jìn)程被gdb停住時,你可以使用info program 來查看程序的是否在運行,進(jìn)程號,被
暫停的原因。

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

一、設(shè)置斷點(BreakPoint)

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

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

break
在指定行號停住。

break +offset
break -offset
在當(dāng)前行號的前面或后面的offset行停住。offiset為自然數(shù)。

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

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

break *address
在程序運行的內(nèi)存地址處停住。

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

break ... if
...可以是上述的參數(shù),condition表示條件,在條件成立時停住。比如在循環(huán)境體中,可以設(shè)置break if i=100,表示當(dāng)i為100時停住程序。

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


二、設(shè)置觀察點(WatchPoint)

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

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

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

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

info watchpoints
列出當(dāng)前所設(shè)置了的所有觀察點。

三、設(shè)置捕捉點(CatchPoint)

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

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

tcatch
只設(shè)置一次捕捉點,當(dāng)程序停住以后,應(yīng)點被自動刪除。

四、維護停止點

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

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

clear
clear
清除所有設(shè)置在函數(shù)上的停止點。

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

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

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

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

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

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

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

五、停止條件維護

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

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

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


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

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

六、為停止點設(shè)定運行命令

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


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

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

例如:

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

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

七、斷點菜單

在C++中,可能會重復(fù)出現(xiàn)同一個名字的函數(shù)若干次(函數(shù)重載),在這種情況
下,break 不能告訴GDB要停在哪個函數(shù)的入口。當(dāng)然,你可以使用break 也就是把
函數(shù)的參數(shù)類型告訴GDB,以指定一個函數(shù)。否則的話,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的重載函數(shù),你可以選一下列表編號就行了。

0表示放棄設(shè)置斷點,1表示所有函數(shù)都設(shè)置斷點。

八、恢復(fù)程序運行和單步調(diào)試

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

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


step

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

next

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

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

set step-mod off
關(guān)閉step-mode模式。

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

until 或 u
當(dāng)你厭倦了在一個循環(huán)體內(nèi)單步跟蹤時,這個命令可以運行程序直到退出循環(huán)體。

stepi 或 si
nexti 或 ni

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

九、信號(Signals)

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

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

handle

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

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


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

十、線程(Thread Stops)

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

break thread
break thread if ...

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

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

當(dāng)你的程序被GDB停住時,所有的運行線程都會被停住。這方便你你查看運行程序
的總體情況。而在你恢復(fù)程序運行時,所有的線程也會被恢復(fù)運行。那怕是主進(jìn)程
在被單步調(diào)試時。

查看棧信息
—————

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

下面是一些查看函數(shù)調(diào)用棧信息的GDB命令:

backtrace
bt
打印當(dāng)前的函數(shù)調(diào)用棧的所有信息。如:

(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

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


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

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

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

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

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

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

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

select-frame 對應(yīng)于 frame 命令。
up-silently 對應(yīng)于 up 命令。
down-silently 對應(yīng)于 down 命令。


查看當(dāng)前棧層的信息,你可以用以下GDB命令:

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

info frame
info f

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

(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
打印出當(dāng)前函數(shù)的參數(shù)名及其值。

info locals
打印出當(dāng)前函數(shù)中所有局部變量及其值。

info catch
打印出當(dāng)前的函數(shù)中的異常處理信息。


查看源程序
—————

一、顯示源代碼

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

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

list
顯示函數(shù)名為function的函數(shù)的源程序。

list
顯示當(dāng)前行后面的源程序。

list -
顯示當(dāng)前行前面的源程序。

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

set listsize
設(shè)置一次顯示源代碼的行數(shù)。

show listsize
查看當(dāng)前l(fā)istsize的設(shè)置。

list命令還有下面的用法:

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

list ,
顯示從當(dāng)前行到last行之間的源代碼。

list +
往后顯示源代碼。

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

行號。
<+offset> 當(dāng)前行號的正偏移量。
<-offset> 當(dāng)前行號的負(fù)偏移量。
哪個文件的哪一行。
函數(shù)名。
哪個文件中的哪個函數(shù)。
<*address> 程序運行時的語句在內(nèi)存中的地址。

二、搜索源代碼

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

forward-search
search
向前面搜索。

reverse-search
全部搜索。

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


三、指定源文件的路徑

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

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

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

四、源代碼的內(nèi)存

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

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

還有一個命令(disassemble)你可以查看源程序的當(dāng)前執(zhí)行時的機器碼,這個命令
會把目前內(nèi)存中的指令dump出來。如下面的示例表示查看函數(shù)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 閱讀(479) | 評論 (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.


查看運行時數(shù)據(jù)
———————

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

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


一、表達(dá)式

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

表達(dá)式的語法應(yīng)該是當(dāng)前所調(diào)試的語言的語法,由于C/C++是一種大眾型的語言,所
以,本文中的例子都是關(guān)于C/C++的。(而關(guān)于用GDB調(diào)試其它語言的章節(jié),我將在后
面介紹)

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

@
是一個和數(shù)組有關(guān)的操作符,在后面會有更詳細(xì)的說明。

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

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


二、程序變量

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

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

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

gdb) p 'f2.c'::x

當(dāng)然,“::”操作符會和C++中的發(fā)生沖突,GDB能自動識別“::” 是否C++的操作符,所以你不必?fù)?dān)心在調(diào)試C++程序時會出現(xiàn)異常。

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

三、數(shù)組

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

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

于是,在GDB調(diào)試過程中,你可以以如下命令顯示出這個動態(tài)數(shù)組的取值:

p *array@len

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

(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}

如果是靜態(tài)數(shù)組的話,可以直接用print數(shù)組名,就可以顯示數(shù)組中所有數(shù)據(jù)的內(nèi)容了。


四、輸出格式

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

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

(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


五、查看內(nèi)存

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

x/

n、f、u是可選的參數(shù)。

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

表示一個內(nèi)存地址。

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

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


六、自動顯示

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

display
display/
display/

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

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

display/i $pc

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

下面是一些和display相關(guān)的GDB命令:

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

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

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

七、設(shè)置顯示選項

GDB中關(guān)于顯示的選項比較多,這里我只例舉大多數(shù)常用的選項。

set print address
set print address on
打開地址輸出,當(dāng)程序顯示函數(shù)信息時,GDB會顯出函數(shù)的參數(shù)地址。系統(tǒng)默認(rèn)為打開的,如:

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


set print address off
關(guān)閉函數(shù)的參數(shù)地址顯示,如:

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

show print address
查看當(dāng)前地址顯示選項是否打開。

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

set print array off
show print array

set print elements
這個選項主要是設(shè)置數(shù)組的,如果你的數(shù)組太大了,那么就可以指定一個來指定數(shù)據(jù)顯示的最大長度,
當(dāng)?shù)竭_(dá)這個長度時,GDB就不再往下顯示了。
如果設(shè)置為0,則表示不限制。

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

set print null-stop
如果打開了這個選項,那么當(dāng)顯示字符串時,遇到結(jié)束符則停止顯示。這個選項默認(rèn)為off。

set print pretty on
如果打開printf pretty這個選項,那么當(dāng)GDB顯示結(jié)構(gòu)體時會比較漂亮。如:

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

set print pretty off
關(guān)閉printf pretty這個選項,GDB顯示結(jié)構(gòu)體時會如下顯示:

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

show print pretty
查看GDB是如何顯示結(jié)構(gòu)體的。


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

show print sevenbit-strings
查看字符顯示開關(guān)是否打開。

set print union
設(shè)置顯示結(jié)構(gòu)體時,是否顯式其內(nèi)的聯(lián)合體數(shù)據(jù)。例如有以下數(shù)據(jù)結(jié)構(gòu):

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}};

當(dāng)打開這個開關(guān)時,執(zhí)行 p foo 命令后,會如下顯示:
$1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}

當(dāng)關(guān)閉這個開關(guān)時,執(zhí)行 p foo 命令后,會如下顯示:
$1 = {it = Tree, form = {...}}

show print union
查看聯(lián)合體數(shù)據(jù)的顯示方式

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

如果關(guān)閉這個選項的話,GDB就不管虛函數(shù)表了。
這個選項默認(rèn)是off。

show print object
查看對象選項的設(shè)置。

set print static-members
這個選項表示,當(dāng)顯示一個C++對象中的內(nèi)容是,是否顯示其中的靜態(tài)數(shù)據(jù)成員。默認(rèn)是on。

show print static-members
查看靜態(tài)數(shù)據(jù)成員選項設(shè)置。

set print vtbl
當(dāng)此選項打開時,GDB將用比較規(guī)整的格式來顯示虛函數(shù)表時。其默認(rèn)是關(guān)閉的。

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


八、歷史記錄

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


九、GDB環(huán)境變量

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

set $foo = *object_ptr

使用環(huán)境變量時,GDB會在你第一次使用時創(chuàng)建這個變量,而在以后的使用中,則直接對其賦值。
環(huán)境變量沒有類型,你可以給環(huán)境變量定義任一的類型。
包括結(jié)構(gòu)體和數(shù)組。

show convenience
該命令查看當(dāng)前所設(shè)置的所有的環(huán)境變量。

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

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

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


十、查看寄存器

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

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

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

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

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

改變程序的執(zhí)行
———————

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


一、修改變量值

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

(gdb) print x=4

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

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

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

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

(gdb) set var width=47

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

二、跳轉(zhuǎn)執(zhí)行

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

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

jump

這里的
是代碼行的內(nèi)存地址。

注意,jump命令不會改變當(dāng)前的程序棧中的內(nèi)容,所以,當(dāng)你從一個函數(shù)跳到另一個
函數(shù)時,當(dāng)函數(shù)運行完返回時進(jìn)行彈棧操作時必然會發(fā)生錯誤,可能結(jié)果還是非常
奇怪的,甚至于產(chǎn)生程序Core Dump。所以最好是同一個函數(shù)中進(jìn)行跳轉(zhuǎn)。

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

set $pc = 0x485


三、產(chǎn)生信號量

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

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

single命令和shell的kill命令不同,系統(tǒng)的kill命令發(fā)信號給被調(diào)試程序時,是由
GDB截獲的,而single命令所發(fā)出一信號則是直接發(fā)給被調(diào)試程序的。

四、強制函數(shù)返回

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

return
return
使用return命令取消當(dāng)前函數(shù)的執(zhí)行,并立即返回,如果指定了,那么該表達(dá)式的值會被認(rèn)作函數(shù)的返回值。


五、強制調(diào)用函數(shù)

call

表達(dá)式中可以一是函數(shù),以此達(dá)到強制調(diào)用函數(shù)的目的。并顯示函數(shù)的返回值,如
果函數(shù)返回值是void,那么就不顯示。

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

在不同語言中使用GDB
——————————

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

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


下面是幾個相關(guān)于GDB語言環(huán)境的命令:

show language
查看當(dāng)前的語言環(huán)境。如果GDB不能識為你所調(diào)試的編程語言,那么,C語言被認(rèn)為是默認(rèn)的環(huán)境。

info frame
查看當(dāng)前函數(shù)的程序語言。

info source
查看當(dāng)前文件的程序語言。

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

當(dāng)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后跟上被列出來的程序語言名,來設(shè)置當(dāng)前的語言環(huán)境。

 

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

簡述

    一 列文件清單
    二:執(zhí)行程序
    三:顯示數(shù)據(jù)
    四:斷點(breakpoint)
    五.?dāng)帱c的管理
    六.變量的檢查和賦值
    七. 單步執(zhí)行
    八.函數(shù)的調(diào)用
    九.機器語言工具
    十.信號

GDB的使用方法

簡述
一 列文件清單

    * List

      (gdb) list line1,line2

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

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

三:顯示數(shù)據(jù)

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

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

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

   1. 對程序中函數(shù)的調(diào)用

      (gdb) print find_entry(1,0)

   2. 數(shù)據(jù)結(jié)構(gòu)和其他復(fù)雜對象

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

   3. 值的歷史成分

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

   4. 人為數(shù)組
      人為數(shù)組提供了一種去顯示存儲器塊(數(shù)組節(jié)或動態(tài)分配的存儲區(qū))內(nèi)容的方法。早期的調(diào)試程序沒有很好的方法將任意的指針換成一個數(shù)組。就像對待參數(shù)一樣,讓我們查看內(nèi)存中在變量h后面的10個整數(shù),一個動態(tài)數(shù)組的語法如下所示:
      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)可以用來在調(diào)試的程序中設(shè)置斷點,該命令有如下四種形式:

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

如果該程序是由很多原文件構(gòu)成的,你可以在各個原文件中設(shè)置斷點,而不是在當(dāng)前的原文件中設(shè)置斷點,其方法如下:

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

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

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

從斷點繼續(xù)運行:countinue 命令
五.?dāng)帱c的管理

1.顯示當(dāng)前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的斷點,如果不帶編號參數(shù),將刪除所有的斷點

      (gdb) delete breakpoint

   2. 禁止使用某個斷點

      (gdb) disable breakpoint 1

      該命令將禁止斷點 1,同時斷點信息的 (Enb)域?qū)⒆優(yōu)?n
   3. 允許使用某個斷點

      (gdb) enable breakpoint 1

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

      (gdb)clean number

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

六.變量的檢查和賦值

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

七. 單步執(zhí)行

    * next 不進(jìn)入的單步執(zhí)行
    * step 進(jìn)入的單步執(zhí)行如果已經(jīng)進(jìn)入了某函數(shù),而想退出該函數(shù)返回到它的調(diào)用函數(shù)中,可使用命令finish

八.函數(shù)的調(diào)用

    * call name 調(diào)用和執(zhí)行一個函數(shù)

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

    * finish 結(jié)束執(zhí)行當(dāng)前函數(shù),顯示其返回值(如果有的話)

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

    * $pc : 程序計數(shù)器
    * $fp : 幀指針(當(dāng)前堆棧幀)
    * $sp : 棧指針
    * $ps : 處理器狀態(tài)

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

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

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

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

(gdb) handle SIGPIPE stop print

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

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

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

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

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

 

用GDB調(diào)試程序

GDB概述
————

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

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

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

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

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

一個調(diào)試示例
——————

源程序: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 }

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

使用GDB調(diào)試:

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命令相當(dāng)于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) <-------------------- 直接回車表示,重復(fù)上一次命令
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 <-------------------- 設(shè)置斷點,在源程序第16行處。
Breakpoint 1 at 0x8048496: file tst.c, line 16.
(gdb) break func <-------------------- 設(shè)置斷點,在函數(shù)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 <--------------------- 單條語句執(zhí)行,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 <--------------------- 繼續(xù)運行程序,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 <--------------------- 查看函數(shù)堆棧。
#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 <--------------------- 退出函數(shù)。
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 <--------------------- 繼續(xù)運行。
Continuing.
result[1-250] = 31375 <----------程序輸出。

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

好了,有了以上的感性認(rèn)識,還是讓我們來系統(tǒng)地認(rèn)識一下gdb吧。

使用GDB
————

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

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

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

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

1、gdb
program也就是你的執(zhí)行文件,一般在當(dāng)然目錄下。

2、gdb core
用gdb同時調(diào)試一個運行程序和core文件,core是程序非法執(zhí)行后core dump后產(chǎn)生的文件。

3、gdb

如果你的程序是一個服務(wù)程序,那么你可以指定這個服務(wù)程序運行時的進(jìn)程ID。
gdb會自動attach上去,并調(diào)試他。program應(yīng)該在PATH環(huán)境變量中搜索得到。

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

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

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

-core
-c
調(diào)試時core dump的core文件。

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

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

啟動gdb后,就你被帶入gdb的調(diào)試環(huán)境中,就可以使用gdb的命令開始調(diào)試程序了,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,查看設(shè)置斷點的所
有命令。也可以直接help 來查看命令的幫助。


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

示例一:在進(jìn)入函數(shù)func時,設(shè)置一個斷點。可以敲入break func,

 

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

Perl 語言編程學(xué)習(xí). 最近玩Linux ,發(fā)現(xiàn)很多腳本語言如 Perl Python Ruby. 有時間便自學(xué),多學(xué)點東西總是有好處的. 由于Perl是用純C語言些成的,學(xué)習(xí)起來相當(dāng)?shù)暮唵? 幾個小時基本熟悉了Perl腳本語言.
posted @ 2009-08-04 07:57 chaosuper 閱讀(108) | 評論 (0)編輯 收藏

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

僅列出標(biāo)題
共12頁: First 4 5 6 7 8 9 10 11 12 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美另类一区| 欧美日韩亚洲一区二区| 亚洲裸体视频| 国产精品成人一区| 国产精品免费网站| 国产精品不卡在线| 欧美精品一区二区久久婷婷| 久久美女性网| 久久综合狠狠综合久久综合88| 亚洲男人的天堂在线aⅴ视频| 夜夜嗨av一区二区三区网站四季av | 欧美+日本+国产+在线a∨观看| 久久免费视频在线| 亚洲欧美日韩另类| 亚洲美女中文字幕| 91久久精品久久国产性色也91| 久久精品国产99国产精品| 久久精品国产77777蜜臀| 久热精品视频在线免费观看| 欧美日韩精品二区第二页| 久久综合狠狠综合久久综青草| 亚洲欧美制服另类日韩| 老司机精品视频一区二区三区| 亚洲国产小视频在线观看| 亚洲国产高清在线观看视频| 亚洲国产欧美日韩| 欧美一区二区黄| 欧美一区永久视频免费观看| 日韩图片一区| 欧美在线综合视频| 欧美激情自拍| 亚洲国产mv| 亚洲国产精品va| 模特精品在线| 极品少妇一区二区| 欧美日韩和欧美的一区二区| 日韩午夜激情| 久久综合婷婷| 猫咪成人在线观看| 国产精品免费看片| 一区二区三区欧美成人| 日韩亚洲国产精品| 亚洲综合视频在线| 国产亚洲欧洲997久久综合| 欧美一区二区三区日韩视频| 久久婷婷久久一区二区三区| 激情久久五月| 免费久久99精品国产自在现线| 小黄鸭视频精品导航| 国产精品theporn88| 亚洲欧美日韩中文播放| 欧美专区亚洲专区| 日韩一级精品视频在线观看| 99精品欧美一区二区三区| 国产香蕉久久精品综合网| 欧美fxxxxxx另类| 欧美性猛交视频| 久久久久免费观看| 欧美大片在线观看一区二区| 亚洲影院免费观看| 久久国产精品高清| 欧美在线视频一区二区| 亚洲影音一区| 国产精品久久久久久久久借妻 | 欧美一区二区三区免费在线看| 韩国av一区二区| 午夜精品一区二区三区在线| 亚洲影视在线| 91久久综合亚洲鲁鲁五月天| 久久久99免费视频| 国产精品日产欧美久久久久| 亚洲日本国产| 国产九九精品| 亚洲欧美日韩另类| 欧美一区三区三区高中清蜜桃| 欧美色播在线播放| 亚洲美女色禁图| 亚洲三级免费观看| 久久久人成影片一区二区三区观看 | 国产日韩精品一区| 亚洲宅男天堂在线观看无病毒| 一区二区三区国产盗摄| 欧美无砖砖区免费| 一区二区欧美视频| 欧美一级在线视频| 国产一区二区三区在线观看精品 | 亚洲精品视频在线观看网站| 亚洲人成人99网站| 欧美国产一区二区三区激情无套| 蜜臀a∨国产成人精品| 亚洲第一页中文字幕| 欧美大片免费观看在线观看网站推荐| 女人香蕉久久**毛片精品| 一本大道久久a久久精二百| 国产精品乱人伦中文| 欧美成人视屏| 久久综合给合久久狠狠狠97色69| 国产精品99久久久久久久女警| 免费不卡在线观看av| 午夜精品久久久久久久久久久久| 亚洲国产成人精品女人久久久 | 欧美影视一区| 欧美一区2区三区4区公司二百| 亚洲天堂av综合网| 午夜综合激情| 免播放器亚洲一区| 一区二区三区波多野结衣在线观看| 久久精品人人爽| 久久精品国产清高在天天线| 欧美亚洲视频一区二区| 亚洲男人影院| 欧美亚洲视频一区二区| 午夜精品美女久久久久av福利| 亚洲小视频在线| 午夜影院日韩| 久久黄色级2电影| 久久综合久久综合久久综合| 久久精品成人一区二区三区蜜臀 | 欧美视频免费看| 国产精品国产三级国产专播精品人| 欧美日韩少妇| 国产目拍亚洲精品99久久精品| 国产精品最新自拍| 亚洲国产一区二区三区在线播 | 欧美xx69| 亚洲三级电影在线观看| 一区二区欧美激情| 亚洲欧美国产精品专区久久| 性欧美超级视频| 免费看精品久久片| 欧美日韩性生活视频| 国产婷婷色综合av蜜臀av| 亚洲国产1区| 欧美一区二区三区四区在线| 欧美电影在线播放| 午夜精品久久久久久久| 麻豆精品网站| 红桃视频一区| 午夜日韩电影| 亚洲一区二区三区乱码aⅴ蜜桃女| 久久人人爽人人爽| 狠狠网亚洲精品| 久久久久国产精品www| 亚洲人成亚洲人成在线观看图片| 亚洲欧美日韩在线观看a三区| 欧美日韩免费观看一区二区三区 | 久久久精品国产一区二区三区 | 久久久久久久999精品视频| 欧美激情影音先锋| 免费国产自线拍一欧美视频| 韩日在线一区| 久久人人爽国产| 香蕉久久a毛片| 国内精品视频666| 两个人的视频www国产精品| 久久激情久久| 欧美在线观看一区二区| 欧美三区视频| 亚洲欧美视频一区| 午夜视频在线观看一区二区| 国产午夜精品理论片a级大结局 | 这里是久久伊人| 99视频日韩| 国产在线视频欧美| 欧美黑人一区二区三区| 欧美精品一区二区三区在线播放| 日韩午夜一区| 亚欧成人精品| 日韩视频一区二区三区| 香蕉久久久久久久av网站| 伊人久久av导航| 亚洲一区二区三区777| 亚洲动漫精品| 先锋资源久久| 午夜精品久久久久久久久久久久 | 精品成人在线| 亚洲伦理在线| 在线不卡免费欧美| 欧美一区亚洲| 欧美在线观看一区二区| 欧美日韩国产电影| 亚洲电影下载| 亚洲激情视频在线播放| 久久久久久成人| 免费在线亚洲欧美| 国产色产综合色产在线视频| 夜夜爽www精品| 亚洲午夜视频在线观看| 欧美激情一区二区在线| 亚洲丶国产丶欧美一区二区三区 | 欧美精品在线看| 欧美黄色影院| 日韩视频一区二区在线观看| 欧美黑人在线播放| 亚洲日韩欧美视频一区| 你懂的亚洲视频| 99re在线精品| 久久国产66| 亚洲电影成人| 欧美激情一二三区|