从C中的函数返回char数组的首选方式

Preferred way of returning a char array from a function in C

本文关键字:数组 方式 char 返回 函数      更新时间:2023-10-16

我目前正在处理一个C++dll,它向调用方返回一个字符串。为了使dll独立于构建过程的其余部分,以及C++缺乏标准ABI,我被迫用C字符指针包装字符串。

据我记忆所及,有两种以纯C方式返回字符串的可能性:

//Method 1
bool Foo1(wchar_t* s1, int len)
{
    //Needs space for 6 chars + null terminator
    if (len < 7)
        return false;
    wcscpy(s1, L"Hello1");
    return true;
}
//Method 2
wchar_t* Foo2()
{
    wchar_t* s2 = new wchar_t[10];
    wcscpy(s2, L"Hello2");
    return s2;
}
//Caller
int _tmain(int argc, _TCHAR* argv[])
{
    wchar_t s1[10];
    bool res = Foo1(s1, sizeof(s1) / sizeof(WCHAR));
    wchar_t* s2 = Foo2();
    delete s2;
    return 0;
}

是否存在有利于这两种解决方案之一的指导方针?我看到方法1主要用于Windows API,可能是由于历史原因。然而,我也没有看到使用第二种方法有任何负面影响,它消除了调用方预先分配内存的需要。唯一的缺点是,释放分配的内存的责任现在掌握在调用者手中。谢谢你的建议。

您不希望在DLL中new字符串,而只是返回它们。您还需要提供一个解除分配函数,调用方可以使用该函数来解除分配。因为如果调用方的delete[]实现与DLL的new[]实现匹配,那么根本不需要降到C级别,只需使用std::string即可。

正如您所指出的,另一种选择是让调用者提供一个缓冲区。

第三种选择是让调用者提供分配机制。

第四种选择是使用Windows专用的分配机制来执行此任务,即BSTR字符串、SysAllocString和好友。

如果有必要的话,我会把它做成COM服务器,而不是纯C风格的DLL。不过,用g++做这件事很麻烦。所以,如果我也必须支持g++,我可能会做一个纯的C DLL,并使用上面提到的BSTR


顺便说一句,没有理由使用Microsoft;除了核心遗留代码之外,还有过时和非标准的_tmain_TCHAR。由于Windows 9x的兼容性较弱,这种宏内容在2000年随着Layer for Unicode的推出而被淘汰。所以它已经过时15年了。

第三种方法:

inline char* calloc_buffer(void*,unsigned n){ return (char*)calloc(1,n);}
char* get( char*(*make_buff)(void*,unsigned n), void* );

它包括一个回调,让调用方分配缓冲区,以及一个调用方可以传递的函数,让他们调用缓冲区。这可以最大限度地提高效率。如果愿意,他们也可以使用本地缓冲区,如果不够大,可以在calloc上回退。带有偏移量和长度的版本也很有用。

接下来,对于一个高质量的C++可移植dll,我们包括可选的仅头部包装器:

inline char* from_vector(void*v_,unsigned n){
  auto v =static_cast<std::vector<char>*>(v_);
  v->resize(n);
  return v->data();
}
inline std::vector<char> get(){
  std::vector<char> buf;
  get( from_vector, &buf );
  return buf;
}

它为您提供了具有近似最优C代码的近似最优C++。

这取决于情况。第二种情况会使返回错误条件变得更加困难,您可能需要考虑这一点。当然,您可以使用errno来克服这个问题。最重要的是,没有对错之分,这是与您正在使用的代码风格或您的个人偏好相匹配的,如果是您自己的应用程序。