日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

gcc选项 和 gdb 使用

發布時間:2025/3/15 编程问答 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gcc选项 和 gdb 使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

CC 編譯詳解

GNU CC(簡稱為Gcc)是GNU項目中符合ANSI C標準的編譯系統,能夠編譯用C、C++和Object C等語言編寫的程序。Gcc不僅功能強大,而且可以編譯如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多種語言,而且Gcc又是一個交叉平臺編譯器,它能夠在當前CPU平臺上為多種不同體系結構的硬件平臺開發軟件,因此尤其適合在嵌入式領域的開發編譯。本章中的示例,除非特別注明,否則均采用Gcc版本為4.0.0。

?

?

GCC入門基礎

表3.6 Gcc所支持后綴名解釋

后 綴 名

所對應的語言

后 綴 名

所對應的語言

.c

C原始程序

.s/.S

匯編語言原始程序

.C/.cc/.cxx

C++原始程序

.h

預處理文件(頭文件)

.m

Objective-C原始程序

.o

目標文件

.i

已經過預處理的C原始程序

.a/.so

編譯后的庫文件

.ii

已經過預處理的C++原始程序

? ?

如本章開頭提到的,Gcc的編譯流程分為了四個步驟,分別為:

· 預處理(Pre-Processing)

· 編譯(Compiling)

· 匯編(Assembling)

· 鏈接(Linking)

下面就具體來查看一下Gcc是如何完成四個步驟的。

首先,有以下hello.c源代碼

#include<stdio.h>

int main()

{

printf("Hello! This is our embedded world!n");

return 0;

}

(1)預處理階段

在該階段,編譯器將上述代碼中的stdio.h編譯進來,并且用戶可以使用Gcc的選項”-E”進行查看,該選項的作用是讓Gcc在預處理結束后停止編譯過程。

?

注意

Gcc指令的一般格式為:Gcc [選項] 要編譯的文件 [選項] [目標文件]

其中,目標文件可缺省,Gcc默認生成可執行的文件,命為:編譯文件.out

?

[root@localhost Gcc]#?Gcc –E hello.c –o hello.i

?

在此處,選項”-o”是指目標文件,由表3.6可知,”.i”文件為已經過預處理的C原始程序。以下列出了hello.i文件的部分內容:

?

typedef int (*__gconv_trans_fct) (struct __gconv_step *,

struct __gconv_step_data *, void *,

__const unsigned char *,

__const unsigned char **,

__const unsigned char *, unsigned char **,

size_t *);

# 2 "hello.c" 2

int main()

{

printf("Hello! This is our embedded world!n");

return 0;

}

?

由此可見,Gcc確實進行了預處理,它把”stdio.h”的內容插入到hello.i文件中。

(2)編譯階段

接下來進行的是編譯階段,在這個階段中,Gcc首先要檢查代碼的規范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤后,Gcc把代碼翻譯成匯編語言。用戶可以使用”-S”選項來進行查看,該選項只進行編譯而不進行匯編,生成匯編代碼。

?

[root@localhost Gcc]# Gcc –S hello.i –o hello.s

?

以下列出了hello.s的內容,可見Gcc已經將其轉化為匯編了,感興趣的讀者可以分析一下這一行簡單的C語言小程序是如何用匯編代碼實現的。

?

.file "hello.c"

.section .rodata

.align 4

.LC0:

.string"Hello! This is our embedded world!"

.text

.globl main

.type main, @function

main:

pushl �p

movl %esp, �p

subl $8, %esp

andl $-16, %esp

movl $0, �x

addl $15, �x

addl $15, �x

shrl $4, �x

sall $4, �x

subl �x, %esp

subl $12, %esp

pushl $.LC0

call puts

addl $16, %esp

movl $0, �x

leave

ret

.size main, .-main

.ident "GCC: (GNU) 4.0.0 20050519 (Red Hat 4.0.0-8)"

.section .note.GNU-stack,"",@progbits

?

(3)匯編階段

匯編階段是把編譯階段生成的”.s”文件轉成目標文件,讀者在此可使用選項”-c”就可看到匯編代碼已轉化為”.o”的二進制目標代碼了。如下所示:

?

[root@localhost Gcc]# Gcc –c?hello.s –o hello.o

?

(4)鏈接階段

在成功編譯之后,就進入了鏈接階段。在這里涉及到一個重要的概念:函數庫。

讀者可以重新查看這個小程序,在這個程序中并沒有定義”printf”的函數實現,且在預編譯中包含進的”stdio.h”中也只有該函數的聲明,而沒有定義函數的實現,那么,是在哪里實現”printf”函數的呢?最后的答案是:系統把這些函數實現都被做到名為libc.so.6的庫文件中去了,在沒有特別指定時,Gcc會到系統默認的搜索路徑”/usr/lib”下進行查找,也就是鏈接到libc.so.6庫函數中去,這樣就能實現函數”printf”了,而這也就是鏈接的作用。

函數庫一般分為靜態庫和動態庫兩種。靜態庫是指編譯鏈接時,把庫文件的代碼全部加入到可執行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其后綴名一般為”.a”。動態庫與之相反,在編譯鏈接時并沒有把庫文件的代碼加入到可執行文件中,而是在程序執行時由運行時鏈接文件加載庫,這樣可以節省系統的開銷。動態庫一般后綴名為”.so”,如前面所述的libc.so.6就是動態庫。Gcc在編譯時默認使用動態庫。

完成了鏈接之后,Gcc就可以生成可執行文件,如下所示。

?

[root@localhost Gcc]# Gcc hello.o –o hello

?

運行該可執行文件,出現正確的結果如下。

?

[root@localhost Gcc]# ./hello

Hello! This is our embedded world!

Gcc編譯選項分析

Gcc有超過100個的可用選項,主要包括總體選項、告警和出錯選項、優化選項和體系結構相關選項。以下對每一類中最常用的選項進行講解。

(1)總體選項

Gcc的總結選項如表3.7所示,很多在前面的示例中已經有所涉及。

表3.7 Gcc總體選項列表

后綴名

所對應的語言

-c

只是編譯不鏈接,生成目標文件“.o”

-S

只是編譯不匯編,生成匯編代碼

-E

只進行預編譯,不做其他處理

-g

在可執行程序中包含標準調試信息

-o file

把輸出文件輸出到file里

-v

打印出編譯器內部編譯各過程的命令行信息和編譯器的版本

-I dir

在頭文件的搜索路徑列表中添加dir目錄

-L dir

在庫文件的搜索路徑列表中添加dir目錄

-static

鏈接靜態庫

-llibrary

連接名為library的庫文件

?

對于“-c”、“-E”、“-o”、“-S”選項在前一小節中已經講解了其使用方法,在此主要講解另外兩個非常常用的庫依賴選項“-I dir”和“-L dir”。

· “-I dir”

正如上表中所述,“-I dir”選項可以在頭文件的搜索路徑列表中添加dir目錄。由于Linux中頭文件都默認放到了“/usr/include/”目錄下,因此,當用戶希望添加放置在其他位置的頭文件時,就可以通過“-I dir”選項來指定,這樣,Gcc就會到相應的位置查找對應的目錄。

比如在“/root/workplace/Gcc”下有兩個文件:

?

?

#include<my.h>

int main()

{

printf(“Hello!!n”);

return 0;

}

?

#include<stdio.h>

?

這樣,就可在Gcc命令行中加入“-I”選項:

?

[root@localhost Gcc] Gcc?hello1.c –I /root/workplace/Gcc/ -o hello1

?

這樣,Gcc就能夠執行出正確結果。

?

小知識

在include語句中,“<>”表示在標準路徑中搜索頭文件,““””表示在本目錄中搜索。故在上例中,可把hello1.c的“#include<my.h>”改為“#include “my.h””,就不需要加上“-I”選項了。

?

· “-L dir”

選項“-L dir”的功能與“-I dir”類似,能夠在庫文件的搜索路徑列表中添加dir目錄。例如有程序hello_sq.c需要用到目錄“/root/workplace/Gcc/lib”下的一個動態庫libsunq.so,則只需鍵入如下命令即可:

?

[root@localhost Gcc]?Gcc hello_sq.c –L /root/workplace/Gcc/lib –lsunq –o hello_sq

?

需要注意的是,“-I dir”和“-L dir”都只是指定了路徑,而沒有指定文件,因此不能在路徑中包含文件名。

另外值得詳細解釋一下的是“-l”選項,它指示Gcc去連接庫文件libsunq.so。由于在Linux下的庫文件命名時有一個規定:必須以lib三個字母開頭。因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母。也就是說Gcc在對”-lsunq”進行處理時,會自動去鏈接名為libsunq.so的文件。

(2)告警和出錯選項

Gcc的告警和出錯選項如表3.8所示。

表3.8 Gcc總體選項列表

選項

含義

-ansi

支持符合ANSI標準的C程序

-pedantic

允許發出ANSI C標準所列的全部警告信息

選項

含義

-pedantic-error

允許發出ANSI C標準所列的全部錯誤信息

-w

關閉所有告警

-Wall

允許發出Gcc提供的所有有用的報警信息

-werror

把所有的告警信息轉化為錯誤信息,并在告警發生時終止編譯過程

?

下面結合實例對這幾個告警和出錯選項進行簡單的講解。

如有以下程序段:

?

#include<stdio.h>

?

void main()

{

long long tmp = 1;

printf(“This is a bad code!n”);

return 0;

}

?

這是一個很糟糕的程序,讀者可以考慮一下有哪些問題?

· “-ansi”

該選項強制Gcc生成標準語法所要求的告警信息,盡管這還并不能保證所有沒有警告的程序都是符合ANSI C標準的。運行結果如下所示:

?

[root@localhost Gcc]# Gcc?–ansi warning.c –o warning

warning.c: 在函數“main”中:

warning.c:7 警告:在無返回值的函數中,“return”帶返回值

warning.c:4 警告:“main”的返回類型不是“int”

?

可以看出,該選項并沒有發現”long long”這個無效數據類型的錯誤。

· “-pedantic”

允許發出ANSI C標準所列的全部警告信息,同樣也保證所有沒有警告的程序都是符合ANSI C標準的。其運行結果如下所示:

?

[root@localhost Gcc]# Gcc –pedantic warning.c –o warning

warning.c: 在函數“main”中:

warning.c:5 警告:ISO C90不支持“long long”

warning.c:7 警告:在無返回值的函數中,“return”帶返回值

warning.c:4 警告:“main”的返回類型不是“int”

?

可以看出,使用該選項查看出了”long long”這個無效數據類型的錯誤。

· “-Wall”

允許發出Gcc能夠提供的所有有用的報警信息。該選項的運行結果如下所示:

[root@localhost Gcc]# Gcc?–Wall warning.c –o warning

warning.c:4 警告:“main”的返回類型不是“int”

warning.c: 在函數”main”中:

warning.c:7 警告:在無返回值的函數中,”return”帶返回值

warning.c:5 警告:未使用的變量“tmp”

?

使用“-Wall”選項找出了未使用的變量tmp,但它并沒有找出無效數據類型的錯誤。

另外,Gcc還可以利用選項對單獨的常見錯誤分別指定警告,有關具體選項的含義感興趣的讀者可以查看Gcc手冊進行學習。

(3)優化選項

Gcc可以對代碼進行優化,它通過編譯選項“-On”來控制優化代碼的生成,其中n是一個代表優化級別的整數。對于不同版本的Gcc來講,n的取值范圍及其對應的優化效果可能并不完全相同,比較典型的范圍是從0變化到2或3。

不同的優化級別對應不同的優化處理工作。如使用優化選項“-O”主要進行線程跳轉(Thread Jump)和延遲退棧(Deferred Stack Pops)兩種優化。使用優化選項“-O2”除了完成所有“-O1”級別的優化之外,同時還要進行一些額外的調整工作,如處理器指令調度等。選項“-O3”則還包括循環展開和其他一些與處理器特性相關的優化工作。

雖然優化選項可以加速代碼的運行速度,但對于調試而言將是一個很大的挑戰。因為代碼在經過優化之后,原先在源程序中聲明和使用的變量很可能不再使用,控制流也可能會突然跳轉到意外的地方,循環語句也有可能因為循環展開而變得到處都有,所有這些對調試來講都將是一場噩夢。所以筆者建議在調試的時候最好不使用任何優化選項,只有當程序在最終發行的時候才考慮對其進行優化。

(4)體系結構相關選項

Gcc的體系結構相關選項如表3.9所示。

表3.9Gcc體系結構相關選項列表

選項

含義

-mcpu=type

針對不同的CPU使用相應的CPU指令。可選擇的type有i386、i486、pentium及i686等

-mieee-fp

使用IEEE標準進行浮點數的比較

-mno-ieee-fp

不使用IEEE標準進行浮點數的比較

-msoft-float

輸出包含浮點庫調用的目標代碼

-mshort

把int類型作為16位處理,相當于short int

-mrtd

強行將函數參數個數固定的函數用ret NUM返回,節省調用函數的一條指令

?

這些體系結構相關選項在嵌入式的設計中會有較多的應用,讀者需根據不同體系結構將對應的選項進行組合處理。在本書后面涉及到具體實例會有針對性的講解。

Gdb調試器

調試是所有程序員都會面臨的問題。如何提高程序員的調試效率,更好更快地定位程序中的問題從而加快程序開發的進度,是大家共同面對的。就如讀者熟知的Windows下的一些調試工具,如VC自帶的如設置斷點、單步跟蹤等,都受到了廣大用戶的贊賞。那么,在Linux下有什么很好的調試工具呢?

本文所介紹的Gdb調試器是一款GNU開發組織并發布的UNIX/Linux下的程序調試工具。雖然,它沒有圖形化的友好界面,但是它強大的功能也足以與微軟的VC工具等媲美。下面就請跟隨筆者一步步學習Gdb調試器。

Gdb使用流程

首先,筆者給出了一個短小的程序,由此帶領讀者熟悉一下Gdb的使用流程。強烈建議讀者能夠實際動手操作。

首先,打開Linux下的編輯器Vi或者Emacs,編輯如下代碼。(由于為了更好地熟悉Gdb的操作,筆者在此使用Vi編輯,希望讀者能夠參見3.3節中對Vi的介紹,并熟練使用Vi)。

?

?

#include <stdio.h>

int sum(int m);

int main()

{

int i,n=0;

sum(50);

for(i=1; i<=50; i++)

{

n += i;

}

printf("The sum of 1-50 is %d n", n );

?

}

int sum(int m)

{

int i,n=0;

for(i=1; i<=m;i++)

n += i;

printf("The sum of 1-m is %dn", n);

}

?

在保存退出后首先使用Gcc對test.c進行編譯,注意一定要加上選項”-g”,這樣編譯出的可執行代碼中才包含調試信息,否則之后Gdb無法載入該可執行文件。

?

[root@localhost Gdb]#?gcc -g test.c -o test

?

雖然這段程序沒有錯誤,但調試完全正確的程序可以更加了解Gdb的使用流程。接下來就啟動Gdb進行調試。注意,Gdb進行調試的是可執行文件,而不是如”.c”的源代碼,因此,需要先通過Gcc編譯生成可執行文件才能用Gdb進行調試。

?

[root@localhost Gdb]#?gdb test

GNU Gdb Red Hat Linux (6.3.0.0-1.21rh)

Copyright 2004 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-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb)

?

可以看出,在Gdb的啟動畫面中指出了Gdb的版本號、使用的庫文件等信息,接下來就進入了由“(gdb)”開頭的命令行界面了。

(1)查看文件

在Gdb中鍵入”l”(list)就可以查看所載入的文件,如下所示:

?

?

注意

在Gdb的命令中都可使用縮略形式的命令,如“l”代便“list”,“b”代表“breakpoint”,“p”代表“print”等,讀者也可使用“help”命令查看幫助信息。

?

(Gdb)?l

1 #include <stdio.h>

2 int sum(int m);

3 int main()

4 {

5 int i,n=0;

6 sum(50);

7 for(i=1; i<=50; i++)

8 {

9 n += i;

10 }

(Gdb)?l

11 printf("The sum of 1~50 is %d n", n );

12

13 }

14 int sum(int m)

15 {

16 int i,n=0;

17 for(i=1; i<=m;i++)

18 n += i;

19 printf("The sum of 1~m is = %dn", n);

20 }

?

可以看出,Gdb列出的源代碼中明確地給出了對應的行號,這樣就可以大大地方便代碼的定位。

(2)設置斷點

設置斷點是調試程序中是一個非常重要的手段,它可以使程序到一定位置暫停它的運行。因此,程序員在該位置處可以方便地查看變量的值、堆棧情況等,從而找出代碼的癥結所在。

在Gdb中設置斷點非常簡單,只需在”b”后加入對應的行號即可(這是最常用的方式,另外還有其他方式設置斷點)。如下所示:

?

(Gdb)?b 6

Breakpoint 1 at 0x804846d: file test.c, line 6.

?

要注意的是,在Gdb中利用行號設置斷點是指代碼運行到對應行之前將其停止,如上例中,代碼運行到第五行之前暫停(并沒有運行第五行)。

(3)查看斷點情況

在設置完斷點之后,用戶可以鍵入”info b”來查看設置斷點情況,在Gdb中可以設置多個斷點。

?

(Gdb)?info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x0804846d in main at test.c:6

?

(4)運行代碼

接下來就可運行代碼了,Gdb默認從首行開始運行代碼,可鍵入”r”(run)即可(若想從程序中指定行開始運行,可在r后面加上行號)。

?

(Gdb)?r

Starting program: /root/workplace/Gdb/test

Reading symbols from shared object read from target memory...done.

Loaded system supplied DSO at 0x5fb000

?

Breakpoint 1, main () at test.c:6

6 sum(50);

?

可以看到,程序運行到斷點處就停止了。

(5)查看變量值

在程序停止運行之后,程序員所要做的工作是查看斷點處的相關變量值。在Gdb中只需鍵入”p”+變量值即可,如下所示:

?

(Gdb)?p n

$1 = 0

(Gdb)?p i

$2 = 134518440

?

在此處,為什么變量”i”的值為如此奇怪的一個數字呢?原因就在于程序是在斷點設置的對應行之前停止的,那么在此時,并沒有把”i”的數值賦為零,而只是一個隨機的數字。但變量”n”是在第四行賦值的,故在此時已經為零。

?

小技巧

Gdb在顯示變量值時都會在對應值之前加上”$N”標記,它是當前變量值的引用標記,所以以后若想再次引用此變量就可以直接寫作”$N”,而無需寫冗長的變量名。

?

(6)單步運行

單步運行可以使用命令”n”(next)或”s”(step),它們之間的區別在于:若有函數調用的時候,”s”會進入該函數而”n”不會進入該函數。因此,”s”就類似于VC等工具中的”step in”,”n”類似與VC等工具中的”step over”。它們的使用如下所示:

?

(Gdb)?n

The sum of 1-m is 1275

7 for(i=1; i<=50; i++)

(Gdb)?s

sum (m=50) at test.c:16

16 int i,n=0;

?

可見,使用”n”后,程序顯示函數sum的運行結果并向下執行,而使用”s”后則進入到sum函數之中單步運行。

(7)恢復程序運行

在查看完所需變量及堆棧情況后,就可以使用命令”c”(continue)恢復程序的正常運行了。這時,它會把剩余還未執行的程序執行完,并顯示剩余程序中的執行結果。以下是之前使用”n”命令恢復后的執行結果:

?

(Gdb)?c

Continuing.

The sum of 1-50 is :1275

?

Program exited with code 031.

?

可以看出,程序在運行完后退出,之后程序處于“停止狀態”。

?

小知識

在Gdb中,程序的運行狀態有“運行”、“暫停”和“停止”三種,其中“暫停”狀態為程序遇到了斷點或觀察點之類的,程序暫時停止運行,而此時函數的地址、函數參數、函數內的局部變量都會被壓入“棧”(Stack)中。故在這種狀態下可以查看函數的變量值等各種屬性。但在函數處于“停止”狀態之后,“棧”就會自動撤銷,它也就無法查看各種信息了。

Gdb基本命令

Gdb的命令可以通過查看help進行查找,由于Gdb的命令很多,因此Gdb的help將其分成了很多種類(class),用戶可以通過進一步查看相關class找到相應命令。如下所示:

?

(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

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) help data

Examining data.

?

List of commands:

?

call -- Call a function in the program

delete display -- Cancel some expressions to be displayed when program stops

delete mem -- Delete memory region

disable display -- Disable some expressions to be displayed when program stops

Type "help" followed by command name for full documentation.

Command name abbreViations are allowed if unambiguous.

?

至此,若用戶想要查找call命令,就可鍵入“help call”。

?

(gdb)?help call

Call a function in the program.

The argument is the function name and arguments, in the notation of the

current working language. The result is printed and saved in the value

history, if it is not void.

?

當然,若用戶已知命令名,直接鍵入“help [command]”也是可以的。

Gdb中的命令主要分為以下幾類:工作環境相關命令、設置斷點與恢復命令、源代碼查看命令、查看運行數據相關命令及修改運行參數命令。以下就分別對這幾類的命令進行講解。

1.工作環境相關命令

Gdb中不僅可以調試所運行的程序,而且還可以對程序相關的工作環境進行相應的設定,甚至還可以使用shell中的命令進行相關的操作,其功能極其強大。表3.10所示列出了Gdb常見工作環境相關命令。

表3.10 Gdb工作環境相關命令

命 令 格 式

含義

set args運行時的參數

指定運行時參數,如:set args 2

show args

查看設置好的運行參數

path dir

設定程序的運行路徑

show paths

查看程序的運行路徑

set enVironment var [=value]

設置環境變量

show enVironment [var]

查看環境變量

cd dir

進入到dir目錄,相當于shell中的cd命令

pwd

顯示當前工作目錄

shell command

運行shell的command命令

2.設置斷點與恢復命令

Gdb中設置斷點與恢復的常見命令如表3.11所示。

表3.11 Gdb設置斷點與恢復相關命令

命 令 格 式

含義

bnfo b

查看所設斷點

break 行號或函數名 <條件表達式>

設置斷點

tbreak 行號或函數名 <條件表達式>

設置臨時斷點,到達后被自動刪除

delete [斷點號]

刪除指定斷點,其斷點號為”info b”中的第一欄。若缺省斷點號則刪除所有斷點

disable [斷點號]]

停止指定斷點,使用”info b”仍能查看此斷點。同delete一樣,省斷點號則停止所有斷點

enable [斷點號]

激活指定斷點,即激活被disable停止的斷點

condition [斷點號] <條件表達式>

修改對應斷點的條件

ignore [斷點號]<num>

在程序執行中,忽略對應斷點num次

step

單步恢復程序運行,且進入函數調用

next

單步恢復程序運行,但不進入函數調用

finish

運行程序,直到當前函數完成返回

c

繼續執行函數,直到函數結束或遇到新的斷點

?

由于設置斷點在Gdb的調試中非常重要,所以在此再著重講解一下Gdb中設置斷點的方法。

Gdb中設置斷點有多種方式:其一是按行設置斷點,設置方法在3.5.1節已經指出,在此就不重復了。另外還可以設置函數斷點和條件斷點,在此結合上一小節的代碼,具體介紹后兩種設置斷點的方法。

① 函數斷點

Gdb中按函數設置斷點只需把函數名列在命令”b”之后,如下所示:

?

(gdb)?b sum

Breakpoint 1 at 0x80484ba: file test.c, line 16.

(gdb)?info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x080484ba in sum at test.c:16

?

要注意的是,此時的斷點實際是在函數的定義處,也就是在16行處(注意第16行還未執行)。

② 條件斷點

Gdb中設置條件斷點的格式為:b 行數或函數名 if 表達式。具體實例如下所示:

?

(gdb) b 8 if i==10

Breakpoint 1 at 0x804848c: file test.c, line 8.

(gdb) info b

Num Type Disp Enb Address What

1 breakpoint keep y 0x0804848c in main at test.c:8

stop only if i == 10

(gdb) r

Starting program: /home/yul/test

The sum of 1-m is 1275

?

Breakpoint 1, main () at test.c:9

9 n += i;

(gdb) p i

$1 = 10

?

可以看到,該例中在第8行(也就是運行完第7行的for循環)設置了一個“i==0”的條件斷點,在程序運行之后可以看出,程序確實在i為10時暫停運行。

3.Gdb中源碼查看相關命令

在Gdb中可以查看源碼以方便其他操作,它的常見相關命令如表3.12所示:

表3.12 Gdb源碼查看相關相關命令

命 令 格 式

含義

list <行號>|<函數名>

查看指定位置代碼

file [文件名]

加載指定文件

forward-search 正則表達式

源代碼前向搜索

reverse-search 正則表達式

源代碼后向搜索

dir dir

停止路徑名

show directories

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

info line

顯示加載到Gdb內存中的代碼

4.Gdb中查看運行數據相關命令

Gdb中查看運行數據是指當程序處于“運行”或“暫停”狀態時,可以查看的變量及表達式的信息,其常見命令如表3.13所示:

表3.13 Gdb查看運行數據相關命令

命 令 格 式

含義

print 表達式|變量

查看程序運行時對應表達式和變量的值

x <n/f/u>

查看內存變量內容。其中n為整數表示顯示內存的長度,f表示顯示的格式,u表示從當前地址往后請求顯示的字節數

display 表達式

設定在單步運行或其他情況中,自動顯示的對應表達式的內容

5.Gdb中修改運行參數相關命令

Gdb還可以修改運行時的參數,并使該變量按照用戶當前輸入的值繼續運行。它的設置方法為:在單步執行的過程中,鍵入命令“set 變量=設定值”。這樣,在此之后,程序就會按照該設定的值運行了。下面,筆者結合上一節的代碼將n的初始值設為4,其代碼如下所示:

?

(Gdb)?b 7

Breakpoint 5 at 0x804847a: file test.c, line 7.

(Gdb)?r

Starting program: /home/yul/test

The sum of 1-m is 1275

?

Breakpoint 5, main () at test.c:7

7 for(i=1; i<=50; i++)

(Gdb)?set n=4

(Gdb)?c

Continuing.

The sum of 1-50 is 1279

?

Program exited with code 031.

?

可以看到,最后的運行結果確實比之前的值大了4。

?

?

Gdb的使用切記點:

· 在Gcc編譯選項中一定要加入”-g”。

· 只有在代碼處于“運行”或“暫停”狀態時才能查看變量值。

· 設置斷點后程序在指定行之前停止。

Make工程管理器

到此為止,讀者已經了解了如何在Linux下使用編輯器編寫代碼,如何使用Gcc把代碼編譯成可執行文件,還學習了如何使用Gdb來調試程序,那么,所有的工作看似已經完成了,為什么還需要Make這個工程管理器呢?

所謂工程管理器,顧名思義,是指管理較多的文件的。讀者可以試想一下,有一個上百個文件的代碼構成的項目,如果其中只有一個或少數幾個文件進行了修改,按照之前所學的Gcc編譯工具,就不得不把這所有的文件重新編譯一遍,因為編譯器并不知道哪些文件是最近更新的,而只知道需要包含這些文件才能把源代碼編譯成可執行文件,于是,程序員就不能不再重新輸入數目如此龐大的文件名以完成最后的編譯工作。

但是,請讀者仔細回想一下本書在3.1.2節中所闡述的編譯過程,編譯過程是分為編譯、匯編、鏈接不同階段的,其中編譯階段僅檢查語法錯誤以及函數與變量的聲明是否正確聲明了,在鏈接階段則主要完成是函數鏈接和全局變量的鏈接。因此,那些沒有改動的源代碼根本不需要重新編譯,而只要把它們重新鏈接進去就可以了。所以,人們就希望有一個工程管理器能夠自動識別更新了的文件代碼,同時又不需要重復輸入冗長的命令行,這樣,Make工程管理器也就應運而生了。

實際上,Make工程管理器也就是個“自動編譯管理器”,這里的“自動”是指它能夠根據文件時間戳自動發現更新過的文件而減少編譯的工作量,同時,它通過讀入Makefile文件的內容來執行大量的編譯工作。用戶只需編寫一次簡單的編譯語句就可以了。它大大提高了實際項目的工作效率,而且幾乎所有Linux下的項目編程均會涉及到它,希望讀者能夠認真學習本節內容。

Makefile基本結構

Makefile是Make讀入的惟一配置文件,因此本節的內容實際就是講述Makefile的編寫規則。在一個Makefile中通常包含如下內容:

· 需要由make工具創建的目標體(target),通常是目標文件或可執行文件;

· 要創建的目標體所依賴的文件(dependency_file);

· 創建每個目標體時需要運行的命令(command)。

它的格式為:

?

target: dependency_files

command

?

例如,有兩個文件分別為hello.c和hello.h,創建的目標體為hello.o,執行的命令為gcc編譯指令:gcc –c hello.c,那么,對應的Makefile就可以寫為:

?

#The simplest example

hello.o: hello.c hello.h

gcc –c hello.c –o hello.o

?

接著就可以使用make了。使用make的格式為:make target,這樣make就會自動讀入Makefile(也可以是首字母小寫makefile)并執行對應target的command語句,并會找到相應的依賴文件。如下所示:

?

[root@localhost makefile]#?make hello.o

gcc –c hello.c –o hello.o

[root@localhost makefile]# ls

hello.c hello.h?hello.o?Makefile

?

可以看到,Makefile執行了“hello.o”對應的命令語句,并生成了“hello.o”目標體。

?

?

注意

在Makefile中的每一個command前必須有“Tab”符,否則在運行make命令時會出錯。

Makefile變量

上面示例的Makefile在實際中是幾乎不存在的,因為它過于簡單,僅包含兩個文件和一個命令,在這種情況下完全不必要編寫Makefile而只需在Shell中直接輸入即可,在實際中使用的Makefile往往是包含很多的文件和命令的,這也是Makefile產生的原因。下面就可給出稍微復雜一些的Makefile進行講解:

?

sunq:kang.o yul.o

Gcc kang.o bar.o -o myprog

kang.o?: kang.c kang.h head.h

Gcc –Wall –O -g –c kang.c -o kang.o

yul.o?: bar.c head.h

Gcc - Wall –O -g –c yul.c -o yul.o

?

在這個Makefile中有三個目標體(target),分別為sunq、kang.o和yul.o,其中第一個目標體的依賴文件就是后兩個目標體。如果用戶使用命令“make sunq”,則make管理器就是找到sunq目標體開始執行。

這時,make會自動檢查相關文件的時間戳。首先,在檢查“kang.o”、“yul.o”和“sunq”三個文件的時間戳之前,它會向下查找那些把“kang.o”或“yul.o”做為目標文件的時間戳。比如,“kang.o”的依賴文件為:“kang.c”、“kang.h”、“head.h”。如果這些文件中任何一個的時間戳比“kang.o”新,則命令“gcc –Wall –O -g –c kang.c -o kang.o”將會執行,從而更新文件“kang.o”。在更新完“kang.o”或“yul.o”之后,make會檢查最初的“kang.o”、“yul.o”和“sunq”三個文件,只要文件“kang.o”或“yul.o”中的任比文件時間戳比“sunq”新,則第二行命令就會被執行。這樣,make就完成了自動檢查時間戳的工作,開始執行編譯工作。這也就是Make工作的基本流程。

接下來,為了進一步簡化編輯和維護Makefile,make允許在Makefile中創建和使用變量。變量是在Makefile中定義的名字,用來代替一個文本字符串,該文本字符串稱為該變量的值。在具體要求下,這些值可以代替目標體、依賴文件、命令以及makefile文件中其它部分。在Makefile中的變量定義有兩種方式:一種是遞歸展開方式,另一種是簡單方式。

遞歸展開方式定義的變量是在引用在該變量時進行替換的,即如果該變量包含了對其他變量的應用,則在引用該變量時一次性將內嵌的變量全部展開,雖然這種類型的變量能夠很好地完成用戶的指令,但是它也有嚴重的缺點,如不能在變量后追加內容(因為語句:CFLAGS = $(CFLAGS) -O在變量擴展過程中可能導致無窮循環)。

為了避免上述問題,簡單擴展型變量的值在定義處展開,并且只展開一次,因此它不包含任何對其它變量的引用,從而消除變量的嵌套引用。

遞歸展開方式的定義格式為:VAR=var

簡單擴展方式的定義格式為:VAR:=var

Make中的變量使用均使用格式為:$(VAR)

?

?

注意

變量名是不包括“:”、“#”、“=”結尾空格的任何字符串。同時,變量名中包含字母、數字以及下劃線以外的情況應盡量避免,因為它們可能在將來被賦予特別的含義。

變量名是大小寫敏感的,例如變量名“foo”、“FOO”、和“Foo”代表不同的變量。

推薦在makefile內部使用小寫字母作為變量名,預留大寫字母作為控制隱含規則參數或用戶重載命令選項參數的變量名。

?

下面給出了上例中用變量替換修改后的Makefile,這里用OBJS代替kang.o和yul.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。這樣在以后修改時,就可以只修改變量定義,而不需要修改下面的定義實體,從而大大簡化了Makefile維護的工作量。

經變量替換后的Makefile如下所示:

?

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $(OBJS) -o sunq

kang.o : kang.c kang.h

$(CC) $(CFLAGS) -c kang.c -o kang.o

yul.o : yul.c yul.h

$(CC) $(CFLAGS) -c yul.c -o yul.o

?

可以看到,此處變量是以遞歸展開方式定義的。

Makefile中的變量分為用戶自定義變量、預定義變量、自動變量及環境變量。如上例中的OBJS就是用戶自定義變量,自定義變量的值由用戶自行設定,而預定義變量和自動變量為通常在Makefile都會出現的變量,其中部分有默認值,也就是常見的設定值,當然用戶可以對其進行修改。

預定義變量包含了常見編譯器、匯編器的名稱及其編譯選項。下表3.14列出了Makefile中常見預定義變量及其部分默認值。

表3.14 Makefile中常見預定義變量

命 令 格 式

含義

AR

庫文件維護程序的名稱,默認值為ar

AS

匯編程序的名稱,默認值為as

CC

C編譯器的名稱,默認值為cc

CPP

C預編譯器的名稱,默認值為$(CC) –E

CXX

C++編譯器的名稱,默認值為g++

FC

FORTRAN編譯器的名稱,默認值為f77

RM

文件刪除程序的名稱,默認值為rm –f

ARFLAGS

庫文件維護程序的選項,無默認值

ASFLAGS

匯編程序的選項,無默認值

CFLAGS

C編譯器的選項,無默認值

CPPFLAGS

C預編譯的選項,無默認值

CXXFLAGS

C++編譯器的選項,無默認值

FFLAGS

FORTRAN編譯器的選項,無默認值

?

可以看出,上例中的CC和CFLAGS是預定義變量,其中由于CC沒有采用默認值,因此,需要把“CC=Gcc”明確列出來。

由于常見的Gcc編譯語句中通常包含了目標文件和依賴文件,而這些文件在Makefile文件中目標體的一行已經有所體現,因此,為了進一步簡化Makefile的編寫,就引入了自動變量。自動變量通常可以代表編譯語句中出現目標文件和依賴文件等,并且具有本地含義(即下一語句中出現的相同變量代表的是下一語句的目標文件和依賴文件)。下表3.15列出了Makefile中常見自動變量。

表3.15Makefile中常見自動變量

命令格式

含義

$*

不包含擴展名的目標文件名稱

$+

所有的依賴文件,以空格分開,并以出現的先后為序,可能包含重復的依賴文件

$<

第一個依賴文件的名稱

$?

所有時間戳比目標文件晚的依賴文件,并以空格分開

命令格式

含義

$@

目標文件的完整名稱

$^

所有不重復的依賴文件,以空格分開

$%

如果目標是歸檔成員,則該變量表示目標的歸檔成員名稱

?

自動變量的書寫比較難記,但是在熟練了之后會非常的方便,請讀者結合下例中的自動變量改寫的Makefile進行記憶。

?

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC)?$^?-o?$@

kang.o : kang.c kang.h

$(CC) $(CFLAGS) -c?$<?-o?$@

yul.o : yul.c yul.h

$(CC) $(CFLAGS) -c?$<?-o?$@

?

另外,在Makefile中還可以使用環境變量。使用環境變量的方法相對比較簡單,make在啟動時會自動讀取系統當前已經定義了的環境變量,并且會創建與之具有相同名稱和數值的變量。但是,如果用戶在Makefile中定義了相同名稱的變量,那么用戶自定義變量將會覆蓋同名的環境變量。

Makefile規則

Makefile的規則是Make進行處理的依據,它包括了目標體、依賴文件及其之間的命令語句。一般的,Makefile中的一條語句就是一個規則。在上面的例子中,都顯示地指出了Makefile中的規則關系,如“$(CC) $(CFLAGS) -c $< -o $@”,但為了簡化Makefile的編寫,make還定義了隱式規則和模式規則,下面就分別對其進行講解。

1.隱式規則

隱含規則能夠告訴make怎樣使用傳統的技術完成任務,這樣,當用戶使用它們時就不必詳細指定編譯的具體細節,而只需把目標文件列出即可。Make會自動搜索隱式規則目錄來確定如何生成目標文件。如上例就可以寫成:

?

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC) $^ -o $@

?

為什么可以省略后兩句呢?因為Make的隱式規則指出:所有“.o”文件都可自動由“.c”文件使用命令“$(CC) $(CPPFLAGS) $(CFLAGS) -c file.c –o file.o”生成。這樣“kang.o”和“yul.o”就會分別調用“$(CC) $(CFLAGS) -c kang.c -o kang.o”和“$(CC) $(CFLAGS) -c yul.c -o yul.o”生成。

?

?

注意

在隱式規則只能查找到相同文件名的不同后綴名文件,如”kang.o”文件必須由”kang.c”文件生成。

?

下表3.16給出了常見的隱式規則目錄:

表3.16 Makefile中常見隱式規則目錄

對應語言后綴名

規則

C編譯:.c變為.o

$(CC) –c $(CPPFLAGS) $(CFLAGS)

C++編譯:.cc或.C變為.o

$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

Pascal編譯:.p變為.o

$(PC) -c $(PFLAGS)

Fortran編譯:.r變為-o

$(FC) -c $(FFLAGS)

2.模式規則

模式規則是用來定義相同處理規則的多個文件的。它不同于隱式規則,隱式規則僅僅能夠用make默認的變量來進行操作,而模式規則還能引入用戶自定義變量,為多個文件建立相同的規則,從而簡化Makefile的編寫。

模式規則的格式類似于普通規則,這個規則中的相關文件前必須用“%”標明。使用模式規則修改后的Makefile的編寫如下:

?

OBJS = kang.o yul.o

CC = Gcc

CFLAGS = -Wall -O -g

sunq : $(OBJS)

$(CC)?$^?-o?$@

%.o : %.c

$(CC) $(CFLAGS) -c?$<?-o?$@

Make使用

使用make管理器非常簡單,只需在make命令的后面鍵入目標名即可建立指定的目標,如果直接運行make,則建立Makefile中的第一個目標。

此外make還有豐富的命令行選項,可以完成各種不同的功能。下表3.17列出了常用的make命令行選項。

表3.17 make的命令行選項

命令格式

含 義

-C dir

讀入指定目錄下的Makefile

-f file

讀入當前目錄下的file文件作為Makefile

命令格式

含 義

-i

忽略所有的命令執行錯誤

-I dir

指定被包含的Makefile所在目錄

-n

只打印要執行的命令,但不執行這些命令

-p

顯示make變量數據庫和隱含規則

-s

在執行命令時不顯示命令

-w

如果make在執行過程中改變目錄,則打印當前目錄名

使用autotools

在上一小節,讀者已經了解到了make項目管理器的強大功能。的確,Makefile可以幫助make完成它的使命,但要承認的是,編寫Makefile確實不是一件輕松的事,尤其對于一個較大的項目而言更是如此。那么,有沒有一種輕松的手段生成Makefile而同時又能讓用戶享受make的優越性呢?本節要講的autotools系列工具正是為此而設的,它只需用戶輸入簡單的目標文件、依賴文件、文件目錄等就可以輕松地生成Makefile了,這無疑是廣大用戶的所希望的。另外,這些工具還可以完成系統配置信息的收集,從而可以方便地處理各種移植性的問題。也正是基于此,現在Linux上的軟件開發一般都用autotools來制作Makefile,讀者在后面的講述中就會了解到。

autotools使用流程

正如前面所言,autotools是系列工具,讀者首先要確認系統是否裝了以下工具(可以用which命令進行查看)。

· aclocal

· autoscan

· autoconf

· autoheader

· automake

使用autotools主要就是利用各個工具的腳本文件以生成最后的Makefile。其總體流程是這樣的:

· 使用aclocal生成一個“aclocal.m4”文件,該文件主要處理本地的宏定義;

· 改寫“configure.scan”文件,并將其重命名為“configure.in”,并使用autoconf文件生成configure文件。

接下來,筆者將通過一個簡單的hello.c例子帶領讀者熟悉autotools生成makefile的過程,由于在這過程中有涉及到較多的腳本文件,為了更清楚地了解相互之間的關系,強烈建議讀者實際動手操作以體會其整個過程。

1.autoscan

它會在給定目錄及其子目錄樹中檢查源文件,若沒有給出目錄,就在當前目錄及其子目錄樹中進行檢查。它會搜索源文件以尋找一般的移植性問題并創建一個文件“configure.scan”,該文件就是接下來autoconf要用到的“configure.in”原型。如下所示:

?

[root@localhost automake]#?autoscan

autom4te: configure.ac: no such file or directory

autoscan: /usr/bin/autom4te failed with exit status: 1

[root@localhost automake]# ls

autoscan.log?configure.scan?hello.c

?

如上所示,autoscan首先會嘗試去讀入“configure.ac”(同configure.in的配置文件)文件,此時還沒有創建該配置文件,于是它會自動生成一個“configure.in”的原型文件“configure.scan”。

2.autoconf

configure.in是autoconf的腳本配置文件,它的原型文件“configure.scan”如下所示:

?

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.59)

#The next one is modified by sunq

#AC_INIT(FULL-PACKAGE-NAME,VERSION,BUG-REPORT-ADDRESS)

AC_INIT(hello,1.0)

# The next one is added by sunq

AM_INIT_AUTOMAKE(hello,1.0)

AC_CONFIG_SRCDIR([hello.c])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_CONFIG_FILES([Makefile])

AC_OUTPUT

?

下面對這個腳本文件進行解釋:

· 以“#”號開始的行為注釋。

· AC_PREREQ宏聲明本文件要求的autoconf版本,如本例使用的版本2.59。

· AC_INIT宏用來定義軟件的名稱和版本等信息,在本例中省略了BUG-REPORT-ADDRESS,一般為作者的e-mail。

· AM_INIT_AUTOMAKE是筆者另加的,它是automake所必備的宏,也同前面一樣,PACKAGE是所要產生軟件套件的名稱,VERSION是版本編號。

· AC_CONFIG_SRCDIR宏用來偵測所指定的源碼文件是否存在,來確定源碼目錄的有

效性。在此處為當前目錄下的hello.c。

· AC_CONFIG_HEADER宏用于生成config.h文件,以便autoheader使用。

· AC_CONFIG_FILES宏用于生成相應的Makefile文件。

· 中間的注釋間可以添加分別用戶測試程序、測試函數庫、測試頭文件等宏定義。

接下來首先運行aclocal,生成一個“aclocal.m4”文件,該文件主要處理本地的宏定義。如下所示:

?

[root@localhost automake]#?aclocal

?

再接著運行autoconf,生成“configure”可執行文件。如下所示:

?

[root@localhost automake]#?autoconf

[root@localhost automake]#?ls

aclocal.m4 autom4te.cache autoscan.log?configure?configure.in hello.c

3.autoheader

接著使用autoheader命令,它負責生成config.h.in文件。該工具通常會從“acconfig.h”文件中復制用戶附加的符號定義,因此此處沒有附加符號定義,所以不需要創建“acconfig.h”文件。如下所示:

?

[root@localhost automake]#?autoheader

4.automake

這一步是創建Makefile很重要的一步,automake要用的腳本配置文件是Makefile.am,用戶需要自己創建相應的文件。之后,automake工具轉換成Makefile.in。在該例中,筆者創建的文件為Makefile.am如下所示:

?

AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS= hello

hello_SOURCES= hello.c

?

下面對該腳本文件的對應項進行解釋。

· 其中的AUTOMAKE_OPTIONS為設置automake的選項。由于GNU(在第1章中已經有所介紹)對自己發布的軟件有嚴格的規范,比如必須附帶許可證聲明文件COPYING等,否則automake執行時會報錯。automake提供了三種軟件等級:foreign、gnu和gnits,讓用戶選擇采用,默認等級為gnu。在本例使用foreign等級,它只檢測必須的文件。

· bin_PROGRAMS定義要產生的執行文件名。如果要產生多個執行文件,每個文件名用空格隔開。

· hello_SOURCES定義“hello”這個執行程序所需要的原始文件。如果”hello”這個程序是由多個原始文件所產生的,則必須把它所用到的所有原始文件都列出來,并用空格隔開。例如:若目標體“hello”需要“hello.c”、“sunq.c”、“hello.h”三個依賴文件,則定義hello_SOURCES=hello.c sunq.c hello.h。要注意的是,如果要定義多個執行文件,則對每個執行程序都要定義相應的file_SOURCES。

接下來可以使用automake對其生成“configure.in”文件,在這里使用選項“—adding-missing”可以讓automake自動添加有一些必需的腳本文件。如下所示:

?

[root@localhost automake]#?automake --add-missing

configure.in: installing './install-sh'

configure.in: installing './missing'

Makefile.am: installing 'depcomp'

[root@localhost automake]#?ls

aclocal.m4 autoscan.log?configure.in?hello.c Makefile.am missing

autom4te.cache configure depcomp install-sh Makefile.in config.h.in

?

可以看到,在automake之后就可以生成configure.in文件。

5.運行configure

在這一步中,通過運行自動配置設置文件configure,把Makefile.in變成了最終的Makefile。如下所示:

?

[root@localhost automake]#?./configure

checking for a BSD-compatible install... /usr/bin/install -c

checking whether build enVironment is sane... yes

checking for gawk... gawk

checking whether make sets $(MAKE)... yes

checking for Gcc... Gcc

checking for C compiler default output file name... a.out

checking whether the C compiler works... yes

checking whether we are cross compiling... no

checking for suffix of executables...

checking for suffix of object files... o

checking whether we are using the GNU C compiler... yes

checking whether Gcc accepts -g... yes

checking for Gcc option to accept ANSI C... none needed

checking for style of include used by make... GNU

checking dependency style of Gcc... Gcc3

configure: creating ./config.status

config.status:?creating Makefile

config.status: executing depfiles commands

可以看到,在運行configure時收集了系統的信息,用戶可以在configure命令中對其進行方便地配置。在./configure的自定義參數有兩種,一種是開關式(--enable-XXX或--disable-XXX),另一種是開放式,即后面要填入一串字符(--with-XXX=yyyy)參數。讀者可以自行嘗試其使用方法。另外,讀者可以查看同一目錄下的”config.log”文件,以方便調試之用。

到此為止,makefile就可以自動生成了。回憶整個步驟,用戶不再需要定制不同的規則,而只需要輸入簡單的文件及目錄名即可,這樣就大大方便了用戶的使用。下面的圖3.9總結了上述過程:

?

圖3.9 autotools生成Makefile流程圖

使用autotools所生成的Makefile

autotools生成的Makefile除具有普通的編譯功能外,還具有以下主要功能(感興趣的讀者可以查看這個簡單的hello.c程序的makefile):

1.make

鍵入make默認執行”make all”命令,即目標體為all,其執行情況如下所示:

?

[root@localhost automake]#?make

if Gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c;

then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi

Gcc -g -O2 -o hello hello.o

此時在本目錄下就生成了可執行文件“hello”,運行“./hello”能出現正常結果,如下所示:

?

[root@localhost automake]#?./hello

Hello!Autoconf!

2.make install

此時,會把該程序安裝到系統目錄中去,如下所示:

?

[root@localhost automake]#?make install

if Gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -MT hello.o -MD -MP -MF ".deps/hello.Tpo" -c -o hello.o hello.c;

then mv -f ".deps/hello.Tpo" ".deps/hello.Po"; else rm -f ".deps/hello.Tpo"; exit 1; fi

Gcc -g -O2 -o hello hello.o

make[1]: Entering directory '/root/workplace/automake'

test -z "/usr/local/bin" || mkdir -p -- "/usr/local/bin"

/usr/bin/install -c 'hello' '/usr/local/bin/hello'

make[1]: Nothing to be done for 'install-data-am'.

make[1]: LeaVing directory '/root/workplace/automake'

?

此時,若直接運行hello,也能出現正確結果,如下所示:

?

[root@localhost automake]#?hello

Hello!Autoconf!

3.make clean

此時,make會清除之前所編譯的可執行文件及目標文件(object file, *.o),如下所示:

?

[root@localhost automake]#?make clean

test -z "hello" || rm -f hello

rm -f *.o

4.make dist

此時,make將程序和相關的文檔打包為一個壓縮文檔以供發布,如下所示:

?

[root@localhost automake]#?make dist

[root@localhost automake]# ls hello-1.0-tar.gz

hello-1.0-tar.gz

?

可見該命令生成了一個hello-1.0-tar.gz的壓縮文件。

由上面的講述讀者不難看出,autotools確實是軟件維護與發布的必備工具,也鑒于此,如今GUN的軟件一般都是由automake來制作的。

?

?

想一想

對于automake制作的這類軟件,應如何安裝呢?

Vi使用練習

1.實驗目的

通過指定指令的Vi操作練習,使讀者能夠熟練使用Vi中的常見操作,并且熟悉Vi的三種模式,如果讀者能夠熟練掌握實驗內容中所要求的內容,則表明對Vi的操作已經很熟練了。

2.實驗內容

(1)在“/root”目錄下建一個名為“/Vi”的目錄。

(2)進入“/Vi”目錄。

(3)將文件“/etc/inittab”復制到“/Vi”目錄下。

(4)使用Vi打開“/Vi”目錄下的inittab。

(5)設定行號,指出設定initdefault(類似于“id:5:initdefault”)的所在行號。

(6)將光標移到該行。

(7)復制該行內容。

(8)將光標移到最后一行行首。

(9)粘貼復制行的內容。

(10)撤銷第9步的動作。

(11)將光標移動到最后一行的行尾。

(12)粘貼復制行的內容。

(13)光標移到“si::sysinit:/etc/rc.d/rc.sysinit”。

(14)刪除該行。

(15)存盤但不退出。

(16)將光標移到首行。

(17)插入模式下輸入“Hello,this is Vi world!”。

(18)返回命令行模式。

(19)向下查找字符串“0:wait”。

(20)再向上查找字符串“halt”。

(21)強制退出Vi,不存盤。

分別指出每個命令處于何種模式下?

3.實驗步驟

(1)mkdir /root/Vi

(2)cd /root/Vi

(3)cp /etc/inittab ./

(4)Vi ./inittab

(5):set nu(底行模式)

(6)17<enter>(命令行模式)

(7)yy

(8)G

(9)p

(10)u

(11)$

(12)p

(13)21G

(14)dd

(15):w(底行模式)

(16)1G

(17)i 并輸入“Hello,this is Vi world!”(插入模式)

(18)Esc

(19)/0:wait(命令行模式)

(20)?halt

(21):q!(底行模式)

4.實驗結果

該實驗最后的結果只對“/root/inittab”增加了一行復制的內容:“id:5:initdefault”。

用Gdb調試有問題的程序

1.實驗目的

通過調試一個有問題的程序,使讀者進一步熟練使用Vi操作,而且熟練掌握Gcc編譯命令及Gdb的調試命令,通過對有問題程序的跟蹤調試,進一步提高發現問題和解決問題的能力。這是一個很小的程序,只有35行,希望讀者認真調試。

2.實驗內容

(1)使用Vi編輯器,將以下代碼輸入到名為greet.c的文件中。此代碼的原意為輸出倒序main函數中定義的字符串,但結果顯示沒有輸出。代碼如下所示:

?

#include <stdio.h>

int display1(char *string);

int display2(char *string);

?

int main ()

{

char string[] = "Embedded Linux";

display1 (string);

display2 (string);

}

int display1 (char *string)

{

printf ("The original string is %s n", string);

}

int display2 (char *string1)

{

char *string2;

int size,i;

size = strlen (string1);

string2 = (char *) malloc (size + 1);

for (i = 0; i < size; i++)

string2[size - i] = string1[i];

string2[size+1] = ' ';

printf("The string afterward is %sn",string2);

}

?

(2)使用Gcc編譯這段代碼,注意要加上“-g”選項以方便之后的調試。

(3)運行生成的可執行文件,觀察運行結果。

(4)使用Gdb調試程序,通過設置斷點、單步跟蹤,一步步找出錯誤所在。

(5)糾正錯誤,更改源程序并得到正確的結果。

3.實驗步驟

(1)在工作目錄上新建文件greet.c,并用Vi啟動:vi greet.c。

(2)在Vi中輸入以上代碼。

(3)在Vi中保存并退出:wq。

(4)用Gcc編譯:gcc -g greet.c -o greet。

(5)運行greet:./greet,輸出為:

?

The original string is Embedded Linux

The string afterward is

?

可見,該程序沒有能夠倒序輸出。

(6)啟動Gdb調試:gdb greet。

(7)查看源代碼,使用命令“l”。

(8)在30行(for循環處)設置斷點,使用命令“b 30”。

(9)在33行(printf函數處)設置斷點,使用命令“b 33”。

(10)查看斷點設置情況,使用命令“info b”。

(11)運行代碼,使用命令“r”。

(12)單步運行代碼,使用命令“n”。

(13)查看暫停點變量值,使用命令“p string2[size - i]”。

(14)繼續單步運行代碼數次,并使用命令查看,發現string2[size-1]的值正確。

(15)繼續程序的運行,使用命令“c”。

(16)程序在printf前停止運行,此時依次查看string2[0]、string2[1]…,發現string[0]沒有被正確賦值,而后面的復制都是正確的,這時,定位程序第31行,發現程序運行結果錯誤的原因在于“size-1”。由于i只能增到“size-1”,這樣string2[0]就永遠不能被賦值而保持NULL,故輸不出任何結果。

(17)退出Gdb,使用命令q。

(18)重新編輯greet.c,把其中的“string2[size - i] = string1[i]”改為“string2[size – i - 1] = string1[i];”即可。

(19)使用Gcc重新編譯:gcc -g greet.c -o greet。

(20)查看運行結果:./greet

?

The original string is Embedded Linux

The string afterward is xuniL deddedbmE

?

這時,輸入結果正確。

4.實驗結果

將原來有錯的程序經過Gdb調試,找出問題所在,并修改源代碼,輸出正確的倒序顯示字符串的結果。

編寫包含多文件的Makefile

1.實驗目的

通過對包含多文件的Makefile的編寫,熟悉各種形式的Makefile,并且進一步加深對Makefile中用戶自定義變量、自動變量及預定義變量的理解。

2.實驗過程

(1)用Vi在同一目錄下編輯兩個簡單的Hello程序,如下所示:

?

#hello.c

#include "hello.h"

int main()

{

printf("Hello everyone!n");

}

#hello.h

#include <stdio.h>

?

(2)仍在同一目錄下用Vi編輯Makefile,且不使用變量替換,用一個目標體實現(即直接將hello.c和hello.h編譯成hello目標體)。然后用make驗證所編寫的Makefile是否正確。

(3)將上述Makefile使用變量替換實現。同樣用make驗證所編寫的Makefile是否正確

(4)用編輯另一Makefile,取名為Makefile1,不使用變量替換,但用兩個目標體實現(也就是首先將hello.c和hello.h編譯為hello.o,再將hello.o編譯為hello),再用make的”-f”選項驗證這個Makefile1的正確性。

(5)將上述Makefile1使用變量替換實現。

3.實驗步驟

(1)用Vi打開上述兩個代碼文件“hello.c”和“hello.h”。

(2)在shell命令行中用Gcc嘗試編譯,使用命令:”Gcc hello.c –o hello”,并運行hello可執行文件查看結果。

(3)刪除此次編譯的可執行文件:rm hello。

(4)用Vi編輯Makefile,如下所示:

?

hello:hello.c hello.h

Gcc hello.c -o hello

?

(5)退出保存,在shell中鍵入:make,查看結果。

(6)再次用Vi打開Makefile,用變量進行替換,如下所示:

?

OBJS :=hello.o

CC :=Gcc

hello:$(OBJS)

$(CC) $^ -o $@

?

(7)退出保存,在shell中鍵入:make,查看結果。

(8)用Vi編輯Makefile1,如下所示:

?

hello:hello.o

Gcc hello.o -o hello

hello.o:hello.c hello.h

Gcc -c hello.c -o hello.o

?

(9)退出保存,在shell中鍵入:make -f Makefile1,查看結果。

(10)再次用Vi編輯Makefile1,如下所示:

?

OBJS1 :=hello.o

OBJS2 :=hello.c hello.h

CC :=Gcc

hello:$(OBJS1)

$(CC) $^ -o $@

$(OBJS1):$(OBJS2)

$(CC) -c $< -o $@

?

在這里請注意區別“$^”和“$<”。

(11)退出保存,在shell中鍵入:make -f Makefile1,查看結果

4.實驗結果

各種不同形式的makefile都能完成其正確的功能。

使用autotools生成包含多文件的Makefile

1.實驗目的

通過使用autotools生成包含多文件的Makefile,進一步掌握autotools的正確使用方法。同時,掌握Linux下安裝軟件的常用方法。

2.實驗過程

(1)在原目錄下新建文件夾auto。

(2)利用上例的兩個代碼文件“hello.c”和“hello.h”,并將它們復制到該目錄下。

(3)使用autoscan生成configure.scan。

(4)編輯configure.scan,修改相關內容,并將其重命名為configure.in。

(5)使用aclocal生成aclocal.m4。

(6)使用autoconf生成configure。

(7)使用autoheader生成config.in.h。

(8)編輯Makefile.am。

(9)使用automake生成Makefile.in。

(10)使用configure生成Makefile。

(11)使用make生成hello可執行文件,并在當前目錄下運行hello查看結果。

(12)使用make install將hello安裝到系統目錄下,并運行,查看結果。

(13)使用make dist生成hello壓縮包。

(14)解壓hello壓縮包。

(15)進入解壓目錄。

(16)在該目錄下安裝hello軟件。

3.實驗步驟

(1)mkdir ./auto。

(2)cp hello.* ./auto(假定原先在“hello.c”文件目錄下)。

(3)命令:autoscan。

(4)使用Vi編輯configure.scan為:

?

# -*- Autoconf -*-

# Process this file with autoconf to produce a configure script.

?

AC_PREREQ(2.59)

AC_INIT(hello, 1.0)

AM_INIT_AUTOMAKE(hello,1.0)

AC_CONFIG_SRCDIR([hello.h])

AC_CONFIG_HEADER([config.h])

# Checks for programs.

AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile)

?

(5)保存退出,并重命名為configure.in。

(6)運行:aclocal。

(7)運行:autoconf,并用ls查看是否生成了configure可執行文件。

(8)運行:autoheader。

(9)用Vi編輯Makefile.am文件為:

?

AUTOMAKE_OPTIONS=foreign

bin_PROGRAMS=hello

hello_SOURCES=hello.c hello.h

?

(10)運行:automake。

(11)運行:./configure。

(12)運行:make。

(13)運行:./hello,查看結果是否正確。

(14)運行:make install。

(15)運行:hello,查看結果是否正確。

(16)運行:make dist。

(17)在當前目錄下解壓hello-1.0.tar.gz:tar –zxvf hello-1.0.tar.gz。

(18)進入解壓目錄:cd ./hello-1.0。

(19)下面開始Linux下常見的安裝軟件步驟:./configure。

(20)運行:make。

(21)運行:./hello(在正常安裝時這一步可省略)。

(22)運行:make install。

(23)運行:hello,查看結果是否正確。

4.實驗結果

能夠正確使用autotools生成Makefile,并且能夠安裝成功短小的Hello軟件。

總結

以上是生活随笔為你收集整理的gcc选项 和 gdb 使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

97人人模人人爽人人喊中文字 | 91黄色成人 | 国产一级二级三级在线观看 | 精品国产一区二区三区男人吃奶 | 国产精品日韩久久久久 | 国产成人一级 | 日韩高清免费在线 | 久久96国产精品久久99软件 | 成年人在线观看网站 | 丰满少妇麻豆av | 深夜免费福利在线 | 99这里只有 | 最近中文字幕大全中文字幕免费 | av电影在线观看完整版一区二区 | 日韩理论片中文字幕 | 91视频免费 | 中文字幕日韩精品有码视频 | 一区二区激情视频 | 中文字幕在线观看的网站 | 久久综合五月 | 欧美久久久久久久久中文字幕 | 在线观看一级 | 日本在线精品视频 | 九九在线国产视频 | 一区二区三区四区五区在线视频 | 国产精品12| 视频在线观看亚洲 | 欧美a性 | 欧美一区在线观看视频 | 2019中文字幕第一页 | 日本中文字幕在线视频 | 国产精品亚洲视频 | 十八岁以下禁止观看的1000个网站 | 操夜夜操| 日韩一二三区不卡 | 亚洲妇女av | 国产精品9999久久久久仙踪林 | 麻豆视频免费入口 | 亚洲女在线| 日日爱999 | .国产精品成人自产拍在线观看6 | 9色在线视频 | 精品字幕| 天天搞天天干天天色 | 欧美日韩一区二区在线 | 国产免费嫩草影院 | 国内精品视频久久 | 日韩欧美综合精品 | 免费高清男女打扑克视频 | 日本久久免费视频 | 色综合天天狠天天透天天伊人 | 免费高清在线观看电视网站 | 伊人五月婷 | 日韩理论影院 | 成人福利在线播放 | 国产精品免费在线播放 | 欧美日韩一区二区在线观看 | 永久免费的av电影 | 免费观看mv大片高清 | 国产一二区在线观看 | 亚洲伊人网在线观看 | 在线国产中文 | 99在线精品观看 | 男女免费视频观看 | 美女在线观看网站 | 最近日本mv字幕免费观看 | 视频在线观看日韩 | 国产精品一级在线 | 国产一级片直播 | 成人手机在线视频 | 日本中文字幕久久 | 欧美性色综合网 | 国产日韩视频在线观看 | 国产精品一区二区免费在线观看 | 国内久久| 日韩欧美高清视频在线观看 | 国产a国产 | 激情中文在线 | 一区二区在线影院 | 91传媒免费在线观看 | 一区二区电影网 | 精品久久一区二区三区 | 中文字幕高清 | 伊人精品影院 | 在线观看色视频 | 精品成人a区在线观看 | 国产99久久九九精品 | 国产午夜精品理论片在线 | 久草在线视频免费资源观看 | 欧美日韩69| 婷婷丁香色 | 日韩91精品 | 久久99热久久99精品 | 精品视频99 | 最近字幕在线观看第一季 | 在线电影 你懂得 | 国产网红在线 | 国产在线观看高清视频 | 国产精品成人久久久 | 99久久精品免费看国产四区 | 久久狠狠婷婷 | 免费在线观看av网址 | 三三级黄色片之日韩 | 免费看的黄色录像 | 日韩黄色一区 | 婷婷av网站 | 欧美一二区视频 | 婷婷深爱 | 国产资源站 | 日韩综合精品 | 在线观看视频你懂得 | 五月激情亚洲 | 精品国产电影一区二区 | 91香蕉视频黄| 色婷婷激情电影 | 丁香色天天 | 黄色一级免费 | 久久一区二区免费视频 | 麻豆一区二区三区视频 | 精品亚洲免费 | 免费久久网站 | 在线观看av片 | 国产成人61精品免费看片 | 亚洲一级片在线看 | 高潮久久久 | 欧美日韩高清 | 国产精品久久久久久久久久久久 | 久久精品99国产精品亚洲最刺激 | 亚洲 在线| 97视频免费在线观看 | 国产二区av | 欧美激情精品久久久久久免费印度 | 日韩欧在线 | 久久久久久久久久免费 | 久久久久久久久久亚洲精品 | 少妇搡bbbb搡bbb搡69 | 日日爱视频 | 99在线国产 | 日本在线观看一区二区三区 | 一级黄色a视频 | 国产中文字幕在线观看 | 97精品国产97久久久久久久久久久久 | 99精品在线免费在线观看 | 色婷婷国产在线 | 久久精品网址 | 玖玖爱在线观看 | 午夜精品福利一区二区 | 五月天中文在线 | 最新av网址在线观看 | 国产免费一区二区三区最新 | 日韩av视屏 | 五月婷婷网站 | 免费色网站 | 在线视频 亚洲 | av电影免费| 狠狠色综合欧美激情 | 久久精品99精品国产香蕉 | 97成人在线观看 | 国产精品久久久久久久久久妇女 | 欧美黑人性爽 | 国产亚洲精品v | 国产精品美女久久 | 国产精品一区二区你懂的 | 午夜精品一区二区国产 | 精品视频在线看 | www.超碰97.com| 在线观看免费国产小视频 | 天堂在线视频中文网 | 91九色视频观看 | 免费久久视频 | 一区二区三区电影大全 | 精品欧美一区二区在线观看 | 久久午夜国产精品 | 99久久超碰中文字幕伊人 | 色婷婷伊人 | 精品在线免费观看 | 亚洲第一区精品 | 在线视频 国产 日韩 | 福利一区二区在线 | 午夜精品久久久久久久99热影院 | 日韩国产精品久久 | 成人av免费在线观看 | 免费一级黄色 | 免费av成人在线 | 最新超碰在线 | 色噜噜在线观看 | 欧美精品一区二区在线播放 | 五月婷久| 91精品视频免费看 | 激情视频区| 久久精品九色 | 手机成人在线电影 | 狠狠撸电影 | 狠狠色丁香九九婷婷综合五月 | 久久不卡国产精品一区二区 | 日韩精品一区二区在线观看 | 日韩黄色一区 | 黄色aaaaa| 热久久免费国产视频 | 九九天堂| 国产91探花 | 六月丁香六月婷婷 | 色五月成人 | 亚洲国产成人精品在线 | 成人久久久久久久久久 | 久久亚洲区 | 亚洲国产美女精品久久久久∴ | 日韩精品一区二区三区三炮视频 | 国产成人精品综合久久久久99 | 免费视频97 | 欧美一级日韩免费不卡 | 天天操天天色天天射 | 久久视频这里只有精品 | 久久久国产99久久国产一 | 超碰在线98 | 97精品一区二区三区 | 91视频久久久 | 午夜性生活片 | 亚洲三级在线免费观看 | a黄色一级 | 久久国产精品视频观看 | 黄色99视频 | 狠狠综合久久 | 精品久久九九 | 精品视频国产 | 久久高视频 | 国产成人精品一区二区三区福利 | 久久精品国产精品亚洲精品 | free,性欧美 九九交易行官网 | 午夜视频在线观看一区 | 国内精品视频一区二区三区八戒 | 日韩黄色大片在线观看 | 超碰在线中文字幕 | 色伊人网 | 国产中文字幕国产 | 精品久久久久久国产偷窥 | 人人讲 | 亚洲免费观看在线视频 | 久久精品视频国产 | 91福利视频久久久久 | 美女久久精品 | 久久精品久久精品久久精品 | 97精品超碰一区二区三区 | 国产精品高清在线 | 在线视频区 | 国产激情小视频在线观看 | 久久精视频 | 久久久久国产精品午夜一区 | 91福利在线导航 | 中文字幕一区二区三区在线播放 | 中文字幕在线视频一区 | 精品久久久久久亚洲综合网站 | 国产精品一区免费观看 | 亚洲精品国久久99热 | 久久久蜜桃 | 国产一区二区精品在线 | 亚洲综合色网站 | 黄污在线看 | 91精品蜜桃 | 中文字幕一区二区在线观看 | 免费不卡中文字幕视频 | 伊人婷婷| 日韩在线大片 | 人人插人人插 | 精品欧美一区二区三区久久久 | 欧美精品亚洲精品 | www.天天色.com| 国产在线色视频 | 伊人久久婷婷 | 最新超碰在线 | 成人久久久久久久久久 | 久久久香蕉视频 | 最近中文字幕国语免费高清6 | 免费久久网| 免费在线观看国产黄 | 国产亚洲精品bv在线观看 | 日本精品视频在线播放 | 在线有码中文 | 91亚洲狠狠婷婷综合久久久 | 深爱激情五月综合 | 国产精品入口66mio女同 | 91三级在线观看 | 国产视频日韩视频欧美视频 | 九色精品免费永久在线 | 亚洲综合小说 | 国产午夜精品免费一区二区三区视频 | 日韩v欧美v日本v亚洲v国产v | 亚洲一一在线 | av电影免费在线看 | 精品极品在线 | caobi视频| 久久久久国产精品免费免费搜索 | 伊人五月婷 | 午夜av免费看 | 免费看特级毛片 | 波多野结衣在线视频免费观看 | 国产剧在线观看片 | 中文字幕在线播放av | 超碰97在线资源站 | 色多多在线观看 | 成人超碰97 | 中文字幕久久网 | 美女一级毛片视频 | 91精品国产高清自在线观看 | 91亚洲狠狠婷婷综合久久久 | 丰满少妇对白在线偷拍 | 亚洲乱亚洲乱亚洲 | 曰本免费av | 日日日操操 | 国产91在线免费视频 | 午夜国产福利在线 | 日韩三区在线 | 中文久久精品 | www.亚洲视频.com| 伊人五月天婷婷 | 色国产在线| 在线免费视| 国产午夜精品一区二区三区嫩草 | 激情综合色图 | 精品在线视频播放 | 色婷五月天 | 久一久久 | 国产喷水在线 | 久久精品爱视频 | 激情综合亚洲 | 日韩激情视频在线观看 | 在线99 | 成人免费看片网址 | 国产精品久久久久久久电影 | 久久久久一区二区三区四区 | 亚洲成人家庭影院 | 精品久久精品久久 | 美女免费视频网站 | www.夜色.com| 午夜久久影院 | 视频一区在线免费观看 | 亚洲欧美在线综合 | 午夜18视频在线观看 | 国产成人精品久久久久 | 日日干日日操 | 一级黄色大片在线观看 | 国产视频在线观看一区 | 亚洲三级黄色 | 中文字幕免费高清在线观看 | 超碰公开在线观看 | 日韩中文字幕视频在线 | 久久久久麻豆v国产 | 免费高清在线视频一区· | 亚洲精品一区二区在线观看 | 国产中文伊人 | 欧美精品黑人性xxxx | 久久久久久久久久久久久久免费看 | 五月亚洲 | 成人h动漫在线看 | 久久精品久久久精品美女 | 精品一区二区在线免费观看 | 日韩一区二区三区免费电影 | 综合黄色网 | 免费在线观看视频一区 | 激情视频区| 开心激情网五月天 | 69亚洲乱| 午夜色大片在线观看 | 精品99在线 | 亚洲欧美少妇 | 亚洲精品 在线视频 | 欧美日韩不卡在线视频 | 亚洲成av人片在线观看 | 国产一区二区三精品久久久无广告 | 国产精品日韩久久久久 | 国产精品观看在线亚洲人成网 | 91av亚洲| 日韩在观看线 | 国产一区电影在线观看 | 日韩色一区二区三区 | 在线观看免费黄色 | 国产精品片 | 免费在线观看av网站 | 国产视频九色蝌蚪 | 黄色av成人在线观看 | 人人看黄色 | 亚洲人人av | 午夜视频99| 亚洲成人av在线播放 | 国产在线视频一区二区 | 欧美日韩在线看 | 国产一级做a爱片久久毛片a | 美女久久久久久久久久久 | 免费人成在线观看 | 在线观看免费成人av | 成人cosplay福利网站 | 黄色特级毛片 | 欧美成人xxx | 黄色精品国产 | 香蕉视频在线看 | 日韩av视屏在线观看 | 天天干天天综合 | 国产91勾搭技师精品 | 欧美国产视频在线 | 一区二区三区在线视频观看58 | 91人人澡人人爽 | 久久精品国产一区二区三区 | 国产精品日韩欧美 | 久操久| 免费黄色a网站 | 18国产精品福利片久久婷 | 制服丝袜成人在线 | 国产在线欧美日韩 | 日日干天天操 | 免费av在线网站 | 天天综合天天做天天综合 | 激情www| 欧美成人精品三级在线观看播放 | 免费视频色 | 中文在线字幕免费观 | 午夜黄色大片 | 久久97超碰 | 99热最新精品| 在线观看激情av | 最新av网站在线观看 | 久久精品5 | 91av观看 | 欧洲激情综合 | 亚洲综合射 | 九九视频免费观看视频精品 | 人人射人人射 | www.888av| 开心色激情网 | 九九在线视频免费观看 | 中文字幕欧美三区 | 狠狠色狠狠综合久久 | 热久久免费国产视频 | 综合成人在线 | 午夜av大片| 美女久久久久久久久久 | 西西大胆啪啪 | 午夜精品电影一区二区在线 | 国产精品久久久久一区 | 最近中文字幕在线中文高清版 | 国产精品免费视频一区二区 | 国产又粗又猛又色又黄网站 | 在线韩国电影免费观影完整版 | 精品久久久精品 | 在线国产精品视频 | 欧美a级片网站 | 天天干天天干天天操 | 亚洲最新av在线网址 | av女优中文字幕在线观看 | 在线а√天堂中文官网 | 中文字幕日韩高清 | 911av视频| 久久福利国产 | 黄色三级网站在线观看 | 午夜精品婷婷 | 亚洲国产精品va在线看黑人 | 狠狠狠色丁香婷婷综合久久五月 | 成人片在线播放 | 91视频国产高清 | 亚洲高清激情 | 日韩欧美在线免费观看 | 在线日本v二区不卡 | 香蕉视频在线视频 | 国产午夜精品福利视频 | 欧美日韩国产在线精品 | 欧美日韩国产一区二区三区 | 久久精品麻豆 | 日韩资源在线观看 | 亚洲精品免费在线观看 | 国产精品一区在线观看 | 亚洲视频在线观看免费 | 久久这里| 国产亚洲精品久久久网站好莱 | 五月婷婷在线视频观看 | 天天av综合网 | av综合在线观看 | 国产片网站 | 国产福利91精品 | mm1313亚洲精品国产 | 黄色av网站在线免费观看 | 日韩免费视频在线观看 | 亚洲精品mv在线观看 | 日韩电影一区二区在线 | 婷婷丁香狠狠爱 | 日韩视频专区 | 福利一区在线 | 日韩有码专区 | 国产精品理论片在线播放 | 97超碰总站 | 国内久久久久久 | 深夜免费福利 | 成人毛片一区 | 手机看片久久 | 在线色亚洲| 欧美a性 | 91精品久久久久久综合乱菊 | 色六月婷婷 | 17婷婷久久www | 精品国产_亚洲人成在线 | 日韩av综合网站 | 欧美专区日韩专区 | 国产拍揄自揄精品视频麻豆 | 激情丁香| 天堂网中文在线 | 丝袜美腿在线播放 | 91精品久久久久久综合五月天 | 看毛片网站 | 亚洲黄色激情小说 | 久久久久中文 | 欧美性生活小视频 | 国产高清在线免费 | 色多多在线观看 | 国产精品久久久久久久久久 | 国产成人中文字幕 | 一区二区视 | 最新高清无码专区 | 国产视频99 | 在线精品亚洲 | 日韩手机在线观看 | 伊人成人久久 | 国产成视频在线观看 | av日韩国产 | 一级黄视频 | 日本动漫做毛片一区二区 | 91精品视频导航 | 免费三级在线 | 欧美 日韩 国产 成人 在线 | 美女视频免费一区二区 | 午夜精品视频福利 | 成年人免费在线看 | 日韩视频精品在线 | 成人a免费看 | 久久综合久久综合这里只有精品 | 超级碰碰碰免费视频 | 久久99国产精品自在自在app | 国产精品自产拍在线观看 | 在线观看中文字幕网站 | 91精品国产99久久久久久红楼 | 一区二区三区四区五区在线视频 | 国产成人久久77777精品 | 免费看黄色小说的网站 | 久久久黄视频 | 中文字幕成人在线观看 | 91成人网在线 | 精品国产诱惑 | 在线观看aaa | 久久久免费毛片 | 免费在线播放黄色 | 婷婷av资源 | 日韩在线视频不卡 | 五月婷婷丁香激情 | 日韩成人av在线 | 最近2019中文免费高清视频观看www99 | 又黄又刺激的视频 | 日韩欧美在线视频一区二区三区 | 国产亚洲综合性久久久影院 | 天天干.com | 日韩最新中文字幕 | 久久久免费毛片 | 久久看免费视频 | av片在线观看免费 | 九九免费精品视频 | 国产成人精品免费在线观看 | 三级黄色在线观看 | 黄色片免费电影 | 丁香婷婷色月天 | 99精品欧美一区二区三区黑人哦 | 免费精品在线视频 | 免费亚洲精品视频 | 在线观看精品黄av片免费 | av福利免费 | 中文字幕在线观看的网站 | 日韩三级精品 | 日韩电影久久久 | 狠狠操天天操 | 国产96在线| 激情五月婷婷网 | 欧美 日韩 成人 | 国产 日韩 欧美 自拍 | 色吊丝在线永久观看最新版本 | 四虎影视久久久 | 免费a级黄色毛片 | 色老板在线 | 久久综合九色综合欧美就去吻 | 亚洲精品播放 | 91自拍91 | 丁香资源影视免费观看 | 久久久久久久久毛片精品 | 色福利网站 | 91久久奴性调教 | 91在线精品观看 | 久久精品国产成人精品 | 在线a人v观看视频 | 日韩高清成人 | 国产一级视频免费看 | 四虎影视成人永久免费观看视频 | 美女福利视频一区二区 | 91污在线 | 精品国产综合区久久久久久 | 99视频精品全部免费 在线 | 欧美天堂视频在线 | 日韩精品久久久久久久电影99爱 | 亚洲成人精品久久 | 久久av伊人 | 最新国产精品亚洲 | 日韩精品免费在线观看视频 | 91久久久久久久一区二区 | zzijzzij亚洲成熟少妇 | 一级黄色a视频 | 国产1区2区 | 天天色天天干天天色 | 国产精品乱码高清在线看 | 亚洲精品乱码 | 成人午夜网址 | 亚洲成a人片综合在线 | 国产夫妻性生活自拍 | 亚洲综合情 | 手机成人在线 | 在线观看免费一区 | 国产成人99av超碰超爽 | 精品一区二区三区四区在线 | 在线观看国产高清视频 | 日韩欧美专区 | 99久久婷婷国产精品综合 | 国产精品成人一区二区 | 久久久久久久久久免费 | 精品亚洲一区二区 | 亚洲欧洲美洲av | 日韩欧美电影在线 | 亚洲精品白浆高清久久久久久 | 久久综合亚洲鲁鲁五月久久 | 五月婷婷久 | 在线观看成人一级片 | av网站在线免费观看 | 四虎成人精品永久免费av | 国产精品美女毛片真酒店 | 91精选在线观看 | 爱爱av在线 | 九色91视频| 一本大道久久精品懂色aⅴ 五月婷社区 | 欧美日韩国产精品一区二区三区 | 免费色av| 伊人婷婷激情 | 国产亚洲精品久久久久久久久久久久 | 日女人免费视频 | www免费看片com | 夜夜夜草| 国产精品不卡一区 | 91九色视频国产 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 美女性爽视频国产免费app | 国产免费又黄又爽 | 成人中心免费视频 | 99免费看片| 久久精选视频 | 最新成人av| 国产首页 | 黄色三级久久 | 在线看中文字幕 | 国产高清久久 | 视频在线日韩 | 亚洲黄网站 | 国产生活一级片 | 狠狠天天| 成片人卡1卡2卡3手机免费看 | 国产69久久精品成人看 | 国产成人精品亚洲日本在线观看 | 午夜丁香视频在线观看 | 人人玩人人添人人 | 日本色小说视频 | 亚洲污视频 | 97干com | 亚洲国产精品传媒在线观看 | 美女网站黄在线观看 | 中文字幕电影网 | 奇米网网址 | 四虎影视成人永久免费观看亚洲欧美 | 亚洲国产中文在线观看 | 91精品综合在线观看 | 激情婷婷综合 | 日本精品视频一区 | 久久久午夜视频 | 日本99久久 | 欧美日韩一区二区在线 | 国语精品久久 | 99在线视频精品 | 久久国产一区二区 | 国产一区在线视频 | 四虎在线观看网址 | 中文字幕一区二区三区久久 | 欧美黑人性猛交 | 在线观看成人毛片 | 美女视频网 | 久久婷婷丁香 | 国产在线 一区二区三区 | 亚州精品在线视频 | 久久久久久久av | 中文字幕一区二区三区乱码不卡 | 欧美日韩免费看 | 国产精品一区电影 | 国内99视频| 久久亚洲福利视频 | 日韩中文字幕在线看 | 西西4444www大胆视频 | 欧美午夜激情网 | 国产69久久精品成人看 | 成人av资源 | 久草观看视频 | 国产色婷婷 | 日韩电影一区二区在线观看 | 国产精品久久久久久吹潮天美传媒 | 黄色亚洲片 | 成年人免费观看在线视频 | 国产高清视频 | 激情视频久久 | 免费电影一区二区三区 | 中文字幕在线观看国产 | 日韩精品专区在线影院重磅 | 日本在线观看一区 | 婷婷在线免费 | 97在线精品国自产拍中文 | 久久久久久久久久免费 | 五月天堂网| a亚洲视频 | 亚洲精品在线二区 | 激情五月婷婷激情 | 99电影456麻豆| 91福利社区在线观看 | 久久夜色精品国产欧美乱 | 日韩精品无 | 日韩欧美69| 日本黄色免费观看 | aaa日本高清在线播放免费观看 | av夜夜操| 国产精品欧美久久久久天天影视 | 精品专区一区二区 | 中文字幕亚洲欧美 | 中文字幕免费高清在线 | 国产精久久久 | 免费在线激情电影 | 欧美在线一 | 国产精品毛片久久久久久 | 久草在线久 | 久久中文欧美 | 日韩精品欧美专区 | 中文字幕免费一区二区 | 99精品在线看 | 色成人亚洲 | 国产一二三区在线观看 | 天天操天天色天天 | 欧美性生活免费 | 免费久久99精品国产 | 婷婷六月天综合 | 波多野结衣在线视频一区 | 欧美日韩中文国产一区发布 | 免费黄色a级毛片 | 亚洲精品日韩av | 成人av一区二区兰花在线播放 | 99精品视频在线观看视频 | 久久精品牌麻豆国产大山 | 人成电影网 | 日韩综合一区二区三区 | 亚洲精品乱码久久久久久蜜桃91 | 成人国产精品电影 | 在线日韩中文字幕 | 亚洲精品小视频在线观看 | 国产福利在线免费 | 国产乱码精品一区二区蜜臀 | 99久久久国产精品免费观看 | 韩国av一区二区三区在线观看 | 午夜精品久久久久99热app | 欧美地下肉体性派对 | 人人爽人人爽人人片av免 | 日韩在线三级 | 国产黄色精品视频 | 国产成人av福利 | 五月宗合网 | 涩涩网站在线看 | av在线播放中文字幕 | 婷婷丁香色综合狠狠色 | 国产久草在线 | 亚洲日韩中文字幕 | 91大神dom调教在线观看 | 国内视频一区二区 | 日韩成人免费观看 | 欧美日韩一区二区三区在线免费观看 | 亚洲黄色免费电影 | 国产小视频在线观看免费 | 亚洲婷婷伊人 | 中文字幕国产精品一区二区 | 黄色特一级片 | 国产在线播放一区二区三区 | 91字幕 | 日韩性xxxx | 狠狠干网址| 天天添夜夜操 | 岛国片在线 | 在线观看av免费 | 国产成人精品一区二区三区福利 | 在线a人片免费观看视频 | 久热久草 | 成年人网站免费在线观看 | 亚洲干视频在线观看 | 亚洲欧美成人综合 | 色婷婷免费视频 | 国内视频一区二区 | 中文字幕a∨在线乱码免费看 | 亚洲精品乱码久久久久久高潮 | 欧美激情在线网站 | 91九色自拍 | 国产色一区 | 碰超在线| 99草视频 | 麻豆视频入口 | 2023天天干| 美女视频国产 | 奇米影视777四色米奇影院 | 精品视频在线观看 | 黄污视频网站 | 人人要人人澡人人爽人人dvd | 日韩成人不卡 | www视频在线观看 | 成人久久视频 | 亚洲精选久久 | 五月婷婷在线视频观看 | 99精品观看 | 成人av动漫在线 | 免费成人av在线 | 91.精品高清在线观看 | 国产精品嫩草55av | 亚洲国产精品视频 | 日韩在线观看你懂的 | 欧美一级片免费在线观看 | 99精品国产免费久久久久久下载 | 久久久精品国产一区二区电影四季 | 91在线观看视频网站 | 高清免费av在线 | 免费激情网 | 99久久精品国产毛片 | 亚洲四虎在线 | 美女久久网站 | av在线免费在线观看 | 国产999精品视频 | 黄色网址av | 国产色道 | 成人av片免费看 | 亚洲精品国偷自产在线91正片 | 在线中文字幕一区二区 | 久久久高清 | 亚洲天堂网在线视频观看 | 色.www | www国产在线 | 亚洲丁香日韩 | 婷婷丁香久久五月婷婷 | 伊人在线视频 | 在线视频久久 | 一区二区三区四区影院 | 日本爱爱片 | 精品福利网 | 国产小视频在线观看 | 日精品在线观看 | 国产成人精品999在线观看 | 狠狠色丁婷婷日日 | 91影视成人| 久久在线精品 | 超碰国产在线播放 | 久草综合视频 | 久久最新 | 色偷偷88欧美精品久久久 | 日韩精品视频在线免费观看 | 久久精品99北条麻妃 | 亚洲区视频在线观看 | 91视频啪 | 欧美三级高清 | 中文字幕电影在线 | 天天综合色天天综合 | 国产免费观看视频 | 91精品电影 | 午夜久久久影院 | 亚洲男人天堂a | 成人黄在线观看 | 色天天综合久久久久综合片 | 日韩二区三区在线 | 中文字幕五区 | 欧洲精品一区二区 | a久久久久 | 日韩av一区二区在线影视 | 91人网站 | 欧美久久久久久久久久久久 | 在线国产精品视频 | 成人av一区二区兰花在线播放 | 99色视频在线 | 在线一区观看 | 国产精品一区二区免费在线观看 | 国产免费一区二区三区最新 | 日韩免费在线观看视频 | 97超级碰碰碰视频在线观看 | 中文字幕免费在线看 | 久久字幕精品一区 | 91免费版在线观看 | 国产精品久久久免费看 | 99色婷婷 | 国产精品爽爽久久久久久蜜臀 | 欧美日韩一级在线 | 91电影福利 | 蜜臀av性久久久久av蜜臀妖精 | 欧美性大战 | 国产在线观看一 | 狠狠色丁香久久婷婷综 | 成人丁香花 | 在线中文字幕电影 | 天天操天天添 | 伊人狠狠色 | 九九精品久久 | 成人av在线直播 | 久久精品精品 | 亚洲电影黄色 | 午夜久操| 91在线欧美 | 欧美精品v国产精品v日韩精品 | 亚洲人成人在线 | 国产精品一区专区欧美日韩 | 亚洲精品一区二区三区新线路 | 黄色91在线观看 | 91福利视频免费 | 99视频在线免费播放 | 精品天堂av | 日韩理论片在线观看 | 在线观看国产 | 天天天综合 | 在线观看的黄色 | 国产精品18毛片一区二区 | 99视频在线| 国产精品18久久久久vr手机版特色 | 九月婷婷人人澡人人添人人爽 | 中文字幕色站 | 国产精品毛片久久 | 日韩免费电影一区二区 | 亚州欧美视频 | 在线免费视频 你懂得 | 美女网站视频一区 | 国产最新网站 | 在线免费高清视频 | 中文字幕有码在线观看 | 国产精品久久久久久99 | 在线不卡视频 | 美女久久久久久久久久 | 国产91在线 | 美洲 | 91在线观看高清 | 欧美乱码精品一区 | 亚洲精品女人 | 天天操天天干天天爽 | 国产精品丝袜久久久久久久不卡 | 亚洲免费精彩视频 | 国产亚洲精品美女 | av电影一区二区 | 操综合 | 黄色毛片视频免费观看中文 | 国产综合精品一区二区三区 | 超碰在线97免费 | 日韩最新中文字幕 | 久久99在线观看 | 麻豆免费视频网站 | 日韩欧美一二三 | 中文在线免费看视频 | 欧美在线视频a | 国产黄色片一级 | 狠狠狠色丁香婷婷综合久久88 | 日韩精品在线播放 | 婷婷丁香视频 | 激情校园亚洲 | 黄色www | 国产精品精品久久久 | 美女很黄免费网站 | 夜色在线资源 | 中文不卡视频 | 91久久丝袜国产露脸动漫 | 久久这里精品视频 | 国产成人免费精品 | 欧美日韩不卡一区 | 精品99久久| 又黄又刺激又爽的视频 | 天天插一插 | 成人精品99| 人人草人| 黄色a一级视频 | 色综合久久久久综合体 | 一区二区三区四区精品视频 | 视频一区二区在线 | 美女黄久久 | 一区二区三区四区五区在线 | 午夜av免费在线观看 | 日本精品视频免费 | 国产一区二区三区黄 | 久草在线最新免费 | 久草久热 | 久久国产91| 亚洲永久国产精品 | 在线视频免费观看 |