1. 程式人生 > >gcc編譯C++程式

gcc編譯C++程式

動態連結庫的編寫和使用

1、動態庫*.so的編譯

這裡我們用到4個檔案,它們分別為:SoDemoTest.h、one.cpp、two.cpp、three.cpp。它們的內容如下: 

SoDemoTest.h

#ifndef __SO_DEMO_TEST_HEADER__   
#define __SO_DEMO_TEST_HEADER__   
#include <iostream>   
using namespace std;  
void one();  
void two();  
void three();  
#endif  

one.cpp

#include "SoDemoTest.h"   
void one()  
{  
    cout << "call one() function." << endl;  
}  

two.cpp

#include "SoDemoTest.h"   
void two()  
{  
    cout << "call two() function." << endl;  
}  

three.cpp

#include "SoDemoTest.h"   
void three()  
{  
    cout << "call three() function." << endl;  
}  

將這幾個檔案編譯成動態庫libtest.so。編譯命令如下:

$ g++ one.cpp two.cpp three.cpp -fPIC -shared -o libtest.so

2、動態庫的連線

在上面的部分,我們已經生成了一個libtest.so的動態連結庫,現在我們用一個程式來呼叫這個動態連結庫。檔名為:main.cpp

main.cpp

#include "SoDemoTest.h"   
int main()  
{  
    one();  
    two();  
    three();  
    return 0;  
}  

將main.cpp與libtest.so連結成一個可執行檔案main。命令如下:

$ g++ main.cpp -L. -ltest -o main

測試可執行程式main是否已經連結的動態庫libtest.so,如果列出了libtest.so,那麼就說明正常連結了。可以執行以下命令:

$ ldd main

執行main可以看看main是否呼叫了動態連結庫中的函式。

3、編譯引數

-shared 該選項指定生成動態連線庫(讓聯結器生成T型別的匯出符號表,有時候也生成弱連線W型別的匯出符號),不用該標誌外部程式無法連線。相當於一個可執行檔案

-fPIC:表示編譯為位置獨立的程式碼,不用此選項的話編譯後的程式碼是位置相關的所以動態載入時是通過程式碼拷貝的方式來滿足不同程序的需要,而不能達到真正程式碼段共享的目的。

-L.:表示要連線的庫在當前目錄中

-ltest:編譯器查詢動態連線庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱

LD_LIBRARY_PATH:這個環境變數指示動態聯結器可以裝載動態庫的路徑。

error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory 出現這個錯誤時候,就要設定

export LD_LIBRARY_PATH=/home/liujiayu/G (等號兩邊不要加空格)

4、注意的問題

呼叫動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的標頭檔案所在目錄 通過 “-I” include進來了,庫所在檔案通過 “-L”引數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定連結的so檔案,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf檔案來指定動態庫的目錄。通常這樣做就可以解決庫無法連結的問題了。


動態呼叫SO檔案

1.製作so檔案:libadd_c.so

add.c:

  1. int add(int a, int b)  
  2. {  
  3.     return a + b;  
  4. }  


編譯:

gcc -shared -fpic -lm -ldl -o libadd_c.so add.c

2.編寫測試函式

test.cpp

  1. #include <stdio.h>
  2. #include <dlfcn.h>
  3. #include <stdlib.h>
  4. #include <iostream>
  5. usingnamespace std;  
  6. int main()  
  7. {  
  8.     int a = 0;  
  9.     void *handle = dlopen("./libadd_c.so", RTLD_LAZY);  
  10.     if(!handle)  
  11.     {  
  12.         printf("open lib error\n");  
  13.         cout<<dlerror()<<endl;  
  14.         return -1;  
  15.     }  
  16.     typedefint (*add_t)(int a, int b);  
  17.     add_t add = (add_t) dlsym(handle, "add");  
  18.     if(!add)  
  19.     {  
  20.         cout<<dlerror()<<endl;  
  21.         dlclose(handle);  
  22.         return -1;  
  23.     }  
  24.     a = add(3, 4);  
  25.     printf("a = %d\n",a);  
  26.     dlclose(handle);  
  27.     return 0;  
  28. }  

編譯:

g++ test.cpp -ldl -o test

3.執行

./test

參考:

介紹一下上面用到的介面函式

1) dlopen

函式原型:void *dlopen(const char *libname,int flag);

功能描述:dlopen必須在dlerror,dlsym和dlclose之前呼叫,表示要將庫裝載到記憶體,準備使用。如果要裝載的庫依賴於其它庫,必須首先裝載依賴庫。如果dlopen操作失敗,返回NULL值;如果庫已經被裝載過,則dlopen會返回同樣的控制代碼。

引數中的libname一般是庫的全路徑,這樣dlopen會直接裝載該檔案;如果只是指定了庫名稱,在dlopen會按照下面的機制去搜尋:

a.根據環境變數LD_LIBRARY_PATH查詢

b.根據/etc/ld.so.cache查詢

c.查詢依次在/lib和/usr/lib目錄查詢。

flag引數表示處理未定義函式的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暫時不去處理未定義函式,先把庫裝載到記憶體,等用到沒定義的函式再說;RTLD_NOW表示馬上檢查是否存在未定義的函式,若存在,則dlopen以失敗告終。

2) dlerror

函式原型:char *dlerror(void);

功能描述:dlerror可以獲得最近一次dlopen,dlsym或dlclose操作的錯誤資訊,返回NULL表示無錯誤。dlerror在返回錯誤資訊的同時,也會清除錯誤資訊。

3) dlsym

函式原型:void *dlsym(void *handle,const char *symbol);

功能描述:在dlopen之後,庫被裝載到記憶體。dlsym可以獲得指定函式(symbol)在記憶體中的位置(指標)。如果找不到指定函式,則dlsym會返回NULL值。但判斷函式是否存在最好的方法是使用dlerror函式,

4) dlclose

函式原型:int dlclose(void *);

功能描述:將已經裝載的庫控制代碼減一,如果控制代碼減至零,則該庫會被解除安裝。如果存在解構函式,則在dlclose之後,解構函式會被呼叫。

好了,現在來編譯打包,命令如下:

$ g++ -shared -fPIC -o libhello.so hello.cpp
$ g++ main.cpp -ldl

在上面dlopen函式中,看到我們傳的第一個引數並沒有指定路徑,只給出了庫的名稱。那是因為已經在環境變數LD_LIBRARY_PATH中指定了 ./ 目錄,如下圖所示。

如果你想放在其他目錄,修改該環境變數即可。


==========================================================================================

-l引數和-L引數:
-l引數就是用來指定程式要連結的庫,-l引數緊接著就是庫名,那麼庫名跟真正的庫檔名有什麼關係呢?就拿數學庫來說,他的庫名是m,他的庫檔名是libm.so,很容易看出,把庫檔名的頭lib和尾.so去掉就是庫名了。
好了現在我們知道怎麼得到庫名,當我們自已要用到一個第三方提供的庫名字libtest.so,那麼我們只要把libtest.so拷貝到/usr/lib裡,編譯時加上-ltest引數,我們就能用上libtest.so庫了(當然要用libtest.so庫裡的函式,我們還需要與 libtest.so配套的標頭檔案)。
放在/lib和/usr/lib和/usr/local/lib裡的庫直接用-l引數就能連結了,但如果庫檔案沒放在這三個目錄裡,而是放在其他目錄裡,這時我們只用-l引數的話,連結還是會出錯,出錯資訊大概是:“/usr/bin/ld: cannot find -lxxx”,也就是連結程式ld在那3個目錄裡找不到libxxx.so,這時另外一個引數-L就派上用場了,比如常用的X11的庫,它在/usr /X11R6/lib目錄下,我們編譯時就要用-L/usr/X11R6/lib -lX11引數,-L引數跟著的是庫檔案所在的目錄名。再比如我們把libtest.so放在/aaa/bbb/ccc目錄下,那連結引數就是-L /aaa/bbb/ccc -ltest。
另外,大部分libxxxx.so只是一個連結,以RH9為例,比如libm.so它連結到/lib/libm.so.x,/lib/libm.so.6又連結到/lib/libm-2.3.2.so,如果沒有這樣的連結,還是會出錯,因為ld只會找libxxxx.so,所以如果你要用到xxxx庫,而只有libxxxx.so.x或者libxxxx-x.x.x.so,做一個連結就可以了ln -s libxxxx-x.x.x.so libxxxx.so。
手工來寫連結引數總是很麻煩的,還好很多庫開發包提供了生成連結引數的程式,名字一般叫xxxx-config,一般放在/usr/bin目錄下,比如gtk1.2的連結引數生成程式是gtk-config,執行gtk-config --libs就能得到以下輸出"-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic-lgmodule -lglib -ldl -lXi -lXext -lX11 -lm",這就是編譯一個gtk1.2程式所需的gtk連結引數,xxx-config除了--libs引數外還有一個引數是--cflags用來生成標頭檔案包含目錄的,也就是-I引數,在下面我們將會講到。你可以試試執行gtk-config --libs --cflags,看看輸出結果。
現在的問題就是怎樣用這些輸出結果了,最笨的方法就是複製貼上或者照抄,聰明的辦法是在編譯命令列里加入這個`xxxx-config --libs --cflags`,比如編譯一個gtk程式:gcc gtktest.c `gtk-config --libs --cflags`這樣就差不多了。注意`不是單引號,而是1鍵左邊那個鍵。


-include和-I引數:

-include用來包含標頭檔案,但一般情況下包含標頭檔案都在原始碼裡用#include xxxxxx實現,-include引數很少用。-I引數是用來指定標頭檔案目錄,/usr/include目錄一般是不用指定的,gcc知道去那裡找,但是如果標頭檔案不在/usr/include裡我們就要用-I引數指定了,比如標頭檔案放在/myinclude目錄裡,那編譯命令列就要加上-I/myinclude引數了,如果不加你會得到一個"xxxx.h: No such file or directory"的錯誤。-I引數可以用相對路徑,比如標頭檔案在當前目錄,可以用-I.來指定。

========================================================================================= gcc & g++現在是gnu中最主要和最流行的c & c++編譯器 。
g++是c++的命令,以.cpp為主,對於c語言字尾名一般為.c。這時候命令換做gcc即可。其實是無關緊要的。
其實編譯器是根據gcc還是g++來確定是按照C標準還是C++標準編譯連結。


下面以Test.cpp為例:

命令: g++ Test.cpp
功能:生成預設為a.exe的檔案,這個過程包含了編譯和連結。
再說下-o命令,-o命令表示輸出的意思,gcc/g++命令是非常靈活的,你不指定輸出的檔名的時候預設生成的是.exe檔案。
你要輸出Test.exe的話可以用:g++ -o Test.exe Test.cpp。-o命令是輸出的意思,這樣就輸出了Test.exe。

gcc/g++在執行編譯工作的時候,總共需要以下幾步:

  1. /************************* 
  2.         Test.cpp 
  3.     *************************/
  4.     #include <IOSTREAM>
  5.     staticint t = 1;  
  6.     #define T 9
  7.     usingnamespace std;  
  8.     typedefint Status;  
  9.     int main()  
  10.     {  
  11.         Status i = 1;  
  12.         cout << T * i << endl; //Test Cout
  13.         return 0;  
  14.     }  

1.預處理,生成.i的檔案[前處理器cpp]

命令:g++ -E Test.cpp > Test.i 

功能:輸出預處理後的檔案,linux下以.i為字尾名。只啟用預處理,這個不生成檔案,你需要把它重定向到一個輸出檔案裡 。這一步主要做了這些事情:巨集的替換,還有註釋的消除,還有找到相關的庫檔案。用編輯器開啟Test.i會發現有很多很多程式碼,你只需要看最後部分就會發現,預處理做了巨集的替換,還有註釋的消除,可以理解為無關程式碼的清除。下面是Test.i檔案的最後部分,可以看見巨集的替換和註釋的消除。

  1. # 5 "Test.cpp" 2
  2. staticint t = 1;  
  3. usingnamespace std;  
  4. typedefint Status;  
  5. int main()  
  6. {  
  7.  Status i = 1;  
  8.  cout << 9 * i << endl;  
  9.  return 0;  
  10. }  


2.將預處理後的檔案不轉換成組合語言,生成檔案.s[編譯器egcs]

命令:g++ -S Test.cpp
功能:會生成Test.s檔案,.s檔案表示是彙編檔案,用編輯器開啟就都是彙編指令。

3.有彙編變為目的碼(機器程式碼)生成.o的檔案[彙編器as]

命令:g++ -c Test.cpp 
功能:.o是GCC生成的目標檔案,除非你是做編譯器和聯結器除錯開發的,否則開啟這種.o沒有任何意義。二進位制機器碼一般人也讀不了。

4.連線目的碼,生成可執行程式[連結器ld]

命令:g++ Test.o -L F:\vs2008\VC\include\iostream
功能:將.o檔案與所需的庫檔案連結整合形成.exe檔案,這就是可執行檔案。-L 表示連結,這裡我後面寫的是絕對路徑,相對各人電腦不同


在上面各個步驟中你可以用-o命令輸出你自己想要的各種名字。比如最後一個命令,用下面的輸出Test.exe
你可以g++ Test.o -o Test.exe -L F:\vs2008\VC\include\iostream


====================================================================================================

gcc and g++分別是gnu的c & c++編譯器 gcc/g++在執行編譯工作的時候,總共需要4步

1.預處理,生成.i的檔案[前處理器cpp]

2.將預處理後的檔案轉換成組合語言,生成檔案.s[編譯器egcs]

3.由彙編變為目的碼(機器程式碼)生成.o的檔案[彙編器as]

4.連線目的碼,生成可執行程式[連結器ld]

[引數詳解]

-x language filename

 設定檔案所使用的語言,使字尾名無效,對以後的多個有效.也就是根據約定C語言的字尾名稱是.c的,而C++的字尾名是.C或者.cpp,如果你很個性,決定你的C程式碼檔案的字尾名是.pig 哈哈,那你就要用這個引數,這個引數對他後面的檔名都起作用,除非到了下一個引數的使用。

  可以使用的引數嗎有下面的這些

  `c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `assembler-with-cpp'.

  看到英文,應該可以理解的。

相關推薦

Macbook中使用Vim和GCC編譯C程式

Macbook中使用Vim和GCC編譯C程式 MAC中使用Vim和GCC編譯C程式 Vim及GCC指令 MAC中使用Vim和GCC編譯C程式 開啟終端(Terminal); 輸入以下命令進入Vim編輯器,同時

gcc編譯c++程式(轉載)

單個原始檔生成可執行程式 下面是一個儲存在檔案 helloworld.cpp 中一個簡單的 C++ 程式的程式碼:  /* helloworld.cpp */ #include <iostream> int main(int argc,char *argv[]) {

linux下用gcc編譯c程式時遇到的問題: error: stdio.h: 沒有那個檔案或目錄

原因是沒有安裝libc6-dev的軟體包。命令列下輸入apt-get install build-essential即可。這個build-essential是幹什麼的呢?原來build-essential是一個列表,包含了編譯debian包必需的大部分元件。安裝完之後,順利解

linux下GCC編譯C程式(一)

GCC的"-lm"選項,它告訴GCC檢視系統提供的數學庫(libm)。因為Linux和UNIX的系統函式庫通常以"lib"為字首,所以我們假設它存在。真正的函式庫位置隨系統的不同而不同,但它一般會位於目錄/lib或/usr/lib中,在這些目錄中還有數以百計的其他必需的系統函式庫。 4. 共享函式庫與靜態函

gcc編譯C++程式

動態連結庫的編寫和使用 1、動態庫*.so的編譯 這裡我們用到4個檔案,它們分別為:SoDemoTest.h、one.cpp、two.cpp、three.cpp。它們的內容如下:  SoDemoTest.h #ifndef __SO_DEMO_TEST_HEADER

64位系統下gcc按照32位編譯c程式

有時候我們需要測試c程式碼在32位環境下的執行結果,這時候就需要gcc按照32位來編譯c了。 1、 安裝 sudo apt-get install lib32readline-gplv2-dev 2、編譯。加 -m32 引數 gcc -m32 hell

gcc編譯c入門

目錄 .net def 函數 庫函數 detail 安排 lan tar 1、在當前目錄下新建c文件 $:vim hello.c 2、按i進入編輯模式。按esc退出編輯模式,輸入源代碼 #include <stdio.h> int main(void) { pr

GCC編譯C源代碼的四個步驟

二進制文件 存在 鏈接 四個步驟 pre 參數 -o 包含 利用 GCC編譯C源代碼有四個步驟:預處理---->編譯---->匯編---->鏈接。 可以利用GCC的參數來控制執行的過程,這樣就可以更深入的了解編譯C程序的過程。 下面將通過對一個程序的編譯來

gcc編譯c語言,非Makefile形式

gcc (選項) (引數) 選項: -o:指定生成的輸出檔案; -E:僅執行編譯預處理; -S:將C程式碼轉換為彙編程式碼; -wall:顯示警告資訊; -c:僅執行編譯操作,不進行連線操作。 引數:需要編譯的檔案 其中選項和引數位置可調換   主函式所在檔案inc

Linux程式設計時使用gcc編譯.c出現以下問題warning: the `gets' function is dangerous and should not be used.

Linux程式設計時使用gcc編譯.c出現以下問題 [[email protected] final1]# gcc -pthread client.c /tmp/ccSuK4v5.o: In function `writedata': client.c:(.text+0xb2a): w

Linux程式設計時使用gcc編譯.c出現以下問題,pthread.c:(.text+0x29): undefined reference to `pthread_create'collect2: err

Linux程式設計時使用gcc編譯.c出現以下問題 client.c:(.text+0x13e): undefined reference to `pthread_create' collect2: ld 返回 1   解決方法如下: 只需在用gcc編譯時加上-pthread選項即

linux下編譯C++程式

一、GCC(GNU Compiler Collection)是Linux下最主要的編譯工具,GCC不僅功能非常強大,結構也異常靈活。它可以通過不同的前端模組來支援各種語言,如Java、Fortran、Pascal、Modula-3和Ada     &nbs

在linux環境下編譯C++ 程式

在linux環境下編譯C++ 程式 單個原始檔生成可執行程式 下面是一個儲存在檔案 helloworld.cpp 中一個簡單的 C++ 程式的程式碼: 單個原始檔生成可執行程式 /* helloworld.cpp */ #include <iostream> int main

VS2013編譯C++程式閃爍即消失

   今天開始在VS2013下練習C++的程式設計,一個很所有學習程式設計開始的程式輸出“Hello C++!”: /************************************* *

linux下gcc編譯 .c檔案生成動態連結庫 .so檔案,並測試呼叫該連結庫

簡單介紹:linux中so檔案為共享庫,和windows下dll相似;so可以共多個程序呼叫,不同程序呼叫同一個so檔案,所使用so檔案不同;so原檔案不需要main函式;例項,1.通過mysqlTest.c中的函式mysql(),生成一個libmysql.so連結庫#inc

cmake編譯c++程式

當在Linux系統下編寫程式時候,如果沒有類似於visual studio、vs code等IDE(整合開發環境)時,如何編譯、執行程式呢?一種方法是編寫makefile檔案,用makefile檔案管理程式指令碼之間的相互依賴關係,其語法相對比較複雜。另一種有效的方法就是利用

mingw下用gcc編譯c檔案出現no such file or directory解決方法

c檔案直接拖進cmd時地址是對的,但gcc不認空格,所以要把路徑當做所有空格都去掉或改成“—”,這樣它就能直接發現檔案了,這時在cmd中編寫:gcc F:\new.c -o F:\new.exe   ,就會出現new.e

安裝、使用eclipse+CDT編譯C++程式

我想安裝、使用eclipse+CDT的初衷在看live555的原始碼,需要方便管理原始碼的工具: 使用eclipse編譯和管理live555原始碼 http://blog.csdn.net/nkmnkm/article/details/7403027   JDK+Eclipse+CDT+Mi

Windows命令列編譯C++程式

命令列編譯程式程式碼,因為編譯效率高、不用去研究開發工具,可以使初學者集中精力在程式碼理解上,因此(命令列+文字編輯器【推薦EditPlus】)非常適合程式設計學習。但往往由於命令列沒有像Visual Studio那麼友好的UI介面,加上需要記憶一堆引數,因而讓很多沒有人指

“vim+make+GCC編譯C++簡單例項

一、主線步驟為三步: 1、用vim編寫程式原始檔 2、用vim編寫makefile命令檔案 3、在終端裡輸入make命令 二、例項檔案有:head.h、head.cc、main.cc     head.h         //宣告head.cc裡的helloworld(