GCC的編譯和調試--入門介紹

編譯與調試
1.1編譯的概念和理解
在進行C程序開發時,編譯就是將編寫的C語言代碼變成可執行程序的過程,這一過程
是由編譯器來完成的。編譯器就是完成程序編譯工作的軟件,在進行程序編譯時完成了一系
列復雜的過程。
1.1.1程序編譯的過程
在執行這一操作時,程序完成了復雜的過程。一個程序的編譯,需要完成詞法分析、語
法分析、中間代碼生成、代碼優化、目標代碼生成。本章將講解這些步驟的作用與原理。
(1)詞法分析。指的是對由字符組成的單詞進行處理,從左至右逐個字符地對源程序進
行掃描,產生一個個的單詞符號。然后把字符串的源程序改造成為單詞符號串的中間程序。
在編譯程序時,這一過程是自動完成的。編譯程序會對代碼的每一個單詞進行檢查。如果單
詞發生錯誤,編譯過程就會停止并顯示錯誤。這時需要對程序中的錯誤進行修改。
(2)語法分析。語法分析器以單詞符號作為輸入,分析單詞符號串是否形成符合語法規
則的語句。例如,需要檢查表達式、賦值、循環等結構是否完整和符合使用規則。在語法分
析時,會分析出程序中錯誤的語句,并顯示出結果。如果語法發生錯誤,編譯任務是不能完
成的。
(3)中間代碼生成。中間代碼是源程序的一種內部表示,或稱中間語言。程序進行詞法
分析和語法分析以后,將程序轉換成中間代碼。這一轉換的作用是使程序的結構更加簡單和
規范。中間代碼生成操作是一個中間過程,與用戶是無關的。
(4)代碼優化。代碼優化是指對程序進行多種等價變換,使得從變換后的程序能生成更
有效的目標代碼。用戶可以在編譯程序時設置代碼優化的參數,可以針對不同的環境和設置
進行優化。
(5)目標代碼生成。目標代碼生成指的是產生可以執行的應用程序,這是編譯的最后一
個步驟。生成的程序是二進制的機器語言,用戶只能運行這個程序,而不能打開這個文件查
看程序的代碼。
1.1.2編譯器
所謂編譯器,是將編寫出的程序代碼轉換成計算機可以運行的程序的軟件。在進行C程
序開發時,編寫出的代碼是源程序的代碼,是不能直接運行的。需要用編譯器編譯成可以運
行的二進制程序。
在不同的操作系統下面有不同的編譯器。C程序是可以跨平臺運行的。但并不是說
Windows系統下C語言編寫的程序可以直接在Linux下面運行。Windows下面C語言編寫的
程序,被編譯成exe文件。這樣的程序只能在Windows系統下運行。如果需要在Linux系統
下運行,需要將這個程序的源代碼在Linux系統重新編譯。不同的操作系統下面有不同的編
譯器。Linux系統下面編譯生成的程序是不能在Windows系統上運行的。
1.2 gcc編譯器
gcc是Linux下的C程序編譯器,具有非常強大的程序編譯功能。在Linux系統下,C語
言編寫的程序代碼一般需要通過gcc來編譯成可執行程序。
1.2.1 gcc編譯器簡介
Linux系統下的gcc編譯器(GNU C Compiler)是一個功能強大、性能優越的編譯器。gcc
支持多種平臺的編譯,是Linux系統自由軟件的代表作品。gcc本來只是C編譯器的,但是后
來發展為可在多種硬體平臺上編譯出可執行程序的超級編譯器。各種硬件平臺對gcc的支持
使得其執行效率與一般的編譯器相比平均效率要高20%~30%。gcc編譯器能將C、C++源程
序、匯程語言和目標程序進行編譯鏈接成可執行文件。通過支持make工具,gcc可以實施項
目管理和批量編譯。
經過多年的發展,gcc已經發生了很大的變化。gcc已經不僅僅能支持C語言,還支持Ada
語言、C++語言、Java語言、Objective C語言、Pascal語言、COBOL語言等更多的語言集的編
譯。gcc幾乎支持所有的硬件平臺,使得gcc對于特定的平臺可以編譯出更高效的機器碼。
gcc在編譯一個程序時,一般需要完成預處理(preprocessing)、編譯(compilation)、匯
編(assembly)和鏈接(linking)過程。使用gcc編譯C程序時,這些過程是使用默認的設置
自動完成的,但是用戶可以對這些過程進行設置,控制這些操作的詳細過程。
1.2.2 gcc對源程序擴展名的支持
擴展名指的是文件名中最后一個點的這個點以后的部分。例如下面是一個C程序源文件
的擴展名。
5.1.c
那么這個文件的文件名是“5.1.c”,擴展名是“.c”。通常來說,源文件的擴展名標識源文
件所使用的編程語言。例如C程序源文件的擴展名一般是“.c”。對編譯器來說,擴展名控制
著缺省語言的設定。在默認情況下,gcc通過文件擴展名來區分源文件的語言類型。然后根據
這種語言類型進行不同的編譯。gcc對源文件的擴展名約定如下所示。
.c為擴展名的文件,為C語言源代碼文件。
.a為擴展名的文件,是由目標文件構成的庫文件。
.C,.cc或.cpp為擴展名的文件,標識為C++源代碼文件。
.h為擴展名的文件,說明文件是程序所包含的頭文件。
.i為擴展名的文件,標識文件是已經預處理過的C源代碼文件,一般為中間代碼文件。
.ii為擴展名的文件,是已經預處理過的C++源代碼文件,同上也是中間代碼文件。
.o為擴展名的文件,是編譯后的目標文件,源文件生成的中間目標文件。
.s為擴展名的文件,是匯編語言源代碼文件。
.S為擴展名的文件,是經過預編譯的匯編語言源代碼文件。
.o為擴展名的文件,是編譯以后的程序目標文件(Object file),目標文件經過連接成
可執行文件
此外,對于gcc編譯器提供兩種顯示的編譯命令,分別對應于編譯C和C++源程序的
編譯命令。
1.3 C程序的編譯
本章以一個實例講述如何用gcc編譯C程序。在編譯程序之前,需要用VIM編寫一個簡
單的C程序。在編譯程序時,可以對gcc命令進行不同的設置。
1.3.1編寫第一個C程序
本節將編寫第一個C程序。程序實現一句文本的輸出和判斷兩個整數的大小關系。本書
中編寫程序使用的編輯器是VIM。程序編寫步驟如下所示。
打開系統的終端。單擊“主菜單”|“系統工具”|“終端”命令,打開一個系統
終端。
在終端中輸入下面的命令,在用戶根目錄“root”中建立一個目錄。
mkdir c
在終端界面中輸入“vim”命令,然后按“Enter”鍵,系統會啟動VIM。
在VIM中按“i”鍵,進入到插入模式。然后在VIM中輸入下面的程序代碼。
#include<stdio.h>
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
}
void main()
{
int i,j,k;
i=3;
j=5;
printf("hello,Linux./n”);
k=max(i,j);
printf("%d/n",k);
}
代碼輸入完成以后,按“Esc”鍵,返回到普通模式。然后輸入下面的命令,保存文件。
:w/root/c/a.c
這時,VIM會把輸入的程序保存到c目錄下的文件a.c中。
再輸入“:q”命令,退出VIM。這時,已經完成了這個C程序的編寫。
1.3.2用gcc編譯程序
上面編寫的C程序,只是一個源代碼文件,還不能作為程序來執行。需要用gcc將這個
源代碼文件編譯成可執行文件。編譯文件的步驟如下所示。
打開系統的終端。單擊“主菜單”|“系統工具”|“終端”命令,打開一個系統
終端。這時進入的目錄是用戶根目錄“/root”。然后輸入下面的命令,進入到c目錄。
cd c
上一節編寫的程序就存放在這個目錄中。輸入“ls”命令可以查看這個目錄下的文件。
顯示的結果如下所示。
輸入下面的命令,將這個代碼文件編譯成可執行程序。
gcc a.c
查看已經編譯的文件。在終端中輸入“ls”命令,顯示的結果如下所示。
a.c a.out
輸入下面的命令對這個程序添加可執行權限。
chmod+x a.out
輸入下面的命令,運行這個程序。
./a.out
程序的運行結果如下所示。
hello,Linux.
5
從上面的操作可知,用gcc可以將一個C程序源文件編譯成一個可執行程序。編譯以
后的程序需要添加可執行的權限才可以運行。在實際操作中,還需要對程序的編譯進行各
種設置。
1.3.3查看gcc的參數
gcc在編譯程序時可以有很多可選參數。在終端中輸入下面的命令,可以查看gcc的這些
可選參數。
gcc--help
在終端中顯示的gcc的可選參數如下所示。進行程序編譯時,可以設置下面的這些參數。
用法:gcc[選項]文件...
選項:
-pass-exit-codes:在某一階段退出時返回最高的錯誤碼
--help:顯示此幫助說明
--target-help:顯示目標機器特定的命令行選項
-dumpspecs:顯示所有內建spec字符串
-dumpversion:顯示編譯器的版本號
-dumpmachine:顯示編譯器的目標處理器
-print-search-dirs:顯示編譯器的搜索路徑
-print-libgcc-file-name:顯示編譯器伴隨庫的名稱
-print-file-name=<庫>:顯示<庫>的完整路徑
-print-prog-name=<程序>:顯示編譯器組件<程序>的完整路徑
-print-multi-directory:顯示不同版本libgcc的根目錄
-print-multi-lib:顯示命令行選項和多個版本庫搜索路徑間的映射
-print-multi-os-directory:顯示操作系統庫的相對路徑
-Wa,<選項>:將逗號分隔的<選項>傳遞給匯編器
-Wp,<選項>:將逗號分隔的<選項>傳遞給預處理器
-Wl,<選項>:將逗號分隔的<選項>傳遞給鏈接器
-Xassembler<參數>:將<參數>傳遞給匯編器
-Xpreprocessor<參數>:將<參數>傳遞給預處理器
-Xlinker<參數>:將<參數>傳遞給鏈接器
-combine:將多個源文件一次性傳遞給匯編器
-save-temps:不刪除中間文件
-pipe:使用管道代替臨時文件
-time:為每個子進程計時
-specs=<文件>:用<文件>的內容覆蓋內建的specs文件
-std=<標準>:指定輸入源文件遵循的標準
--sysroot=<目錄>:將<目錄>作為頭文件和庫文件的根目錄
-B<目錄>:將<目錄>添加到編譯器的搜索路徑中
-b<機器>:為gcc指定目標機器(如果有安裝)
-V<版本>:運行指定版本的gcc(如果有安裝)
-v:顯示編譯器調用的程序
-###:與-v類似,但選項被引號括住,并且不執行命令
-E:僅作預處理,不進行編譯、匯編和鏈接
-S:編譯到匯編語言,不進行匯編和鏈接
-c:編譯、匯編到目標代碼,不進行鏈接
-o<文件>:輸出到<文件>
-x<語言>:指定其后輸入文件的語言。允許的語言包括c、c++、assembler等。
以-g、-f、-m、-O、-W或--param開頭的選項將由gcc自動傳遞給其調用的不同子進程。若要
向這些進程傳遞其他選項,必須使用-W<字母>選項。
1.3.4設置輸出的文件
在默認情況下,gcc編譯出的程序為當前目錄下的文件a.out。-o參數可以設置輸出的目
標文件。例如下面的命令,可以設置將代碼編譯成可執行程序do。
gcc a.c-o do
也可以設置輸出目錄文件為不同的目錄。例如下面的命令,是將目錄文件設置成/tmp目
錄下的文件do。
gcc a.c-o/tmp/do
輸入下面的命令,查看生成的目錄文件。結果如下所示,在編譯程序時生成的目錄為/tmp
目錄下的文件do。
-rwxrwxr-x 1 root root 5109 12-28 13:33/tmp/do
1.3.5查看編譯過程
參數-v可以查看程序的編譯過程和顯示已經調用的庫。輸入下面的命令,在編譯程序時
輸出編譯過程。
gcc-v a.c
顯示的結果如下所示。
使用內建specs。
目標:i386-redhat-linux
配置為:../configure--prefix=/usr--mandir=/usr/share/man
--infodir=/usr/share/info--enable-shared--enable-threads=posix
--enable-checking=release--with-system-zlib--enable-__cxa_atexit
--disable-libunwind-exceptions
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada
--enable-java-awt=gtk--disable-dssi--enable-plugin
--host=i386-redhat-linux
線程模型:posix
gcc版本4.1.2 20070925(Red Hat 4.1.2-33)
/usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1
-quiet-v a.c-quiet-dumpbase a.c-mtune=generic-auxbase a-version
-o/tmp/cc8P7rzb.s
忽略不存在的目錄“/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386-
redhat-linux/include”
#include"..."搜索從這里開始:
#include<...>搜索從這里開始:
/usr/local/include
/usr/lib/gcc/i386-redhat-linux/4.1.2/include
/usr/include
搜索列表結束。
GNU C版本4.1.2 20070925(Red Hat 4.1.2-33)(i386-redhat-linux)
由GNU C版本4.1.2 20070925(Red Hat 4.1.2-33)編譯。
GGC準則:--param ggc-min-expand=64--param ggc-min-heapsize=64394
Compiler executable checksum:ab322ce5b87a7c6c23d60970ec7b7b31
a.c:In function‘main’:
a.c:16:警告:‘main’的返回類型不是‘int’
as-V-Qy-o/tmp/ccEFPrYh.o/tmp/cc8P7rzb.s
GNU assembler version 2.17.50.0.18(i386-redhat-linux)using BFD version
version 2.17.50.0.18-1 20070731
/usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2
--eh-frame-hdr--build-id-m elf_i386--hash-style=gnu-dynamic-linker
/lib/ld-linux.so.2/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crt1.o
從顯示的編譯過程可知,gcc自動加載了系統的默認配置,調用系統的庫函數完成了程序
的編譯過程。
1.3.6設置編譯的語言
gcc可以對多種語言編寫的源代碼。如果源代碼的文件擴展名不是默認的擴展名,gcc就
無法編譯這個程序。可以用-x選擇來設置程序的語言。可以用下面的步驟來練習這一操作。
輸入下面的命令,將C程序文件復制一份。
cp a.c a.u
復制出的文件a.u是一個C程序文件,但擴展名不是默認的擴展名。這時輸入下面的
命令編譯這個程序。
gcc a.u
顯示的結果如下所示,表明文件的格式不能識別。
a.u:file not recognized:File format not recognized
collect2:ld返回1
這時,用-x參數設置編譯的語言,命令如下所示。這樣就可以正常地編譯文件a.u。
gcc-x'c'a.u
需要注意的是,這里的c需要用單引號擴起來。當編譯擴展名不是.c的C程序時,需要
使用-x參數。
1.3.7-asci設置ANSIC標準
ANSIC是American National Standards Institute(ANSI:美國標準協會)出版的C語言
標準。使用這種標準的C程序可以在各種編譯器和系統下運行通過。gcc可以編譯ANSIC的
程序,但是gcc中的很多標準并不被ANSIC所支持。在gcc編譯程序時,可以用-ansic來設置
程序使用ANSIC標準。例如下面的命令,在設置程序編譯時,用ANSIC標準進行編譯。
gcc-asci a.out
5.3.8
1.3.8 g++編譯C++程序
gcc可以編譯C++程序。編譯C程序和C++程序時,使用的是不同的命令。編譯C++程
序時,使用的命令是g++。該命令的使用方法與gcc是相似的。下面是使用g++命令編譯C++
程序的實例。
下面是一個C++程序的代碼,實現與1.3.1節程序同樣的功能。C++程序的代碼與C程序
的代碼非常相似。
#include<iostream>
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
}
int main()
{
int i,j,k;
i=3;
j=5;
printf("hello,Linux./n");
k=max(i,j);
printf("%d/n",k);
return(0);
}
輸入下面的命令,編譯這個C++程序。
g++5.2.cpp–o 5.2.out
輸入下面的命令,對這個程序添加可執行權限。
chmod+x 5.2.ou
輸入下面的命令,運行這個程序,程序的代碼如下所示。
hello,Linux.
5
從結果可知,這個程序與1.3.1節中的程序運行結果是相同的。
1.4編譯過程的控制
編譯過程指的是gcc對一個程序進行編譯時完成的內部處理和步驟。編譯程序時會自動
完成預處理(Preprocessing)、編譯(Compilation)、匯編(Assembly)和鏈接(Linking)4個
步驟。本節將講解如何對這4個步驟進行控制。
1.4.1編譯過程簡介
gcc把一個程序的源文件,編譯成一個可執行文件,中間包括很多復雜的過程。可以用圖
1-1來表示編譯中4個步驟的作用和關系。
源文件
匯編文件
目標文件
可執行文件
使用-E選項進行預處理
使用-S選項匯編
-c選項生成目標文件
鏈接目標文件和庫
生成可執行文件
預處理文件
圖1-1 gcc編譯源文件到可執行文件的過程
在4個過程中,每一個操作都完成了不同的功能。編譯過程的功能如下所示。
預處理:在預處理階段,主要完成對源代碼中的預編譯語句(如宏定義define等)和
文件包含進行處理。需要完成的工作是對預編譯指令進行替換,把包含文件放置到需
要編譯的文件中。完成這些工作后,會生成一個非常完整的C程序源文件。
編譯:gcc對預處理以后的文件進行編譯,生成以.s為后綴的匯編語言文件。該匯編語
言文件是編譯源代碼得到的匯編語言代碼,接下來交給匯編過程進行處理。匯編語言
是一種比C語言更低級的語言,直接面對硬盤進行操作。程序需要編譯成匯編指令以
后再編譯成機器代碼。
匯編:匯編過程是處理匯編語言的階段,主要調用匯編處理程序完成將匯編語言匯編
成二進制機器代碼的過程。通常來說,匯編過程是將.s的匯編語言代碼文件匯編為.o
的目標文件的過程。所生成的目標文件作為下一步鏈接過程的輸入文件。
鏈接:鏈接過程就是將多個匯編生成的目標文件以及引用的庫文件進行模塊鏈接生成
一個完整的可執行文件。在鏈接階段,所有的目標文件被安排在可執行程序中的適當
的位置。同時,該程序所調用到的庫函數也從各自所在的函數庫中鏈接到程序中。經
過了這個過程以后,生成的文件就是可執行的程序。
1.4.2控制預處理過程
參數-E可以完成程序的預處理工作而不進行其他的編譯工作。下面的命令,可以將本章
編寫的程序進行預處理,然后保存到文件a.cxx中。
gcc-E-o a.cxx a.c
輸入下面的命令,查看經過預處理以后的a.cxx文件。
vim a.cxx
可以發現,文件a.cxx約有800行代碼。程序中默認包含的頭文件已經被展開寫到到這個
預處理文件
匯編文件
目標文件
可執行文件
鏈接目標文件和庫
生成可執行文件
-c選項生成目標文件
使用-s選項匯編
使用-E選項進行預處理
文件中。顯示的文件a.cxx前幾行代碼如下所示。可見,在程序編譯時,需要調用非常多的頭
文件和系統庫函數。
#1"a.c"
#1"<built-in>"
#1"<command line>"
#1"a.c"
#1"/usr/include/stdio.h"1 3 4
#28"/usr/include/stdio.h"3 4
#1"/usr/include/features.h"1 3 4
#335"/usr/include/features.h"3 4
#1"/usr/include/sys/cdefs.h"1 3 4
#360"/usr/include/sys/cdefs.h"3 4
#1"/usr/include/bits/wordsize.h"1 3 4
#361"/usr/include/sys/cdefs.h"2 3 4
#336"/usr/include/features.h"2 3 4
#359"/usr/include/features.h"3 4
#1"/usr/include/gnu/stubs.h"1 3 4
#1"/usr/include/bits/wordsize.h"1 3 4
#5"/usr/include/gnu/stubs.h"2 3 4
#1"/usr/include/gnu/stubs-32.h"1 3 4
#8"/usr/include/gnu/stubs.h"2 3 4
#360"/usr/include/features.h"2 3 4
#29"/usr/include/stdio.h"2 3 4
1.4.3生成匯編代碼
參數-S可以控制gcc在編譯C程序時只生成相應的匯編程序文件,而不繼續執行后面的
編譯。下面的命令,可以將本章中的C程序編譯成一個匯編程序。
gcc-S-o a.s a.c
輸入下面的命令,查看匯編文件a.s。可以發現這個文件一共有60行代碼。這些代碼是
這個程序的匯編指令。部分匯編程序代碼如下所示。
.file"a.c"
.text
.globl max
.type max,@function
max:
pushl%ebp
movl%esp,%ebp
subl$4,%esp
movl 8(%ebp),%eax
cmpl 12(%ebp),%eax
jle.L2
movl 8(%ebp),%eax
movl%eax,-4(%ebp)
jmp.L4
.L2:
movl 12(%ebp),%eax
movl%eax,-4(%ebp)
.L4:
movl-4(%ebp),%eax
leave
ret
.size max,.-max
.section.rodata
.LC0:
.string"hello,Linux."
.LC1:
.string"%d/n"
.text
.globl main
.type main,@function
main:
leal 4(%esp),%ecx
andl$-16,%esp
.......
popl%ebp
leal-4(%ecx),%esp
ret
.size main,.-main
.ident"GCC:(GNU)4.1.2 20070925(Red Hat 4.1.2-33)"
.section.note.GNU-stack,"",@progbits
1.4.4生成目標代碼
參數-c可以使得gcc在編譯程序時只生成目錄代碼而不生成可執行程序。輸入下面的命
令,將本章中的程序編譯成目錄代碼。
gcc-c-o a.o a.c
輸入下面的命令,查看這個目錄代碼的信息。
file a.o
顯示文件a.o的結果如下所示,顯示文件a.o是一個可重定位的目標代碼文件。
a.o:ELF 32-bit LSB relocatable,Intel 80386,version 1(SYSV),not stripped
1.4.5鏈接生成可執行文件
gcc可以把上一步驟生成的目錄代碼文件生成一個可執行文件。在終端中輸入下面的命令。
gcc a.o-o aa.out
這時生成一個可執行文件aa.out。輸入下面的命令查看這個文件的信息。
file aa.out
顯示的結果如下所示,表明這個文件是可在Linux系統下運行的程序文件。
aa.out:ELF 32-bit LSB executable,Intel 80386,version 1(SYSV),dynamically
linked(uses shared libs),for GNU/Linux 2.6.9,not stripped
1.5 gdb調試程序
所謂調試,指的是對編好的程序用各種手段進行查錯和排錯的過程。進行這種查錯處理
時,并不僅僅是運行一次程序檢查結果,而是對程序的運行過程、程序中的變量進行各種分
析和處理。本節將講解使用gdb進行程序的調試。
1.5.1 gdb簡介
gdb是一個功能強大的調試工具,可以用來調試C程序或C++程序。在使用這個工具進
行程序調試時,主要使用gdb進行下面5個方面的操作。
啟動程序:在啟動程序時,可以設置程序運行環境。
設置斷點:斷點就是可以在程序設計時暫停程序運行的標記。程序會在斷點處停止,
用戶便于查看程序的運行情況。這里的斷點可以是行數、程序名稱或條件表達式。
查看信息:在斷點停止后,可以查看程序的運行信息和顯示程序變量的值。
分步運行:可以使程序一個語句一個語句的執行,這時可以及時地查看程序的信息。
改變環境:可以在程序運行時改變程序的運行環境和程序變量。
1.5.2在程序中加入調試信息
為了使用gdb進行程序調試,需要在編譯程序中加入供gdb使用的調試信息。方法是在
編譯程序時使用一個-g參數。在終端中輸入下面的命令,在編譯程序時加入調試信息。
gcc-g-o a.debug a.c
這時,編譯程序a.c,生成一個a.bedug的可執行程序。這個可執行程序中加入了供調試
所用的信息。
1.5.3啟動gdb
在調試文件以前,需要啟動gdb。在終端中輸入下面的命令。
gdb
這時,gdb的啟動信息如下所示。這些提示顯示了gdb的版本和版權信息。
GNU gdb Red Hat Linux(6.6-35.fc8rh)
Copyright(C)2006 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".
(gdb)
1.5.4在gdb中加載需要調試的程序
使用gdb調試一個程序之前,需要加載這個程序。加載程序的命令是file。在(gdb)提示
符后面輸入下面的命令加載程序a.debug。
file a.debug
命令的運行結果如下所示,顯示已經加載了這個文件,并且使用了系統庫文件。
Reading symbols from/root/c/a.debug...done.
Using host libthread_db library"/lib/libthread_db.so.1".
1.5.5在gdb中查看代碼
用gcc命令編譯程序加入了-g命令以后,編譯后的a.debug程序中加入了斷點。可以用list
命令顯示程序的源代碼和斷點。下面的步驟是查看加入斷點以后的代碼。
在(gdb)提示符后面輸入下面的命令。
list 1
這時,gdb會顯示第一個斷點以前的代碼。顯示的代碼如下所示。
1#include<stdio.h>
2
3 int max(int i,int j)
4{
5 if(i>j)
6{
7 return(i);
8}
9 else
10{
(gdb)
這時,按“Enter”鍵,顯示下一個斷點以前的代碼。結果如下所示。
11 return(j);
12}
13}
14
15 void main()
16{
17 int i,j,k;
18 i=3;
19 j=5;
20 printf("hello,Linux./n");
(gdb)
按“Enter”鍵,顯示下一個斷點以前的代碼。結果如下所示。
21 k=max(i,j);
22 printf("%d/n",k);
23}
(gdb)
1.5.6在程序中加入斷點
程序會運行到斷點的位置停止下來,等待用戶處理信息或者查看中間變量。如果自動設
置的斷點不能滿足調試要求,可以用break命令增加程序的斷點。例如需要在程序的第6行增
加一個斷點,可以輸入下面的命令。
break 6
這時gdb顯示的結果如下所示。
Breakpoint 1 at 0x8048402:file a.c,line 6.
輸入下面的命令,在程序的第18行、19行、21行增加斷點。
break 18
break 19
break 21
1.5.7查看斷點
命令info breakpoint可以查看程序中設置的斷點。輸入“info breakpoint”命令,結果如
下所示。顯示程序中所有的斷點。
1 breakpoint keep y 0x08048402 in max at a.c:6
2 breakpoint keep y 0x08048426 in main at a.c:18
3 breakpoint keep y 0x0804842d in main at a.c:19
4 breakpoint keep y 0x08048440 in main at a.c:21
加上相應的斷點編號,可以查看這一個斷點的信息。例如下面的命令就是查看第二個斷點。
info breakpoint 2
顯示的結果如下所示。
2 breakpoint keep y 0x08048426 in main at a.c:18
1.5.8運行程序
gdb中的run命令可以使這個程序以調試的模式運行。下面的步驟是分步運行程序,對程
序進行調試。
在(ddb)提示符后輸入“run”命令,顯示的結果如下所示。
Starting program:/root/c/a.debug
warning:Missing the separate debug info file:
/usr/lib/debug/.build-id/ac/2eeb206486bb7315d6ac4cd64de0cb50838ff6.debug
warning:Missing the separate debug info file:
/usr/lib/debug/.build-id/ba/4ea1118691c826426e9410cafb798f25cefad5.debug
Breakpoint 2,main()at a.c:18
18 i=3;
結果顯示了程序中的異常,并將異常記錄到了系統文件中。然后在程序的第二個斷
點的位置第18行停下。
這時輸入“next”命令,程序會在下一行停下,結果如下所示。
19 j=5;
輸入“continue”命令,程序會在下一個斷點的位置停下。結果如下所示。
Continuing.
Breakpoint 3,main()at a.c:19
21 k=max(i,j);
輸入“continue”命令,程序運行到結束。結果如下所示,表明程序已經運行完畢正
常退出。
5
Program exited with code 02.
step命令與next命令的作用相似,對程序實現單步運行。不同之處是,在遇上函數
調用時,step函數可以進行到函數內部。而next函數只是一步完成函數的調用。
1.5.9變量的查看
print命令可以在程序的運行中查看一個變量的值。本節將用下面的步驟來講解變量的查
看方法。
輸入下面的命令,運行程序。
run
程序在第一個斷點位置停下。顯示的結果如下所示。
Breakpoint 2,main()at a.c:18
18 i=3;
程序進入第18行之前停下,并沒有對i進行賦值。可以用下面的命令來查看i的值。
print i
顯示的結果如下所示,表示i現在只是一個任意值。
$5=-1076190040
輸入下面的命令,使程序運行一步。
step
顯示的結果如下所示。
19 j=5;
這時程序在19行以前停下,這時輸入下面的命令,查看i的值。
print i
這時顯示的i的結果如下所示。表明i已經賦值為3。
$6=3
這時輸入“step”命令,再次輸入“step”命令,顯示的結果如下所示。
21 k=max(i,j);
這時輸入“step”命令,會進入到子函數中,結果如下所示。這時,顯示了傳遞給函
數的變量和值。
max(i=3,j=5)at a.c:5
5 if(i>j)
這時,輸入“step”命令,顯示的結果如下所示,表明函數會返回變量j。
11 return(j);
輸入下面的命令,查看j的值。
print j
顯示的結果如下所示,表明j的值為5。
$7=5
這時再運行兩次“step”命令,顯示的結果如下所示。
22 printf("%d/n",k);
這時,輸入下面的命令,查看k的值。
print k
顯示的結果如下所示,表明k的值為5。
$8=5
17完成了程序的調試運行以后,輸入“q”命令,退出gdb。
1.6程序調試實例
本節講解一個程序調試實例。先編寫一個程序,在程序運行時,發現結果與預想結果有
些不同。然后用gdb工具進行調試,通過對單步運行和變量的查看,查找出程序的錯誤。
1.6.1編寫一個程序
本節將編寫一個程序,要求程序運行時可以顯示下面的結果。
1+1=2
2+1=3 2+2=4
3+1=4 3+2=5 3+3=6
4+1=5 4+2=6 4+3=7 4+4=8
很明顯,這個程序是通過兩次循環與一次判斷得到的。程序中需要定義三個變量。下面
用這個思路來編寫這個程序。
打開一個終端。在終端中輸入“vim”命令,打開VIM。
在VIM中按“i”鍵,進入到插入模式。然后在VIM中輸入下面的代碼。
#include<stdio.h>
main()
{
int i,j,k;
for(i=1;i<=4;i++)
{
for(j=1;j<=4;j++);
{
if(i>=j)
{
k=i+j;
printf("%d+%d=%d",i,j,k);
}
}
printf("/n");
}
}
在VIM中按“Esc”鍵,返回到普通模式。然后輸入下面的命令,保存這個文件。
:w/root/c/test.c
輸入“:q”命令退出VIM。很容易發現,在第二個循環后
1.6.2編譯文件
本節將對上一節編寫的程序進行編譯和運行。在運行程序時,會發現程序有錯誤。
在終端中輸入下面的命令,編譯這個程序。
gcc/root/c/test.c
程序可以正常編譯通過,輸入下面的命令,運行這個程序。
/root/c/a.out
程序的顯示結果是4個空行,并沒有按照預想的要求輸出結果。
輸入下面的命令,對這個程序進行編譯。在編譯加入-g參數,為gdb調試做準備。
gcc-g-o test.debug 6.2.c
這時,程序可以正常編譯通過。輸出的文件是test.debug。這個文件中加入了文件調
試需要的信息。
1.6.3程序的調試
本節將講述使用gdb對上一節編寫的程序進行調試,查找出程序中的錯誤。
在終端中輸入“gdb”命令,進入到gdb,顯示的結果如下所示。
GNU gdb Red Hat Linux(6.6-35.fc8rh)
Copyright(C)2006 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".
導入文件。在gdb中輸入下面的命令。
file/root/c/test.debug
這時顯示的結果如下所示。表明已經成功加載了這個文件。
Reading symbols from/root/c/test.debug...(no debugging symbols
found)...done.
Using host libthread_db library"/lib/libthread_db.so.1".
查看文件。在終端中輸入下面的命令。
list
顯示的文件查看結果如下所示。
1#include<stdio.h>
2
3 main()
4{
5 int i,j,k;
6 for(i=1;i<=4;i++)
7{
8 for(j=1;j<=4;j++);
9{
10 if(i>=j)
(gdb)
11{
12 k=i+j;
13 printf("%d+%d=%d",i,j,k);
14}
15}
16 printf("/n");
17}
18}
(gdb)
Line number 19 out of range;6.2.c has 18 lines.
在程序中加入斷點。從顯示的代碼可知,需要在第6行、第11行、第12行和第13
行加入斷點。在gdb中輸入下面的命令。
break 6
break 11
break 12
break 13
gdb顯示的添加斷點的結果如下所示。
Breakpoint 1 at 0x8048405:file 6.2.c,line 6.
Breakpoint 2 at 0x8048429:file 6.2.c,line 11.
Breakpoint 3 at 0x8048429:file 6.2.c,line 12.
Breakpoint 4 at 0x8048432:file 6.2.c,line 13.
輸入下面的命令,運行這個程序。
run
運行到第一個斷點顯示的結果如下所示。
Breakpoint 1,main()at 6.2.c:6
6 for(i=1;i<=4;i++)
輸入“step”命令,程序運行一步,結果如下所示。
8 for(j=1;j<=4;j++);
這說明程序已經進入了for循環。這時輸入下面命令,查看i的值。
print i
顯示的結果如下所示。
$2=1
這時再輸入“step”命令,顯示的結果如下所示。
10 if(i>=j)
這時再輸入“step”命令,顯示的結果如下所示。
16 printf("/n");
這表明,在進行j的for循環時,沒有反復執行循環體。這時再輸入“step”命令,
顯示的結果如下所示。
for(i=1;i<=4;i++)
這表明,程序正常的進行了i的for循環。這是第二次執行for循環。
17輸入“step”命令,顯示的結果如下所示。
8 for(j=1;j<=4;j++);
18這表明,程序執行到for循環。這時再次輸入“step”命令,顯示的結果如下所示。
10 if(i>=j)
19輸入“step”命令,顯示的結果如下所示。
16 printf("/n");
20輸入“step”命令,顯示的結果如下所示。
6 for(i=1;i<=4;i++)
21這說明,程序正常的進行了i的for循環,但是沒有執行j的for循環。這一定是j的
for循環語句有問題。這時就不難發現j的for循環后面多了一個分號。
22輸入“q”命令,退出gdb。
1.6.4 gdb幫助的使用
gdb有非常多的命令。輸入“help”命令可以顯示這些命令的幫助信息。本節將講解幫助
信息的使用。
在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 all"for the list of all commands.
Type"help"followed by command name for full documentation.
Type"apropos word"to search for commands related to"word".
Command name abbreviations are allowed if unambiguous.
上面的幫助信息顯示,輸入“help all”會輸出所有幫助信息。
在“help”命令后面加上一個命令名稱,可以顯示這個命令的幫助信息。例如輸入
“help file”,顯示的file命令幫助信息如下所示。
Use FILE as program to be debugged.
It is read for its symbols,for getting the contents of pure memory,
and it is the program executed when you use the`run'command.
If FILE cannot be found as specified,your execution directory path
($PATH)is searched for a command of that name.
No arg means to have no executable file and no symbols.
1.7 gdb常用命令
除了前面講述的gdb命令以外,gdb還有很多種命令。這些命令可以完成程序調試的各
種功能。其他的常用命令含義如下所示。
backtrace:顯示程序中的當前位置和表示如何到達當前位置的棧跟蹤(同義詞:where)。
breakpoint:在程序中設置一個斷點。
cd:改變當前工作目錄。
clear:刪除剛才停止處的斷點。
commands:命中斷點時,列出將要執行的命令。
continue:從斷點開始繼續執行。
delete:刪除一個斷點或監測點,也可與其他命令一起使用。
display:程序停止時顯示變量和表達式。
down:下移棧幀,使得另一個函數成為當前函數。
frame:選擇下一條continue命令的幀。
info:顯示與該程序有關的各種信息。
info break:顯示當前斷點清單,包括到達斷點處的次數等。
info files:顯示被調試文件的詳細信息。
info func:顯示所有的函數名稱。
info local:顯示當函數中的局部變量信息。
info prog:顯示被調試程序的執行狀態。
info var:顯示所有的全局和靜態變量名稱。
jump:在源程序中的另一點開始運行。
kill:異常終止在gdb控制下運行的程序。
list:列出相應于正在執行的程序的源文件內容。
next:執行下一個源程序行,從而執行其整體中的一個函數。
print:顯示變量或表達式的值。
pwd:顯示當前工作目錄。
pype:顯示一個數據結構(如一個結構或C++類)的內容。
quit:退出gdb。
reverse-search:在源文件中反向搜索正規表達式。
run:執行該程序。
search:在源文件中搜索正規表達式。
set variable:給變量賦值。
signal:將一個信號發送到正在運行的進程。
step:執行下一個源程序行,必要時進入下一個函數。
undisplay display:命令的反命令,不要顯示表達式。
until:結束當前循環。
up:上移棧幀,使另一函數成為當前函數。
watch:在程序中設置一個監測點(即數據斷點)。
whatis:顯示變量或函數類型。
1.8編譯程序常見的錯誤與問題
在編寫程序時,無論是邏輯上還是語法上,不可能一次做到完全正確。于是在編譯程序
時,就會發生編譯錯誤。本節將講述程序編譯時常見的錯誤類型與處理方法。
1.8.1邏輯錯誤與語法錯誤
在編程時,出現的錯誤可能有邏輯錯誤和語法錯誤兩種。這兩種錯誤的發生原因和處理
方法是不同的。本節將講述這兩種錯誤的處理方法。
邏輯錯誤指的是程序的設計思路發生了錯誤。這種錯誤在程序中是致命的,程序可能
正常編譯通過,但是結果是錯誤的。當程序正常運行而結果錯誤時,一般都是編程的
思路錯誤。這時,需要重新考慮程序的運算方法與數據處理流程是否正確。
語法錯誤:語法錯誤指的是程序的思路正確,但是在書寫語句時,發生了語句錯誤。
這種錯誤一般是編程時不小心或是對語句的錯誤理解造成的。在發生語句錯誤時,程
序一般不能正常編譯通過。這時會提示錯誤的類型和錯誤的位置,按照這些提示改正
程序的語法錯誤即可完成錯誤的修改。
1.8.2 C程序中的錯誤與異常
C程序中的錯誤,根據嚴重程序的不同,可以分為異常與警誤兩類。在編譯程序時,這
兩種情況對編譯的影響是不同的,對錯誤與異常的處理方式是不同的。
1.什么是異常
異常指的是代碼中輕微的錯誤,這些錯誤一般不會影響程序的正常運行,但是不完全符
合編程的規范。在編譯程序時,會產生一個“警告”,但是程序會繼續編譯。下面的程序會使
程序發生異常,在編譯時產生一個警告錯誤。
在除法中,0作除數。
在開方運算時,對負數開平方。
程序的主函數沒有聲明類型。
程序的主函數沒有返回值。
程序中定義了一個變量,但是沒有使用這個變量。
變量的存儲發生了溢出。
2.什么是錯誤
錯誤指的是程序的語法出現問題,程序編譯不能正常完成,產生一個錯誤信息。這時會
顯示錯誤的類型與位置。根據這些信息可以對程序進行修改。
1.8.3編譯中的警告提示
在編譯程序時,如果發生了不嚴重的異常,會輸出一個錯告錯誤,然后完成程序的編譯。
例如下面的內容是一個程序在編譯時產生的警告。
5.1.c:In function'main':
5.1.c:16:警告:‘main’的返回類型不是‘int’
5.1.c:18:警告:被零除
這些的含義如下所示。
(1)“In function'main':”表示發生的異常在main函數內。
(2)“5.1.c:16:”表示發生異常的文件是5.1.c,位置是第16行。
(3)下面的信息是第16行的異常,表明程序的返回類型不正確。
‘main’的返回類型不是‘int’
(4)下面的警告信息表明程序的第18行有除數為0的錯誤。
5.1.c:18:警告:被零除
1.8.4找不到包含文件的錯誤
程序中的包含文件在系統或工程中一定要存在,否則程序編譯時會發生致命錯誤。例如
下面的語句包含了一個不正確的頭文件。
#include<stdio1.h>
編譯程序時,會發生錯誤,錯誤信息如下所示。
5.1.c:2:20:錯誤:stdio2.h:沒有那個文件或目錄
1.8.5錯誤地使用逗號
程序中逗號的含義是并列幾個內容,形成某種算法或結構。程序中如果錯誤地使用逗號,會
使程序在編譯時發生致命錯誤。例如下面的代碼,是程序中的if語句后面有一個錯誤的逗號。
int max(int i,int j)
{
if(i>j),
{
return(i);
}
else
{
return(j);
}
}
程序編譯時輸出的錯誤信息如下所示。表明max函數中逗號前面的表達式有錯誤,實際
上的錯誤是多一個逗號。
5.1.c:In function‘max’:
5.1.c:4:錯誤:expected expression before‘,’token
5.1.c:In function‘max’:
1.8.6括號不匹配錯誤
程序中的引號、單引號、小括號、中括號、大括號等符號必須成對出現。這方面的錯誤
會使程序發生符號不匹配的錯誤。發生這種錯誤后,編譯程序往往不能理解代碼的含義,也
不能準確顯示錯誤的位置,而是顯示表達式錯誤。例如下面的代碼,在最后一行上了一個花
括號。
int max(int i,int j)
{
if(i>j)
{
return(i);
}
else
{
return(j);
}
編譯程序時,會顯示下面的錯誤信息。
5.1.c:22:錯誤:expected declaration or statement at end of input
1.8.7小括號不匹配錯誤
程序中的小括號一般在一行內成對出現并且相匹配。小括號不匹配時,程序發生致命錯
誤。例如下面的代碼,第一行多了一個右半邊括號。
if(i>j))
{
return(i);
}
else
{
return(j);
}
編程程序時,會發生下面的錯誤。顯示括號前面有錯誤,并且導致下面的else語句也有
錯誤。
5.1.c:4:錯誤:expected statement before‘)’token
5.1.c:8:錯誤:expected expression before‘else’
1.8.8變量類型或結構體聲明錯誤
程序中的變量或結構體的名稱必須正確,否則程序會發生未聲明的錯誤。例如下面的代
碼,用一個不存在的類型來聲明一個變量。
ch a;
程序在運行時,會顯示出這個變量錯誤,并且會顯示有其他的錯誤。
5.1.c:17:錯誤:‘ch’未聲明(在此函數內第一次使用)
5.1.c:17:錯誤:(即使在一個函數內多次出現,每個未聲明的標識符在其
5.1.c:17:錯誤:所在的函數內只報告一次。)
5.1.c:17:錯誤:expected‘;’before‘a’
1.8.9使用不存在的函數的錯誤
如果程序引用了一個不存在的函數,會使用程序發生嚴重的錯誤。例如下面的代碼,引
用了一個不存在的函數add。
k=add(i,j);
程序顯示的錯誤信息如下所示,表明在main函數中的add函數沒有定義。
/tmp/ccYQfDJy.o:In function`main':
5.1.c:(.text+0x61):undefined reference to`add'
collect2:ld返回1
5.8.10大小寫錯誤
C程序對代碼的大小寫是敏感的,不同的大小寫代表不同的內容。例如下面的代碼,將
小寫的“int”錯誤的寫成了“Int”。
Int t;
程序顯示的錯誤信息如下所示,表明“Int”類型不存在或未聲明。發生這個錯誤時,會
輸出多行錯誤提示。
5.1.c:16:錯誤:‘Int’未聲明(在此函數內第一次使用)
5.1.c:16:錯誤:(即使在一個函數內多次出現,每個未聲明的標識符在其
5.1.c:16:錯誤:所在的函數內只報告一次。)
5.1.c:16:錯誤:expected‘;’before‘t’
1.8.11數據類型的錯誤
程序中的某些運算,必須針對相應的數據類型,否則這個運算會發生數據類型錯誤。例
如下面的代碼,錯誤地將兩個整型數進行求余運算。
float a,b;
a=a%b;
程序編譯時,輸出下面的錯誤,表明“%”運算符的操作數無效。
5.1.c:19:錯誤:雙目運算符%操作數無效
1.8.12賦值類型錯誤
任何一個變量,在賦值時必須使用相同的數據類型。例如下面的代碼,錯誤地將一個字
符串賦值給一個字符。
char c;
c="a";
程序編譯時的結果如下所示,表明賦值時數據類型錯誤。
5.1.c:19:警告:賦值時將指針賦給整數,未作類型轉換
1.8.13循環或判斷語句中多加分號
分號在程序中的作用是表示一個語句結束。在程序的語句中用一個單獨的分號表示一個
空語句。但是在循環或判斷結構的后面,一個分號會導致程序的邏輯發生錯誤。關于這些結
構的使用方法,后面的章節將會詳細講到。下面的程序,在for語句的后面,錯誤的添加了一
個分號,導致程序不能正常地進行循環。
#include<stdio.h>
main()
{
int sum,j;
sum=0;
for(j=0;j<11;j++);
{
sum=sum+j;
}
printf(“%d”,sum);
}
這個程序的本意是要求出10以內的整數和。但是在for語句的后面,錯誤地使用了一個
分號。這時,程序不能正確地進行循環,而是把分號作為一個語句進行循環,所以程序輸出
的結果為“11”。
1.9小結
程序的編譯和調試是編程的一個重要環節。本章講解了Linux系統中C編程的編譯器gcc
和編譯器gdb的使用。使用gcc時,需要對編譯進行各種設置,需要理解gcc各項參數的作
用。gdb的學習重點是gdb單步運行程序的理解,通過程序的單步運行發現程序中的問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454837.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454837.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454837.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

A* a=new B ,會不會產生內存泄露了,露了B-A的部分?

A* anew B ,delete a;會不會產生內存泄露了&#xff0c;露了B-A的部分。其中B為A的子類 析構函數在下邊3種情況時被調用&#xff1a;1.對象生命周期結束&#xff0c;被銷毀時&#xff1b;2.delete指向對象的指針時&#xff0c;或delete指向對象的基類類型指針&#xff0c;而其基…

spring 第一天:1015

對象加強的三種方法&#xff1a;1/繼承2/裝飾著模式3/動態調用 2&#xff1a;裝飾著模式&#xff1a;就是就是1-先建一個基類 &#xff0c;如咖啡類 。味道很苦2- 再建一個類配料類 也就是說是所欲配料種類的父類。然后寫多配料子類個子類繼承配料類&#xff0c;。3-子類三個步…

java public 繼承_java繼承問題

代碼&#xff1a;父類&#xff1a;public class Father {public Father() {System.out.println("基類構造函數{");show();new a();System.out.println("}");}public void show() {System.out.println("基類----show");}public class a {public a…

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圓環數(數位DP+惡心細節)

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圓環數 Time Limit: 5 Sec Memory Limit: 64 MBDescription 正如你所知&#xff0c;奶牛們沒有手指以至于不能玩“石頭剪刀布”來任意地決定例如誰先擠奶的順序。她們甚至也不能通過仍硬幣的方式。 所以她們通過"round number&q…

Optimizing Code with GCC

現在的編譯器越來越聰明&#xff0c;功能越來越強&#xff0c;從簡單的函數內聯&#xff0c;到復雜的寄存器分析&#xff0c;一系列代碼革命使程序運行得越來越快。大多數時候&#xff0c;更快比更小重要&#xff0c;因為磁盤空間和內存都變得便宜了。但是在嵌入式系統里&#…

QTP的那些事--操作excel的函數

1: QTP Excel函數 操作EXCEL 數據表格 表單 編輯EXCEL 工作表 2: Dim ExcelApp As Excel.Application 3: Dim excelSheet As Excel.worksheet 4: Dim excelBook As Excel.workbook 5: Dim fso As scrīpting.FileSystemObject 6: 7: ******************…

java-生產者消費者模式

經常會有公司叫我們手撕代碼&#xff0c;比如網易&#xff0c;阿里&#xff0c;那我們是不是該掌握下呢。下面這段代碼來自《現代操作系統》進程與線程P49頁。 public class ProducerConsumer {public ProducerConsumer() { }private static final int N 100;static Producer …

yum查詢已經安裝mysql_通過yum安裝mysql

在linux中安裝數據庫首選MySQL&#xff0c;Mysql數據庫的第一個版本就是發行在Linux系統上&#xff0c;其他選擇還可以有postgreSQL&#xff0c;oracle等在Linux上安裝mysql數據庫&#xff0c;我們可以去其官網上下載mysql數據庫的rpm包&#xff0c;http://dev.mysql.com/downl…

koa2-cookie-session

node.js的path.extname方法使用   由于該方法屬于path模塊&#xff0c;使用前需要引入path模塊&#xff08;var path require(“path”) &#xff09;   接收參數&#xff1a;   p path 路徑 path.extname(index.html)// returns.htmlpath.extname(index.)// returns.pat…

從程序員角度看ELF

從程序員角度看ELF原文:《 ELF:From The Programmers Perspective》作者&#xff1a;Hongjiu Lu <mailto: hjlnynexst.com>NYNEX Science & Technology, Inc. 500 Westchester Avenue White Plains, NY 10604, USA 翻譯&#xff1a;alert7 <mailto: alert721cn.co…

JAVA命令符找不到符號_[轉]Java命令行編譯文件時出現的錯誤,找不到符號或軟件包不存在等...

標簽(空格分隔)&#xff1a; Javajavascript習慣了eclipse的自動編譯&#xff0c;Java命令行編譯、執行文件只會最基礎的部分&#xff0c;就是對單文件的編譯和執行&#xff0c;并且不包含任何外部JAR包。但有時候你還非得用命令行&#xff0c;會碰到一些問題&#xff0c;博主這…

C#中POST數據和接收的幾種方式

POST方式提交數據&#xff0c;一種眾所周知的方式&#xff1a; html頁面中使用form表單提交&#xff0c;接收方式&#xff0c;使用Request.Form[""]或Request.QueryString[""]來獲取。 這里介紹另外一種POST方式和接收方式&#xff0c;就是將整個數據作為加…

java自動注入注解_Spring自動注解標簽@Autowired不能注入xml配置的bean嗎?

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓配置service的xmlservice代碼public class LoginServiceImpl extends BaseDaoServiceImpl implements LoginService {Overridepublic Map queryByUserName(String userName){IDao iDao super.getAppDao();return (Map)iDao.queryF…

一卡通vip充值消費線上oracle庫服務器故障排查過程

上圖是oracle體系總架構圖今天突然公司所有終端pos機不能刷卡消費&#xff0c;財務室不能充值&#xff0c;一下很多電話打過來了&#xff0c;第一反應肯定數據庫出問題了&#xff0c;登陸到數據庫服務器&#xff0c;果然sqlplus連進去后就不斷提示要求輸入用戶名&#xff0c;彈…

最詳細的Linux下C編程

gcc 目 錄 1. gcc 1. makefile寫法 2. gcc_egcs使用 3. gdb使用 4. gcc常用選項對代碼的影響 1. 一般情況 2. -O 編譯選項 3. -O2 編譯選項 4. -fomit-frame-pointer 編譯選項 5. -fomit-frame-pointer…

sqlserver 存儲過程 增加

CREATE PROCEDURE [dbo].[InsertMessage]( strTable varchar(50), --表名 strValues nvarchar(1000), --要插入的數據&#xff08;用英文逗號分隔&#xff09;,如果是字符串類型&#xff0c;需加單引號 only_field varchar(20)NULL, --唯一性字段(列名) only_valu…

java開發計算機考試服務器_2011計算機二級JAVA編程:取得服務器當前的各種具體時間...

取得服務器當前的各種具體時間/*** 取得服務器當前的各種具體時間* 回車&#xff1a;日期時間*/import java.util.*;public class GetNowDate{Calendar calendar null;public GetNowDate(){calendar Calendar.getInstance();calendar.setTime(new Date());}public int getYea…

(cljs/run-at (JSVM. :all) 細說函數)

前言 作為一門函數式編程語言&#xff0c;深入了解函數的定義和使用自然是十分重要的事情&#xff0c;下面我們一起來學習吧&#xff01; 3種基礎定義方法 defn 定義語法 (defn name [params*]exprs*) 示例 (defn tap [ns x](println ns x)x) fn 定義語法 (fn name? [params*]…

Request的getHeader()和getParameter()的區別

區別是&#xff1a;一個是獲得HTTP頭信息,一個是獲得表單參數值。轉載于:https://www.cnblogs.com/pxffly/p/7460514.html

gcc中的內嵌匯編語言(Intel i386平臺)

gcc中的內嵌匯編語言&#xff08;Inteli386平臺&#xff09; 一.聲明 雖然Linux的核心代碼大部分是用C語言編寫的&#xff0c;但是不可避免的其中還是有一部分是用匯編語言寫成的。有些匯編語言代碼是直接寫在匯編源程序中的&#xff0c;特別是Linux的啟動代碼部分&#xff1b…