在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
这可能已经被问了一百万次了。使用&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。考虑使用strlcpy
或strcpy_s
而不是strncpy
在库函数中强制空终止。
如果我们尝试将太多字符写入 value 会发生什么?也就是说,调用者指定的 valueLength 小于要写入的字符串?
@jakeliquorblues 然后它将结果截断为指定的大小。如果您想支持任意大小,那么使用char**
参数可能更合适。或者至少提供一个错误代码,表明缓冲区不够大,以便调用者可以用更大的缓冲区回调
@jakeliquorblues:双重间接允许getproperty
分配必要的空间,而不是写入调用者已经分配的缓冲区。你会像这样使用它:char *result; getproperty("name", &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>?
在 C++ 中将 std::string 转换为 Unicode 字符串