在c ++ 11中将std :: string转换为char * [重复]

Posted

技术标签:

【中文标题】在c ++ 11中将std :: string转换为char * [重复]【英文标题】:Convert std::string to char * in c++11 [duplicate] 【发布时间】:2014-03-17 16:34:41 【问题描述】:

我正在为混合语言编写一个库,因此我们必须坚持使用 C 接口。我需要调用这个 API 函数:

void getproperty(const char * key, /* in  */ 
                 char * value      /* out */) 

我需要用 std::string 的内容设置 value

std::string stdString = funcReturningString();
// set value to contents of stdString

我已经看到很多关于可能策略的答案,但没有针对这个特定用例的答案,也没有被认为是“最佳”或“标准”。我们正在使用 C++11,因为它的价值。一些答案含糊地建议将字符串的内容复制到缓冲区并使用它。但是怎么做呢?我想在这次谈话中获得更多意见,以便我最终可以选择一个策略并坚持下去。

谢谢!

更新:这是我使用的解决方案

我使用 uint32_t 与其他 API 函数保持一致。 bufferSize 是一个引用,因为 FORTRAN 通过引用调用所有内容。

void getproperty(const char * key,            /* in  */ 
                 const uint32_t & bufferSize, /* in  */
                 char * value,                /* out */
                 uint32_t & error             /* out */) 

  std::string stdString = funcReturningString();

  if (bufferSize < str.size() + 1) 
    // Set the error code, set an error message, and return
  

  strcpy(value, stdString.c_str());

我选择了 strcpy() 来保持一切标准,我选择了一个 bufferSize 参数,这样我就不必担心 cmets 中提到的一些双重间接问题。

【问题讨论】:

stdString.c_str() 有什么问题? @P0W,它不返回char * strString.c_str()将返回一个 const char*。也许试试this,它是普通的c++,但我不知道你为什么需要c++11 这可能已经被问了一百万次了。使用&amp;str[0] 我将假设该成员的名称getproperty() 是一个强有力的指标,表明该指针的非常量性并非偶然。如果是这样,这是一个写得很糟糕的函数,因为没有指定长度参数来防止缓冲区溢出,作者应该被鞭笞。 【参考方案1】:

getproperty API 调用似乎存在缺陷。调用者无法说明value 的长度。这意味着getproperty 的实现不能安全地写入value,因为它不知道它有多长。 getproperty 方法需要更改为采用char** value 或采用value 缓冲区的长度作为参数。让我们假设后者已经完成

void getproperty(const char* key, char* value, size_t valueLength);

现在为了复制从funcReturningString返回的值的内容,我们只需要使用strncpy

std::string stdString = funcReturningString();
strlcpy(value, stdString.c_str(), valueLength);

请注意,这仍然存在缺陷。如果valueLength == 0 或它小于字符串的长度,那么这将不起作用。最好将getproperty 更改为也返回某种错误代码,以便可以将此类故障传达给调用者

【讨论】:

+1 指出getproperty 的缺陷。 +1。考虑使用strlcpystrcpy_s 而不是strncpy 在库函数中强制空终止。 如果我们尝试将太多字符写入 value 会发生什么?也就是说,调用者指定的 valueLength 小于要写入的字符串? @jakeliquorblues 然后它将结果截断为指定的大小。如果您想支持任意大小,那么使用char** 参数可能更合适。或者至少提供一个错误代码,表明缓冲区不够大,以便调用者可以用更大的缓冲区回调 @jakeliquorblues:双重间接允许getproperty 分配必要的空间,而不是写入调用者已经分配的缓冲区。你会像这样使用它:char *result; getproperty("name", &amp;result);getproperty 会使用malloc 之类的东西来分配空间,将其地址写入result,然后将数据复制到它指向的缓冲区。【参考方案2】:

一个简单的方法是:

strcpy(value, stdString.c_str());

但是您需要小心缓冲区溢出。理想情况下,这个函数的调用者还应该给出它提供的缓冲区的大小,以确保你不会写到它的末尾。一种方法是传递一个额外的参数 buffer_size 并使用

strncpy(value, stdString.c_str(), buffer_size);
if (buffer_size > 0) value[buffer_size-1] = 0;

否则,在复制之前先缩短 stdString。

【讨论】:

【参考方案3】:

如果您需要获取值的可修改副本,您应该自己分配内存,value 的类型也应该是指针到指针。

void getproperty(const char * key, /* in  */ 
                 char ** value     /* out */) 
  if (value == NULL) 
      // ERROR
      return;
  

  const std::string& stdString = funcReturningString();

  // if this function is called from C 
  // you need to use 'malloc' instead of 'new' operator
  *value = malloc(stdString.size() + 1); // +NUL
  if (*value == NULL) 
      // ERROR mem alloc
      return;
  

  memcpy(*value, stdString.c_str(), stdString.size() + 1); // +NUL

那么你应该这样称呼它(在 C 上):

char* value = NULL;
getproperty("key", &value);
// use
...
// free it
free(value);

【讨论】:

为什么不用copy(stdString.begin(), stdString.end(), *value) 而不是memcpy? (类型安全性很好) 何乐而不为,它也可以使用。 如果这是针对 Windows 的,那么您通常需要在分配的同一个 DLL 中释放。这是因为与 c-runtime 的链接可能是静态/动态、单/多线程的某种组合。在每种情况下,它都会影响堆的管理方式。所以你需要一个ReleaseMemory() 函数。 是的,你是对的。可能你应该添加像void freeproperty(char* value)这样的函数。【参考方案4】:

如果这是针对 Windows 的,那么您通常需要在您分配的同一个 DLL 中释放。这是因为与 c-runtime 的链接可能是静态/动态、单/多线程的某种组合。在每种情况下,它都会影响堆的管理方式。

如果有很多 C 运行时库,那就太糟糕了。 原则上,单一定义规则说程序中应该只有一个 malloc 和一个 free... 拥有它们是件好事 :-)memcpy 可能出于性能原因而更受欢迎。另外,当您确实需要复制内存时,为什么要使用迭代器和算法copy 而不是memcpy?它提供了什么“类型安全”?

【讨论】:

C 和 C++ 标准都不承认 DLL/SO 的存在,因此 ODR 实际上不能可靠地应用于它们……(并且表现良好的 DLL/SO 仅导出有限的API ODR 通常不是问题) 你觉得这很奇怪吗? dll 和 Sos 正在使用中,但不符合标准。 ? :-) 实际上,它们都是目标文件,用于形成一个应该有效且不会生病的程序...... 再一次,如果标准对 dll 只字未提,它仍然需要 ODR。并且由于“没有问题”,因此使用 malloc/free 没有问题......此外,malloc 和 free 的行为和使用由标准的特定声明定义。我认为,DLL 可以有自己的内存管理器(您是否在 DLL 中本地管理内存独占?):-),但 malloc/free 应该仍然可以根据需要使用... 这有什么奇怪的? DLL/SO 本质上是特定于平台的(就像实际的二进制格式一样,更不用说编译器生成的汇编程序),因此不能在标准中定义。至少对于 DLL,您对它们“只是”目标文件的假设是错误的,因为 DLL 没有链接,而只是在程序执行期间加载,并允许通过导出的入口点/函数进行访问。是的,该标准需要 ODR,但由于 DLL 没有链接,因此永远不会发生 ODR 违规 - DLL 和 EXE 从根本上是相互分离的…… 这太可笑了。请参阅上面有关 malloc/free 的信息,并尝试解释为什么在 ODR 未损坏且实现也未损坏的情况下它们无法按要求工作(或向 Microsoft 询问其实现行为如此不正确的原因)。 DLL 是库并且是链接的。无论它们是链接还是加载,程序都必须不破坏 ODR,它真正形成的方式(来自 EXE、DLL 和其他 ocx)不起作用。

以上是关于在c ++ 11中将std :: string转换为char * [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++17 中将 std::string 转换为 std::vector<std::byte>?

如何在MFC中将std:string转换为LPCTSTR

在 C++ 中将 std::string 转换为 Unicode 字符串

在 C++ 中将字符串转换为 Cstring

C++ .NET 将 System::String 转换为 std::string

在 C++ 中将浮点数转换为 std::string