如何在无C++运行环境下,运行大部分的C++代码(一)

前言

虽说没有C++运行环境,但是有C运行环境,可以直接把所有的C++代码,转化为C代码。可以参考以下链接:

如何将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的依赖,达到了目的。

下一篇 如何在无C++运行环境下,运行大部分的C++代码(二)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,出现"undefined reference to `operator new(unsigned long long)' collect2.exe: error: ld returned 1 exit status"错误是由于C++代码中使用了未定义的函数或符号导致的。这个错误通常发生在链接阶段,编译器无法找到对应的函数或符号的定义。 解决这个问题的方法有以下几种: 1. 确保正确链接所需的库文件:检查makefile文件中是否正确链接了所需的库文件。特别是对于C++代码,可能需要链接C++标准库(libstdc++)和其他相关的库文件。 2. 检查函数或符号的定义:确保使用的函数或符号在代码中有正确的定义。如果是使用了第三方库,可能需要检查是否正确包含了对应的头文件,并链接了对应的库文件。 3. 检查编译器选项:有时候编译器选项可能会导致链接错误。可以尝试修改编译器选项,例如使用不同的优化级别或调试选项。 4. 检查编译器版本和标准:有时候编译器版本或使用的C++标准可能会导致链接错误。可以尝试更新编译器版本或修改使用的C++标准。 5. 检查代码中的语法错误:有时候链接错误可能是由于代码中的语法错误导致的。可以仔细检查代码,确保没有语法错误。 下面是一个示例的makefile文件,用于编译C++代码并链接所需的库文件: ```makefile CC = g++ CFLAGS = -Wall -std=c++11 LDFLAGS = -lstdc++ TARGET = myprogram OBJS = main.o $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) %.o: %.cpp $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(TARGET) $(OBJS) ``` 请注意,上述示例中的makefile文件假设源代码文件为main.cpp,并且需要链接C++标准库(libstdc++)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值