了解一下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