前言
虽说没有C++运行环境,但是有C运行环境,可以直接把所有的C++代码,转化为C代码。可以参考以下链接:
但是由于C++原有代码量巨大,手动改成C代码比较费力,而且存在后续更新维护的问题。那有没有其它方式,在不改动或较少改动C++代码的情况下,能让代码变得可用呢?
简单分析C++和C的编译过程
一个简单的测试代码
// test.cpp
#include <iostream>
using namespace std;
class Box
{
public:
double length;
double breadth;
double height;
};
int main(int argc, char const *argv[])
{
Box box;
box.height = 1.0;
box.length = 2.0;
box.breadth = 3.0;
double volume = box.height * box.length * box.breadth;
cout << "volume is: " << volume << endl;
return 0;
}
使用g++编译,生成 a.out文件,并运行。
chenls@chenls-pc:Desktop$ g++ test.cpp
chenls@chenls-pc:Desktop$ ./a.out
volume is: 6
使用readelf查看a.out的依赖关系
chenls@chenls-pc:Desktop$ readelf -a a.out | grep NEEDED
0x0000000000000001 (NEEDED) 共享库:[libstdc++.so.6]
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
发现可执行程序是会依赖libstdc++.so
修改test.cpp改用printf输出
// test.cpp
#include <stdio.h>
class Box
{
public:
double length;
double breadth;
double height;
};
int main(int argc, char const *argv[])
{
Box box;
box.height = 1.0;
box.length = 2.0;
box.breadth = 3.0;
double volume = box.height * box.length * box.breadth;
printf("volume is:%f\n", volume);
return 0;
}
编译执行,查看依赖关系
chenls@chenls-pc:Desktop$ g++ test.cpp
chenls@chenls-pc:Desktop$ ./a.out
volume is:6.000000
chenls@chenls-pc:Desktop$ readelf -a a.out | grep NEEDED
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
惊奇的发现,其不再依赖libstdc++.so了。此代码中包含了C++的class类,但在编译后,并不依赖c++的动态库,所以我们可以在一个无C++运行环境中运行此程序。
一个更加复杂的例子
// test.cpp
#include <stdio.h>
// 基类
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle : public Shape
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// 输出对象的面积
printf("Total area: %d\n", Rect.getArea());
return 0;
}
编译执行,查看依赖关系
chenls@chenls-pc:Desktop$ g++ test.cpp
chenls@chenls-pc:Desktop$ ./a.out
Total area: 35
chenls@chenls-pc:Desktop$ readelf -a a.out | grep NEEDED
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
既然我们上面使用了C++类和类的继承,都可以不依赖libstdc++.so,那我们尝试使用gcc编译呢?
chenls@chenls-pc:Desktop$ gcc test.cpp
chenls@chenls-pc:Desktop$ ./a.out
Total area: 35
chenls@chenls-pc:Desktop$ readelf -a a.out | grep NEEDED
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
依然一切工作正常,那么区别在那呢?
gcc和g++的区别
GCC:GNU Compiler Collection(GUN 编译器集合),它可以编译C、C++、JAV、Fortran、Pascal、Object-C、Ada等语言。
gcc是GCC中的GUN C Compiler(C 编译器),g++是GCC中的GUN C++ Compiler(C++编译器)。
具体区别如下:
1、gcc会根据文件后缀(.c,.cpp)自动识别是C文件还是C++文件,g++均认为是C++文件。
2、在编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,g++会在需要时自动链接C++标准库(libstdc++.so)。
3、gcc在编译cpp文件时、g++在编译c文件和cpp文件时(这时候gcc和g++调用的都是cpp文件的编译器),会加入一些额外的宏。 如下:
#define __GXX_WEAK__ 1
#define __cplusplus 1
#define __DEPRECATED 1
#define __GNUG__ 4
#define __EXCEPTIONS 1
#define __private_extern__ extern
实际上 g++ == gcc -xc++ -lstdc++ -shared-libgcc
总结:目前知道gcc和g++的区别后,我们可以认为,如果一个c++代码,如果能够直接通过gcc编译链接通过,那么它将不依赖c++运行环境。
更多gcc编译和链接原理,可参考 gcc程序的编译过程和链接原理
使用C++的new关键字
// test.cpp
#include <stdio.h>
// 基类
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle : public Shape
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
// 使用new关键字
Rectangle *Rect = new Rectangle();
Rect->setWidth(5);
Rect->setHeight(7);
// 输出对象的面积
printf("Total area: %d\n", Rect->getArea());
delete Rect;
return 0;
}
分别使用g++和gcc编译
chenls@chenls-pc:Desktop$ g++ test.cpp
chenls@chenls-pc:Desktop$ ./a.out
Total area: 35
chenls@chenls-pc:Desktop$ gcc test.cpp
/usr/bin/ld: /tmp/ccxla3ZO.o: in function `main':
test.cpp:(.text+0x12): undefined reference to `operator new(unsigned long)'
/usr/bin/ld: test.cpp:(.text+0x7a): undefined reference to `operator delete(void*, unsigned long)'
collect2: error: ld returned 1 exit status
编译报错 undefined reference to `operator new(unsigned long)' ,为什么会报错?这需要对C++的new 的实现有一定了解,可参考 深入C++的new
链接上libstdc++试试。
chenls@chenls-pc:Desktop$ gcc test.cpp -lstdc++
chenls@chenls-pc:Desktop$ ./a.out
Total area: 35
编译运行都正常。
chenls@chenls-pc:Desktop$ readelf -a a.out | grep NEEDED
0x0000000000000001 (NEEDED) 共享库:[libstdc++.so.6]
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
当然此时又加上了 libstdc++.so的依赖,达不到我们不想依赖libstdc++.so的目的。
重载C++的new和delete
// new_delete.cpp
#include <stdlib.h>
void *operator new(size_t size)
{
return malloc(size);
}
void *operator new[](size_t size)
{
return malloc(size);
}
void operator delete(void *p)
{
free(p);
}
void operator delete[](void *p)
{
free(p);
}
void operator delete(void *p, unsigned long s)
{
free(p);
}
再来编译运行
chenls@chenls-pc:Desktop$ gcc test.cpp new_delete.cpp
chenls@chenls-pc:Desktop$ ./a.out
Total area: 35
chenls@chenls-pc:Desktop$ readelf -a a.out | grep NEEDED
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
我们发现重载new和delete后,又不再对libstdc++.so的依赖,达到了目的。