【问题标题】:C struct pointer to C++ class指向 C++ 类的 C 结构指针
【发布时间】:2021-01-12 23:45:44
【问题描述】:

我正在编写一个将加载到 C++ 程序中的 C 共享库。这个程序中有一个我需要访问的对象,所以我将使用一个结构指针。但我不太确定如何使 C 结构指针指向 C++ 类,因为它还包含函数(构造函数、析构函数等)。我认为这是可能的,因为结构只是保存数据,这意味着我可以按照与内存中的 C++ 类匹配的方式来排列它。示例:

//C++ class
class test
{
    public:
    int   number;
    char* string;

    public:
    test();
    ~test();
    void function(int new_number);
    void function(char* new_string);
} obj;
//C struct pointer

struct test_t
{
    //???
};

//...
struct test_t* p_obj = (struct test_t*)addr_of_obj;

有什么想法吗?

编辑:我无法编辑正在将库加载到的程序的源代码。我强制它加载这个库,它将为我访问和修改内存。

【问题讨论】:

  • 为什么 c 库必须知道将加载它的其他项目?我认为应该是相反的。
  • class test : test_t { whatever; };?这不需要你弄清楚如何将test_t 变成一个类——只需从test_t 继承即可。
  • 共享库将用C编译,程序已经用C++编译。我不能将 C++ 类继承到 C 代码中。而且我不能修改目标程序的源代码,这就是我使用这个库来访问和修改内存的原因。 “测试”类只是一个例子。
  • 您可以弄清楚特定实现如何布置特定 C++ 类,并定义与该布局匹配的 C 结构(在您的示例中,struct test_t {int number; char* string; }; 可能会起作用)。这将是脆弱的,在编译器之间不可移植,甚至可能在同一编译器的不同版本之间,或者在相同版本但使用不同选项进行编译之间。 C++ 类的二进制布局没有标准化,实现范围很广。

标签: c++ c class memory struct


【解决方案1】:

经过一些测试,我认为这个问题的答案是:取决于编译器和类。我将在下面给出的示例是在 Linux 上使用 GCC 和 G++ 编译的(似乎也可以使用 clang 和 clang++)。

示例 1 - 具有一些变量、构造函数、析构函数和重载函数的类 - 没什么特别之处。

//C++ class
class test
{
    private:
    int number;
    char* string;

    public:
    test() { this->number = 0; this->string = (char*)""; this->boolean = false; }
    ~test() {  }
    void function(int num) { this->number = num; }
    void function(char* str) { this->string = str; }
    int    get_number() { return this->number; }
    char*  get_string() { return this->string; }

    bool boolean;
};
//C structure
//To create a struct pointer for this class, I just had to ignore the functions. 

struct test_t
{
    int number;
    char* string;
    bool boolean;
};

示例 2 - 一个包含虚函数的类。在这种情况下,我们必须在结构的开头添加一个额外的指针参数。这将与该类的虚函数表对齐,由于虚函数而不再隐藏。

//C++ class
class test
{
    private:
    int number;
    char* string;

    public:
    test() { this->number = 0; this->string = (char*)""; this->boolean = false; }
    ~test() {  }
    virtual void v_function()  { printf("virtual function\n"); }
    virtual void v_function2() { printf("another virtual function\n"); }
    void function(int num) { this->number = num; }
    void function(char* str) { this->string = str; }
    int    get_number() { return this->number; }
    char*  get_string() { return this->string; }

    bool boolean;
};
//C structure
//It is the same as before, but with the VFT pointer
struct test_t
{
    void** vptr; //A pointer to function addresses (void**)
    int number;
    char* string;
    bool boolean;
};

总之,100% 确保 C 结构指针与 C++ 类对齐的唯一方法是查看内存并手动对齐。你可以通过在你的结构中放置垫子来跳过一些变量:

//C++ class
class test
{
    private:
    int number;
    std::vector<std::string> vector;
    char* string;

    public:
    test() { this->number = 0; this->string = (char*)""; this->boolean = false; }
    ~test() {  }
    virtual void v_function()  { printf("virtual function\n"); }
    virtual void v_function2() { printf("another virtual function\n"); }
    void function(int num) { this->number = num; }
    void function(char* str) { this->string = str; }
    int    get_number() { return this->number; }
    char*  get_string() { return this->string; }

    bool boolean;
};
//C structure
struct test_t
{
    void** vptr;
    int number;
    char vector[24]; //std::vector<std::string> pad (24 bytes long)
    char* string;
    bool boolean;
};

【讨论】:

  • 这是错误的,只要你的 C++ 类有一些 virtual 函数(或析构函数)。 virtual method table 通常是一些 hidden 指针。而且你需要考虑析构函数和 C++ 异常(所以throw
  • 我明白你在说什么。我用虚函数进行了测试,结果搞砸了:{_vptr.test = 0x555555557d98 , number = 10, string = 0x555555556049 "testing", boolean = false}
  • 编辑了答案。 @BasileStarynkevitch 现在好吗?
  • 基本上只适用于可以被认为是 POD 的类,即普通和标准的内存布局,在非静态数据成员之间没有访问修饰符,没有非静态 const 成员,没有虚拟方法,没有虚拟继承,没有基类的数据成员等
【解决方案2】:

实现这一点并使代码易于支持的方法是使用 C++ 代码中的 C 定义。也许使用私有实现来减少兼容性问题。

//C struct - non-static data

struct test_t
{
    int   number;
    char* string;
};

test_t 的 C++ 包装器。

// C++ interface

struct  test_t;
struct test
{
    test_t *priv; //private implementation
public:
    // methods.
};

【讨论】:

    【解决方案3】:

    classes 和 structs 在内存中的布局方式并未由 C 和 C++ 标准定义,而是在特定于平台的应用程序二进制接口 (ABI) 文档中进行了布局。为确保安全,您必须确保布局匹配。如果任何功能是虚拟的,您肯定会遇到问题。

    更安全的选择是将extern "C" 结构嵌入到您的C++ 类中,或者从C 代码中隐藏该类,只允许通过extern "C" 访问器函数进行访问。

    【讨论】:

      猜你喜欢
      • 2021-04-01
      • 2021-07-14
      • 1970-01-01
      • 1970-01-01
      • 2011-11-30
      • 2016-08-25
      • 1970-01-01
      • 2022-06-15
      • 2017-01-04
      相关资源
      最近更新 更多