我正在为用 C++ 编写的库编写一个包装器,以便可以从 C 中使用它。在包装器代码中,我制作了 C++ 容器底层数据的大量副本。例如。如果 C++ 库函数返回
std::vector<int>
,我的包装器将返回 {size_t len; size_t size; void *arr;}
形式的结构,其中 arr
包含向量中数据的副本。当用户使用完数据后,他们必须释放它。
我的问题是:用户(C 代码)在 C++ 中已 free()
:d 的指针上调用
malloc()
是否总是合法?或者我必须在我的包装器代码中创建一个等效的函数吗?
std::malloc
和 C 的 free
std::malloc
在<cstdlib>
中定义,据说它与C中的<stdlib.h>
具有相同的内容和含义(有一些变化,例如命名空间)。
这意味着您可以在 C++ 中使用 std::malloc
分配一些内存,并将其传递给某个调用 free
的 C 函数。
std::malloc
话虽如此,使用
free
来分配像您建议的 std::vector
这样的内存是不安全的。
所有进行内存分配的容器默认使用 std::allocator
,它使用 operator new
。
混合
new
和free
将是未定义的行为,即使获取和释放内存的底层操作系统函数是相同的。
std::vector
// C23
struct vector {
alignas(void*) unsigned char data[3 * sizeof(void*)];
};
void vector_init(struct vector*);
void vector_push(struct vector*, int element);
void vector_destroy(struct vector*);
int main() {
vector v;
vector_init(&v);
vector_push(&v, 42);
vector_destroy(&v);
}
到目前为止,我们基本上只是定义了一个
struct vector
来包含一定数量的字节和三个不透明函数。所有代码都是C23兼容的,我们可以用C++实现实际功能。
// C++20
static_assert(alignof(vector::data) >= alignof(std::vector));
static_assert(sizeof(vector::data) >= sizeof(std::vector));
extern "C" {
void vector_init(struct vector* v) {
std::construct_at(reinterpret_cast<std::vector<int>*>(v->data));
}
void vector_destroy(struct vector* v) {
std::destroy_at(reinterpret_cast<std::vector<int>*>(v->data));
}
void vector_push(struct vector* v, int element) {
auto* vec = std::launder(reinterpret_cast<std::vector<int>*>(v->data));
vec->push_back(element);
}
}
C++ 端使用
std::construct_at
(或者在 C++20 之前,您可以使用新的放置)。我们在 std::vector
的原始字节中创建一个 vector::data
。
请注意,我们在此代码中的任何位置都没有调用 new
、delete
、malloc
或 free
。
std::vector
仍然负责所有内存管理。