如果有人问你,extern "C"的作用是什么?你会如何回答?
先说一段故事:C++创始人在编写C++的时候,C语言正盛行,他不得不让C++兼容C。C++最大的特性就是封装,继承,多态,重载。而这些特性恰恰是C语言所不具备的。至于多态,核心技术是通过虚函数表实现的,其实也就是指针。而对于重载,与C语言相比,其实就是编译方式不同而已: C++编译方式和C编译方式。对于函数调用,编译器只要知道函数的参数类型和返回值以及函数名就可以进行编译连接。extern “C”是让程序按照C的方式编译。我们先来看看C++和C两种编译方式对于究竟有何不同。
main.c
#include <stdio.h>
int func(int a)
{
printf("a = %d",a);
return a;
}
int main()
{
int i=10;
func(i);
return 0;
}
gcc -c main.c之后生成main.o我们使用objdump -d main.o查看如下内容:
main.o.c: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <func>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 89 7d fc mov %edi,-0x4(%rbp)
b: 8b 45 fc mov -0x4(%rbp),%eax
e: 89 c6 mov %eax,%esi
10: bf 00 00 00 00 mov $0x0,%edi
15: b8 00 00 00 00 mov $0x0,%eax
1a: e8 00 00 00 00 callq 1f <func+0x1f>
1f: 8b 45 fc mov -0x4(%rbp),%eax
22: c9 leaveq
23: c3 retq
0000000000000024 <main>:
24: 55 push %rbp
25: 48 89 e5 mov %rsp,%rbp
28: 48 83 ec 10 sub $0x10,%rsp
2c: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp)
33: 8b 45 fc mov -0x4(%rbp),%eax
36: 89 c7 mov %eax,%edi
38: e8 00 00 00 00 callq 3d <main+0x19>
3d: b8 00 00 00 00 mov $0x0,%eax
42: c9 leaveq
43: c3 retq
我们可以看到函数func就是func,在.o文件中名字没有变化。
main.cpp
#include <iostream>
int func(int a)
{
return a;
}
int main()
{
int i=10;
func(i);
return 0;
}
使用g++ -c main.cpp之后生成main.o,同样使用objdump -d main.o看到如下内容:
main.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <_Z4funci>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d fc mov %edi,-0x4(%rbp)
7: 8b 45 fc mov -0x4(%rbp),%eax
a: 5d pop %rbp
b: c3 retq
000000000000000c <main>:
c: 55 push %rbp
d: 48 89 e5 mov %rsp,%rbp
10: 48 83 ec 10 sub $0x10,%rsp
14: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp)
1b: 8b 45 fc mov -0x4(%rbp),%eax
1e: 89 c7 mov %eax,%edi
20: e8 00 00 00 00 callq 25 <main+0x19>
25: b8 00 00 00 00 mov $0x0,%eax
2a: c9 leaveq
2b: c3 retq
000000000000002c <_Z41__static_initialization_and_destruction_0ii>:
2c: 55 push %rbp
2d: 48 89 e5 mov %rsp,%rbp
30: 48 83 ec 10 sub $0x10,%rsp
34: 89 7d fc mov %edi,-0x4(%rbp)
37: 89 75 f8 mov %esi,-0x8(%rbp)
3a: 83 7d fc 01 cmpl $0x1,-0x4(%rbp)
3e: 75 27 jne 67 <_Z41__static_initialization_and_destruction_0ii+0x3b>
40: 81 7d f8 ff ff 00 00 cmpl $0xffff,-0x8(%rbp)
47: 75 1e jne 67 <_Z41__static_initialization_and_destruction_0ii+0x3b>
49: bf 00 00 00 00 mov $0x0,%edi
4e: e8 00 00 00 00 callq 53 <_Z41__static_initialization_and_destruction_0ii+0x27>
53: ba 00 00 00 00 mov $0x0,%edx
58: be 00 00 00 00 mov $0x0,%esi
5d: bf 00 00 00 00 mov $0x0,%edi
62: e8 00 00 00 00 callq 67 <_Z41__static_initialization_and_destruction_0ii+0x3b>
67: c9 leaveq
68: c3 retq
0000000000000069 <_GLOBAL__sub_I__Z4funci>:
69: 55 push %rbp
6a: 48 89 e5 mov %rsp,%rbp
6d: be ff ff 00 00 mov $0xffff,%esi
72: bf 01 00 00 00 mov $0x1,%edi
77: e8 b0 ff ff ff callq 2c <_Z41__static_initialization_and_destruction_0ii>
7c: 5d pop %rbp
7d: c3 retq
我们可以看到函数func名字变为了_Z4funci,因此在c/c++混合编程中经常看到如下方式:
#ifndef __INCvxWorksh /*防止该头文件被重复引用*/
#define __INCvxWorksh
#ifdef __cplusplus //__cplusplus是cpp中自定义的一个宏
extern "C" { //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
#endif
/**** some declaration or so *****/
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */
例子一:
animal.h
#ifndef __ANIMAL_H__ //防止被重复包含
#define __ANIMAL_H__
class ANIMAL{
public:
ANIMAL(char* );
~ANIMAL();
char* getname(void) const;
private:
char* name;
};
#ifdef __cplusplus
extern "C" {
#endif
void print(void);
#ifdef __cplusplus
}
#endif
#endif // __ANIMAL_H__
animal.cpp
#include "animal.h"
#include <iostream>
#include <string.h>
using namespace std;
ANIMAL::ANIMAL(char *data)//构造函数
{ name = new char[64];
strcpy(name, data);
}
ANIMAL::~ANIMAL() //析构函数
{
if(name)
{
delete[] name;
name = NULL;
}
}
char* ANIMAL::getname(void) const
{ return name;
}
void print(void) //对外接口,而且必须有一个非类中方法,才能被C调用
{
char name[]="dog";
ANIMAL animal(name);
char* animal_name = animal.getname();
cout << "animal name is :" << animal_name << endl;
}
main.c
#include<stdio.h>
int main(void)
{
print();
return 0;
}
使用如下命令编译:
g++ -c animal.cpp
gcc -lstdc++ main.c animal.o -o main
执行./main输出内容如下:
animal name is :dog
如果在编译中未加-lstdc++的话会有如下错误:
[root@centos-linux-7 func]# gcc main.c animal.o -o main
animal.o:在函数‘ANIMAL::ANIMAL(char*)’中:
animal.cpp:(.text+0x16):对‘operator new[](unsigned long)’未定义的引用
animal.o:在函数‘ANIMAL::~ANIMAL()’中:
animal.cpp:(.text+0x69):对‘operator delete[](void*)’未定义的引用
animal.o:在函数‘print’中:
animal.cpp:(.text+0xc4):对‘std::cout’未定义的引用
animal.cpp:(.text+0xc9):对‘std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)’未定义的引用
animal.cpp:(.text+0xd8):对‘std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)’未定义的引用
animal.cpp:(.text+0xdd):对‘std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)’未定义的引用
animal.cpp:(.text+0xe5):对‘std::ostream::operator<<(std::ostream& (*)(std::ostream&))’未定义的引用
animal.o:在函数‘__static_initialization_and_destruction_0(int, int)’中:
animal.cpp:(.text+0x13b):对‘std::ios_base::Init::Init()’未定义的引用
animal.cpp:(.text+0x14a):对‘std::ios_base::Init::~Init()’未定义的引用
animal.o:(.eh_frame+0x8b):对‘__gxx_personality_v0’未定义的引用
collect2: 错误:ld 返回 1
对应的Makefile:
main:main.c animal.o
gcc -lstdc++ main.c animal.o -o main
animal.o:animal.h animal.cpp
g++ -c animal.cpp
.PHONY : clean
clean:
-rm animal.o main
Makefile基本知识可以参考我的笔记Makefile与C
C++调用C函数
C++调用C函数的例子: 引用C的头文件时,需要加extern "C"
//cExample.h
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
int add(int x,int y);
#endif
//cExample.c
#include "cExample.h"
int add( int x, int y ) {
return x + y;
}
//cppExample.cpp
extern "C" {
#include "cExample.h"
}
int main(int argc, char* argv[]) {
add(2,3);
return 0;
}
Makefile
CFLAGS=-O2 -Wall
main: cExample.o cppExample.o
g++ $(CFLAGS) -o main cExample.o cppExample.o
cExample.o: cExample.c
gcc $(CFLAGS) -c cExample.c
cppExample.o: cppExample.cpp
g++ $(CFLAGS) -c cppExample.cpp
clean:
rm -f *.o main
注意,如果cppExample.cpp中没有extern "c"的话,会在最后一步链接的时候出错。
C调用C++函数
// cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" { int add( int x, int y ); }
#endif
// cppExample.cpp
#include "cppExample.h"
int add( int x, int y ) {
return x + y;
}
// cExample.c
extern int add( int x, int y );
int main() {
add( 2, 3 );
return 0;
}
Makefile
CFLAGS=-O2 -Wall
main: cExample.o cppExample.o
g++ $(CFLAGS) -o main cExample.o cppExample.o
cppExample.o: cppExample.cpp
g++ $(CFLAGS) -c cppExample.cpp
cExample.o: cExample.c
gcc $(CFLAGS) -c cExample.c
clean:
rm -f *.o main
楼主还有邀请资格吗
还有邀请链接,如果你需要我发给你。
@Younfor 还有邀请链接,如果你需要我发给你。已发送邮件给你。