瞭解一下C與C++如何合做,gcc和g++編譯出來的東西有什麼區別。session
objdump是個好工具,能夠用於查看.o
文件的內容,也能夠查看可執行文件的內容。函數
查看符號表
objdump -t foo.o
工具
查看正文段
objdump -S foo.o
code
查看全部session
objdump -D foo.o
編譯器
先來看下面這個文件foo.cstring
#include <stdio.h> #include "foo.h" void foo() { printf("foo\n"); }
以gcc -c foo.c
編譯結果以下it
0000000000000000 <_Z3foov>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: bf 00 00 00 00 mov $0x0,%edi 9: e8 00 00 00 00 callq e <_Z3foov+0xe> e: 90 nop f: 5d pop %rbp 10: c3 retq
以g++ -c foo.c
編譯結果以下io
0000000000000000 <foo>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: bf 00 00 00 00 mov $0x0,%edi 9: e8 00 00 00 00 callq e <foo+0xe> e: 90 nop f: 5d pop %rbp 10: c3 retq
這個文件足夠簡單,能夠看到區別就只是函數名而已,gcc並無改變函數名,而g++在先後加了一些串。其實g++將參數信息插在函數名的尾部了,如上的_Z3foov
中的v
就表明了void。編譯
_Z3fooi
。_Z3food
。_Z3fooid
。若是參數是個自定義的類呢,好比:test
int foo(My my) { return 0; }
被編譯成
0000000000000047 <_Z3foo2My>: 47: 55 push %rbp 48: 48 89 e5 mov %rsp,%rbp 4b: 89 7d f0 mov %edi,-0x10(%rbp) 4e: b8 00 00 00 00 mov $0x0,%eax 53: 5d pop %rbp 54: c3 retq
能夠看到,直接以類名拼接在末尾。
若是是個std的類呢?好比string
void foo(std::string my) { printf("foo%s\n", my.c_str()); }
被編譯成
000000000000001a <_Z3fooNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE>: 1a: 55 push %rbp 1b: 48 89 e5 mov %rsp,%rbp 1e: 48 83 ec 10 sub $0x10,%rsp 22: 48 89 7d f8 mov %rdi,-0x8(%rbp) 26: 48 8b 45 f8 mov -0x8(%rbp),%rax 2a: 48 89 c7 mov %rax,%rdi 2d: e8 00 00 00 00 callq 32 <_Z3fooNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE+0x18> 32: 48 89 c6 mov %rax,%rsi 35: bf 00 00 00 00 mov $0x0,%edi 3a: b8 00 00 00 00 mov $0x0,%eax 3f: e8 00 00 00 00 callq 44 <_Z3fooNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE+0x2a> 44: 90 nop 45: c9 leaveq 46: c3 retq
很長很長,由於類名確實很長,這個你用lstrace跑個程序就知道了,不少函數名都很長得看不懂。
在C++源文件中是不能直接調用C源文件中的函數的,連接的時候就會報對‘foo()’未定義的引用
,由於C++源文件編譯時沒問題,連接時就找不到符號了.舉個例子,如今有文件main.cpp、foo.h、foo.c。
main.cpp內容以下:
#include "foo.h" int main() { foo(); return 0; }
foo.h內容以下:
#ifndef __FOO__ #define __FOO__ void foo(); #endif
foo.c內容以下:
#include <stdio.h> void foo() { printf("foo\n"); }
如今以以下命令編譯他們
g++ -c main.cpp gcc -c foo.c g++ -o test foo.o main.o # 這一步會報錯
報錯內容:
main.c:(.text+0x10):對‘foo()’未定義的引用 collect2: error: ld returned 1 exit status
這是由於在連接兩個.o
文件時,找不到foo
這個函數才報的錯。foo
確實是在foo.o
裏邊的,只不過main.o
中其實須要的是函數_Z3foov
纔對。
正確的作法是修改foo.h
文件以下
#ifndef __FOO__ #define __FOO__ extern "C" { void foo(); } #endif
這樣編譯出來的foo.o
沒有任何區別,可是main.o
就有區別了,裏面的符號_Z3foov
全被替換成foo
了(用objdump -t查看),這樣連接起來就沒問題。
看到這裏,extern "C"
的用法也就清晰了,即告訴g++編譯器,大括號內的符號都以C的符號命名方式去調用。值得注意的是,一般foo.h
不是一直被cpp文件所include的,有時一個程序會有C和CPP文件同時須要include它,通常須要在使用extern "C"
的時候用宏__cplusplus
來判斷此時的編譯器是否是C++的,就像下面這樣:
#ifndef __FOO__ #define __FOO__ #ifdef __cplusplus extern "C" { #endif void foo(); #ifdef __cplusplus } #endif #endif