当前位置: 首页 > 知识库问答 >
问题:

与C #中的C #灵活数组成员进行互操作的正确方法是什么?

上官英哲
2023-03-14

我知道灵活的阵列成员不是C11标准的一部分。

那么,与从C 11返回或接受具有灵活数组成员的结构作为参数的C代码互操作的正确方式是什么?

我应该写一个填充程序,将灵活的数组成员从 C 结构映射到 C 中的指针吗?

共有3个答案

方野
2023-03-14

由于灵活数组成员不能对C (my_type fam[])公开;不是有效的C成员),我们必须定义自己的类型。

幸运的是,C链接函数没有依赖于其参数的符号。因此,我们可以在共享标头中修改foo的定义,或者定义我们自己的并且不包含它们的标头。

这是一个可能在布局方面兼容的< code>struct。请注意,在C语言中,永远不要在堆栈上声明这些:

struct foo {
  int count;
  #ifndef __cplusplus
  my_type fam[];
  #else
  my_type do_not_use_fam_placeholder;
  my_type* fam() {
    return &do_not_use_fam_placeholder;
  }
  my_type const* fam() const {
    return &do_not_use_fam_placeholder;
  }
  #endif
};

这依赖于C中的< code>foo结构的二进制布局,它是前缀成员,后跟灵活数组成员的元素,不需要进行额外的打包或对齐。它还要求灵活数组成员不能为空。

我会使用这个 1,但如果计数fam 之间有填充,则会遇到对齐问题。

不建议在foo上使用memcpy或memmov等。一般来说,在 C 端创建一个 foo 不是一个好主意。如果必须这样做,可以执行以下操作:

struct foo_header {
  int count;
};

foo* create_foo_in_cpp(int count) {
  std::size_t bytes = sizeof(foo)+sizeof(my_type)*(count-1);
  foo* r = (foo*)malloc(bytes);
  ::new((void*)r) foo_header{count};
  for (int i = 0; i < count; ++i)
    ::new( (void*)(r->fam()+i) my_type();
  return r;
};

它构造了 C 中每个有问题的对象。C的客体存在规则比C的更严格;仅仅获取一些 POD 内存并将其解释为 POD 在 C 中不是一个有效的操作,而在 C 中却是有效的。上面的s 将在运行时优化为 noops,但 C 需要声明在严格读取标准的情况下,应将有问题的内存视为该类型的对象。

现在,手动地按元素构造数组的元素以及数组和元素之间的布局兼容性存在一些标准问题(缺陷),因此您必须在一定程度上相信C编译器的ABI和C代码是兼容的(或检查它)。

一般来说,C和C之间的所有互操作都不是由C标准定义的(除了C包含的标准C库的某些部分;即使在这里,也没有要求C使用相同的C库)。您必须了解您对C和C的特定实现是如何互操作的。

边浩波
2023-03-14

Windows使用了一个技巧,将灵活的数组成员设置为大小1(因为Win32API早在该功能进入C99之前就已经开发出来了,更不用说C了)

struct foo {
    int count;
    my_type fam[1];
};

如果您被允许更改 C 版本,请在 C 和 C 中使用相同的结构。如果您无法更改 C 版本,则需要在 C 中重新定义结构。修改 C 结构时,您仍然需要更改 C 代码,但至少它可以很好地编译

参见

  • 灵活的数组成员在 C 中有效吗?
  • 为什么有些结构以大小为 1 的数组结尾?
艾弘义
2023-03-14

据我所知,标准C甚至不接受带有灵活数组成员的结构声明。既然如此,我认为除了(用C)写包装函数别无选择,除非包含FAM的结构类型对你的C代码来说是不透明的。我不确定包装纸是不是你想要的那种垫片。

然而,在我们进一步讨论之前,我应该指出,如果您的C函数接受并返回指向具有灵活数组成员的结构的指针,那么问题与它们传递并返回实际结构的指针有很大不同。我将假设他们确实使用指向这些结构的指针,否则似乎一开始就没有FAM的意义。

我猜给定C声明,例如

struct foo {
    int count;
    my_type fam[];
};

我将在C中表示相同的数据

struct cpp_foo {
    int count;
    my_type *fam;
};

,这当然也可以由C处理。请注意,无法在这些数组之间成功转换,因为数组不是指针。

给定一个C函数

struct foo *do_something(struct foo *input);

所需的包装可能如下所示:

struct cpp_foo *do_something_wrap(struct cpp_foo *input) {
    struct cpp_foo *cpp_output = NULL;

    // Prepare the input structure
    size_t fam_size = input->count * sizeof(*input->fam);
    struct foo *temp = malloc(sizeof(*temp) + fam_size);

    if (!temp) {
        // handle allocation error ...
    } else {
        struct foo *output;

        temp->count = input->count;
        memcpy(temp->fam, input->fam, fam_size);

        // call the function
        output = do_something(temp);

        if (output) {
            // Create a copy of the output in C++ flavor
            cpp_output = malloc(sizeof(*cpp_output));
            if (!cpp_output) {
                // handle allocation error
            } else {
                fam_size = output->count * sizeof(output->fam[0])
                cpp_output->fam = malloc(fam_size);
                if (!cpp_output) // handle allocation error

                memcpy(cpp_output->fam, output->fam, fam_size);

                // Supposing that we are responsible for the output object ...
                free(output);
            }
        } // else cpp_output is already NULL

        free(temp);
    }

    return cpp_output;
}

自然,如果你有几个函数要包装,那么你可能想写可重用的转换函数来简化它。

 类似资料:
  • 问题内容: 我对swig中的typemap和如何使用数组感到迷茫。我准备了一个使用swig在java和c之间使用数组的工作示例,但是我不知道这是否是正确的方法。 基本上,我想将字节数组从Java 传递给c作为“ signed char *” +它的大小,在c中对其进行修改并查看java中的更改,并在c中创建一个数组并在Java中使用它。 *在C中被修改为Java或其他的缓冲区?](http://c

  • 问题内容: 目标: 使用C#和SQL2008正确设置参数化的SQL插入语句 问题: for循环中使用了以下语句,因此必须清除这些值。运行此代码后,它指出在250附近有语法错误。代码如下 } 在此先感谢您的更正,评论和建议。 问题答案: 您不必在SQL代码中重新声明变量。这应该工作:

  • 本文向大家介绍什么是C#中的静态成员函数?,包括了什么是C#中的静态成员函数?的使用技巧和注意事项,需要的朋友参考一下 静态函数只能访问静态变量。静态函数甚至在创建对象之前就已存在。 将静态函数设置为- 以下是演示静态函数用法的示例- 示例 输出结果

  • 问题内容: 我有一个大量的用C 编写的类库。我试图通过Swift内的某种桥接来利用它们,而不是将它们重写为Swift代码。主要动机是C 代码代表一个在多个平台上使用的核心库。实际上,我只是在创建一个基于Swift的UI,以允许核心功能在OS X下工作。 还有其他问题,“如何从Swift调用C 函数”。这 不是 我的问题。要桥接到C 函数,可以很好地进行以下操作: 通过“ C”定义桥接头 Swift

  • 本章包含了许多可以在Python使用原生代码(主要是C/C++)方式的介绍,这个过程通常被称为封装。本章的目的是给你有哪些技术存在已经各自有优劣式的一点儿感觉,这样你可以根据你的具体需求选择适合的方式。无论如何,只要你开始做封装,你几乎都必然需要咨询你选定技术的文档。 2.8.1 简介 本章将涵盖一下技术: Python-C-Api Ctypes SWIG (简化封装器和接口生成器) Cython

  • 短版 Spotify Android应用程序在最近的一个版本中引入了一个“设备广播状态”开关,我想在我的应用程序中接收那些广播。有哪些广播可用,我应该如何配置我的广播接收器? 我从Spotify应用程序本身中使用了设备广播状态开关,我很高兴该开关取消/激活了这些广播事件。 我现在遇到的问题是,这些信息似乎只是零星地发送。我接收playstatechanged事件(播放、暂停、下一个、上一个),但它

  • 本文向大家介绍什么是C#类的静态成员?,包括了什么是C#类的静态成员?的使用技巧和注意事项,需要的朋友参考一下 我们可以使用static关键字将类成员定义为static。当我们将一个类的成员声明为静态成员时,这意味着无论创建了多少个该类的对象,静态成员只有一个副本。 关键字static表示该类仅存在成员的一个实例。静态变量用于定义常量,因为可以通过调用该类而不创建其实例来检索其值。静态变量可以在成

  • 给予 什么是?它是实现定义的还是未定义的行为?C和C的答案不同吗?