ubuntu開發環境安裝gcc/make/gdb
更新安裝源
apt-get update
安裝gcc和c++的開發庫,安裝的時候自動包含其它依賴庫
apt-get install g++
調試工具
apt-get install gdb
apt-get install make
遠程連接工具
apt-get install openssh-server
編輯工具
apt-get install vim
使用gcc/g++編譯程序詳解
gcc(g++)
預處理,頭文件include、導入庫、宏
編譯,檢測語法等錯誤,將.c/.cpp等文件轉化爲.o或.obj
彙編
鏈接,.o/.so或.obj/.dll等文件轉化爲exe
gcc [ 選項 ] <文件名>
gcc常用選項
選項 | 含義 |
---|---|
-o file | 將經過gcc處理過的結果存爲文件file,這個結果文件可能是預 處理文件、彙編文件、目標文件或者最終的可執行文件。假設 被處理的源文件爲source.suffix,如果這個選項被省略了,那 麼生成的可執行文件默認名稱爲a.out;目標文件默認名爲 source.o;彙編文件默認名爲source.s;生成的預處理文件則 發送到標準輸出設備。 |
-c | 僅對源文件進行編譯,不鏈接生成可執行文件。在對源文件 進行查錯時,或只需產生目標文件時可以使用該選項。 |
-g[gdb] | 在可執行文件中加入調試信息,方便進行程序的調試。如果 使用中括號中的選項,表示加入gdb擴展的調試信息,方便 使用gdb來進行調試 |
-O[0、1、2、3] | 對生成的代碼使用優化,中括號中的部分爲優化級別,缺省 的情況爲2級優化,0爲不進行優化。注意,採用更高級的優 化並不一定得到效率更高的代碼。 |
-Dname[=definition] | 將名爲name的宏定義爲definition,如果中括號中的部分缺 省,則宏被定義爲1 |
-Idir | 在編譯源程序時增加一個搜索頭文件的額外目錄——dir,即include增加一 個搜索的額外目錄。 |
-Ldir | 在編譯源文件時增加一個搜索庫文件的額外目錄——dir |
-llibrary | 在編譯鏈接文件時增加一個額外的庫,庫名爲liblibrary.so |
-w | 禁止所有警告 |
-Wwarning | 允許產生warning類型的警告,warning可以是:main、unused等很多取值, 最常用是-Wall,表示產生所有警告。如果warning取值爲error,其含義是將 所有警告作爲錯誤(error),即出現警告就停止編譯。 |
gcc文件擴展名規範
擴展名 | 類型 | 可進行的操作方式 |
---|---|---|
.c | c語言源程序 | 預處理、編譯、彙編、 鏈接 |
.C,.cc,.cp,.cpp,. c++,.cxx | c++語言源程序 | 預處理、編譯、彙編、 鏈接 |
.i | 預處理後的c語言源程序 | 編譯、彙編、鏈接 |
.ii | 預處理後的c++語言源程序 | 編譯、彙編、鏈接 |
.s | 預處理後的彙編程序 | 彙編、鏈接 |
.S | 未預處理的彙編程序 | 預處理、彙編、鏈接 |
.h | 頭文件 | 不進行任何操作 |
.o | 目標文件 | 鏈接 |
g++ 分步編譯
gdb調試工具使用和coredump
#1 預處理 (Preprocessing)
g++ -E test.cpp >test.ii
#2 編譯 (Compilation)
g++ -S test.ii
#3 彙編 (Assembly)
g++ -c test.s
#4 鏈接 (Linking)
g++ test.o -o test
#5 運行
./test
gdb調試器
gdb概述
Linux下的gdb調試器,是一款GNU組織開發併發布的UNIX/Linux下的程 序調試工具。它沒有圖形化的友好界面,但功能強大。
gdb指令參數
命令格式 | 作用 |
---|---|
list <行號>|<函數名> | 查看指定位置的程序源代碼 |
break 行號|函數名<條件表達式> | 設置斷點 |
info break | 顯示斷點信息 |
run | 運行程序 |
print 表達式|變量 | 查看程序運行時對應表達式和變量的值 |
next | 單步恢復程序運行,但不進入函數調用 |
step | 單步恢復程序運行,且進入函數調 用 |
continue | 繼續執行函數,直到函數結束或遇到新斷點 |
coredump
vim test.cpp
#include<iostream>
using namespace std;
#define NAME "NAME=cpp"
int main(int argc,char *argv[])
{
cout<<"test g++ makefile"<<endl;
int *c = 0;
*c = 1000;//error
cout<<NAME<<endl;
return 0;
}
按esc,輸入wq保存退出
生成帶調試信息的可執行文件test,文件會大一些
g++ test.cpp -o test -g
這是在調試狀態下發現的錯誤
gdb test
run
Program received signal SIGSEGV, Segmentation fault.
0x000000000040087d in main (argc=1, argv=0x7fffffffe1c8) at test.cpp:8
8 *c = 1000;//error
隨着項目代碼複雜度加大,有時候我們想在運行程序之後查看程序是在哪一行dump掉的。可以查看一下coredump,默認是關閉的。
(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ulimit -c
0
啓動一下
(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ulimit -c unlimited
再次查看coredump狀態
(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ulimit -c
unlimited
執行test
(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ./test
test g++ makefile
段錯誤 (核心已轉儲)
報錯信息存儲在core文件
(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ls -a | grep core
core
gdb調試core文件,輸出錯誤信息,這樣我們就能知道現場環境的程序代碼是在哪裏dump掉的。
(caffe2_env) zhoujianwen@zhoujianwen-System:~$ gdb test core
warning: exec file is newer than core file.
[New LWP 8480]
Core was generated by `./test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000000000040087d in main (argc=1, argv=0x7ffdbf9b7a58) at test.cpp:8
8 *c = 1000;//error
後面的調試當中,除了coredump會使用到,應用程序以調試狀態運行過程中出現問題,也能查看coredump發現問題,特別是系統在做熔斷的時候,我們生成兩次coredump作比較差異發現問題。
gcc編譯動態鏈接庫
-fPIC 編譯選項 -fPIC If supported for the target machine, emit position- independent code, suitable for dynamic linking,even if branches need large displacements. 產生位置無關代碼(PIC),一般創建共享庫時用到。 在x86上,PIC的代碼的符號引用都是通過ebx進行操作的。
-shared
示例
g++ -shared -fPIC mylib.cpp -o libmylib.so
g++ test.cpp -lmylib -L/root/cpp
#!/bin/bash
LD_LIBRARY_PATH=./;
export LD_LIBRARY_PATH
./testlib
Makefile中 -I -L -l區別
我們用gcc編譯程序時,可能會用到“-I”(大寫i),“-L”(大寫l),“-l”(小寫l)等參數,下面做個記錄:
例:
gcc -o hello hello.c -I /home/hello/include -L /home/hello/lib -lworld
上面這句表示在編譯hello.c時:
-I /home/hello/include表示將/home/hello/include目錄作爲第一個尋找頭文件的目錄,尋找的順序是:/home/hello/include–>/usr/include–>/usr/local/include
-L /home/hello/lib表示將/home/hello/lib目錄作爲第一個尋找庫文件的目錄,尋找的順序是:/home/hello/lib–>/lib–>/usr/lib–>/usr/local/lib
-lworld表示在上面的lib的路徑中尋找libworld.so動態庫文件(如果gcc編譯選項中加入了“-static”表示尋找libworld.a靜態庫文件)
創建目錄
mkdir code
cd code
pwd
mkdir src
mkdir lib
mkdir bin
mkdir include
mkdir doc
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ ls
bin doc include lib src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ cd src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ mkdir testlib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ cd testlib
創建文件,一個頭文件對應一個庫文件
vim testlib.cpp
#include<iostream>
#include "test_lib.h"
using namespace std;
int main(int argc,char *argv[])
{
TestLib t;
return 0;
}
vim test_lib.h
#ifndef TEST_LIB_H
#define TEST_LIB_H
class TestLib
{
public:
TestLib();
};
#endif
vim test_lib.cpp
#include "test_lib.h"
#include <iostream>
using namespace std;
TestLib::TestLib()
{
std::cout<<"TestLib Create"<<std::endl;
}
生成xx.so動態鏈接庫
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ (caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
test_lib.cpp testlib.cpp test_lib.h
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ g++ -fPIC -shared test_lib.cpp -o libtestlib.so
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
libtestlib.so test_lib.cpp testlib.cpp test_lib.h
編譯testlib.cpp和-ltestlib,-ltestlib其實就是libtestlib.so文件,這是格式規範,指定共享庫路徑是在./
當前路徑下查找,生成可執行文件testlib
注意:
-l(小寫l),-L(大寫l)
-ltestlib表示在上面的lib的路徑中尋找libtestlib
.so動態庫文件(如果gcc編譯選項中加入了“-static”表示尋找libtestlib.a靜態庫文件)
-L ./表示將~/code/src/testlib
目錄作爲第一個尋找庫文件的目錄,尋找的順序是:~/code/src/testlib
–>/lib–>/usr/lib–>/usr/local/lib
首先庫文件名(庫文件名不等於庫名)的命名規則爲lib+<庫名>+.so,如果有一個testlib庫, 那麼相應的庫文件爲 libtestlib.so 爲了在執行編譯命令的時候鏈接指定的庫,我們需要用到-L(大寫l)和-l(小寫l)命令.
如果在路徑 ~/code/src/testlib
下面有一個庫文件叫做 libtestlib.so, 我想在編譯的時候鏈接它, 只需加上−ltestlib −L ~/code/src/testlib或−ltestlib −L ./命令即可。對於那些在/lib, /usr/lib 和 /usr/local/lib 路徑下的庫, 我們可以使用更加簡單的命令 −l(小寫l) 而無需添加庫文件目錄路徑
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ g++ testlib.cpp -ltestlib -L./ -o testlib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls ./
libtestlib.so testlib test_lib.cpp testlib.cpp test_lib.h
當你執行./testlib就會提示找不到動態鏈接庫,主要是環境變量LD_LIBRARY_PATH沒有指定庫加載的正確路徑
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ./testlib
./testlib: error while loading shared libraries: libtestlib.so: cannot open shared object file: No such file or directory
查詢一下當前環境變量LD_LIBRARY_PATH的路徑是/usr/local/cuda-10.0/lib64,顯然不是我們剛纔的測試路徑~/code/src/testlib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ echo $LD_LIBRARY_PATH
/usr/local/cuda-10.0/lib64
解決共享庫沒法找到的問題,方法有幾種,最簡單是在剛纔目錄的當前路徑下創建一個run.sh,但是export LD_LIBRARY_PATH
它只對當次run.sh執行操作有效。
vim run.sh
#!/bin/bash
LD_LIBRARY_PATH=./;
export LD_LIBRARY_PATH
./test
按下esc,wq保存退出
修改run.sh執行權限
chmod +x run.sh
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ./run.sh
TestLib Create
makefile
GNU make用來構建和管理自己的工程
Makefile 文件描述了整個工程的編譯、 連接等規則
makefile介紹和第一個項目示例
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ mkdir testmake
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ cd testmake/
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$
vim testmake.cpp
#include <iostream>
using namespace std;
int main(int argc,char *argv[])
{
std::cout<<"test makefile"<<std::endl;
return 0;
}
按esc,保存退出wq
vim makefile
編輯如下內容
testmake:testmake.cpp
g++ testmake.cpp -o testmake
./testmake
按esc,保存退出wq,在保存之前確保tab格式輸入正確。
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$ ls
makefile testmake testmake.cpp
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$ make
g++ testmake.cpp -o testmake
./testmake
test makefile
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$ ./testmake
test makefile
通過執行語句,把依賴的文件生成目標。爲什麼不直接使用g++ testmake.cpp -o testmake ./testmake
,而是另外新建一個testmake來管理呢?因爲當項目要編譯的文件數量比較多的時候,使用make可以更有效地管理和構建項目的工程代碼。下面只是編譯一個文件,如果有多個,或者是分開部署的呢,而且還要知道哪一部分編譯出錯了,這樣的管理效率就不一樣了。
Makefile中常見預定義變量
makefile變量的使用
make和makefile
makefile文件主要包含了5部分內容:
- 顯式規則。說明了如何生成一個或多個目標文件。由makefile文件的創作者指出,包括要生成的文件、文件的依賴文件、生成的命令。
- 隱式規則。由於make有自動推導的功能,所以隱式的規則可以比較粗糙地簡略書寫makefile文件,這是由make所支持的。
- 變量定義。在makefile文件中要定義一系列的變量,變量一般都是字符串, 這與C語言中的宏有些類似。當makefile文件執行時,其中的變量都會擴展到相應的引用位置上。
- 文件指示。其包括3個部分,一個是在一個makefile文件中引用另一個 makefile文件;另一個是指根據某些情況指定makefile文件中的有效部分; 還有就是定義一個多行的命令。
- 註釋。makefile文件中只有行註釋,其註釋用“#”字符。如果要在 makefile文件中使用“#”字符,可以用反斜框進行轉義,如:“#”。
makefile變量使用和規則分析
在上面的例子中,先讓我們看看edit的規則:
edit : main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
我們可以看到[.o]文件的字符串被重複了兩次,如果我們的工程需要加入一個新的[.o]文件,那麼我們需要在兩個地方加(應該是三個地方,還有一個地方在clean 中)。當然,我們的makefile並不複雜,所以在兩個地方加也不累,但如果makefile變得複雜,那麼我們就有可能會忘掉一個需要加入的地方,而導致編譯失敗。所以,爲了makefile的易維護,在 makefile中我們可以使用變量。makefile的變量也就是一個字符串,理解成C語言中的宏可能會更好。
我們在makefile 一開始就這樣定義:
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
於是,我們就可以很方便地在我們的makefile中以“$(objects)” 的方式來使用這個變量了,於是我們的改良版makefile就變成下面這個樣子:
edit : $(objects)
cc -o edit $(objects)
引入文件
include
解決方案配置_多項目依賴_配置管理器
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ ls
bin doc include lib src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ cd src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ ls
testlib testmake
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ cd testlib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
libtestlib.so run.sh testlib test_lib.cpp testlib.cpp test_lib.h test_lib.h.gch test_lib.o testlib.o
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ vim makefile
編輯makefile文件
testlib.so:test_lib.cpp test_lib.h
g++ $+ -o $@ -shared -fPIC
clean:
rm -rf *.o *.so
按esc退出編輯模式,輸入wq保存退出。
make
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ make
g++ test_lib.cpp test_lib.h -o testlib.so -shared -fPIC
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
libtestlib.so run.sh test_lib.cpp test_lib.h test_lib.o testlib.so
make clean
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ make clean
rm -rf *.o *.so
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
makefile run.sh testlib test_lib.cpp testlib.cpp test_lib.h test_lib.h.gch
修改makefile文件內容
在rm -rf *.o *.so
前添加@可隱藏rm -rf *.o *.so
顯示語句的執行
testlib.so:test_lib.cpp test_lib.h
g++ $+ -o $@ -shared -fPIC
clean:
@echo "begin clean..."
@rm -rf *.o *.so
@echo "end clean."
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ (caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ make clean
begin clean...
end clean.
添加install
testlib.so:test_lib.cpp test_lib.h
g++ $+ -o $@ -shared -fPIC
clean:
@echo "begin clean..."
@rm -rf *.o *.so
@echo "end clean."
install:
cp *.so /usr/lib
按esc退出編輯模式,輸入wq保存退出。
make生成*.so文件,make install把*.so文件拷貝到/usr/lib
make
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ sudo make install
[sudo] zhoujianwen 的密碼:
g++ test_lib.cpp test_lib.h -o testlib.so -shared -fPIC
cp *.so /usr/lib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls /usr/lib | grep testlib
testlib.so
構建依賴關係
在install後面添加testlib.so
,這樣在執行make install之前可以確保testlib.so文件已經生成,再把*.so拷貝到/usr/lib目錄下。
testlib.so:test_lib.cpp test_lib.h
g++ $+ -o $@ -shared -fPIC
clean:
@echo "begin clean..."
@rm -rf *.o *.so
@echo "end clean."
install:testlib.so
cp *.so /usr/lib
這樣執行一條命令語句就可以了
sudo make install
構建多文件項目
目錄結構
~/code/src/testlib/
test_lib.h
test_lib.cpp
~/code/src/testlib/testmutimake
main.cpp include test_lib.h
person.h
person.cpp include test_lib.h
cd ..
ls
mkdir testmutimake
cd testmutimake
vim main.cpp
main.cpp
#include <iostream>
#include "person.h"
#include "test_lib.h"
using namespace std;
int main(int argc,char *argv[])
{
TestLib t;
Person p;
return 0;
}
person.h
```cpp
#ifndef PERSON_H
#define PERSON_H
class Person
{
pbulic:
Person();
}
#endif
person.cpp
#include "person.h"
#include "test_lib.h"
#include <iostream>
using namespace std;
Person::Person(){
TestLib t;
std::cout<<"Person Create"<<std::endl;
};
vim makefile
自動推導和手動配置,雙結合的做法。
TARGET=main
OBJS = main.o person.o
CPPFLAGES=-I../testlib
LIBS = -ltestlib
$(TARGET):$(OBJS)
g++ $+ -o $@ $(LIBS) #這一步是鏈接
main.o:main.cpp
g++ -c $+ $(CPPFLAGS)
clean:
rm -rf $(OBJS) $(TARGET)
g++ $+ -o $@ -I../testlib -ltestlib #這一步是鏈接
本來-ltestlib後面是要添加-L …/testlib,不用添加是因爲之前已經將testlib.so文件拷貝到/usr/lib目錄下,這樣就會默認搜索/usr/lib目錄
編譯彙編怎麼做呢?是使用gcc -c指令來做自動推導的,自動推導出來就出現一個問題,就是頭文件的路徑沒有添加。第一步我們不要讓它自動推導,而是自己編寫。
main.o:main.cpp
g++ -c $+ -I../testlib