Convert std::string to char * in c++11
我正在为混合语言编写一个库,因此我们必须坚持使用C接口。我需要调用这个API函数:
1 2
| void getproperty(const char * key, /* in */
char * value /* out */) { |
我需要用STD::字符串的内容设置值。
1 2
| std::string stdString = funcReturningString();
// set value to contents of stdString |
我已经看到了许多可能的策略的答案,但是对于这个特定的用例没有一个,也没有被认为是"最好的"或"标准的"。我们使用C++ 11,因为它的价值。有些答案含糊不清地建议将字符串的内容复制到缓冲区并使用它。但是怎么做呢?我想对这次谈话有更多的看法,这样我才能最终选择一个策略并坚持下去。
谢谢您!
更新:这是我使用的解决方案
我使用uint32来保持与其他api函数的一致性。BufferSize是一个引用,因为Fortran通过引用调用所有内容。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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参数,这样我就不必担心注释中提到的双间接寻址的一些问题。
- stdString.c_str()出了什么问题?
- @p0w,它不返回char *。
- strString.c_str()将返回常量char*。也许试试这个,它是纯C++但是我不知道你为什么需要C++ 11
- 它返回一个const*char,从而产生从const*char到char*的无效转换错误。
- 这已经被问了大概一百万次了。使用&str[0]。
- 使用strcpy()函数将const char*复制到char *中,这是一个额外的开销。
- 您的API是否有方法在不调用GetProperty的情况下请求值的长度?
- 我假设那个成员的名字,getproperty(),是一个很强的指标,它表明指针的非常量不是偶然的。如果是这样的话,这是一个写得很糟糕的函数,因为没有指定长度参数来防止缓冲区溢出,所以应该鞭打作者。
- 你凭什么接受一个uint32_t?小于指针的类型传递值的效率更高。
似乎getpropertyAPI调用有缺陷。调用方无法说明value的长度。这意味着getproperty的实现不能安全地写入value,因为它不知道它有多长时间。getproperty方法要么需要更改为取char** value或取value缓冲区的长度作为参数。假设后者完成了
1
| void getproperty(const char* key, char* value, size_t valueLength); |
现在,为了复制从funcReturningString返回的值的内容,我们只需要使用strncpy。
1 2
| std::string stdString = funcReturningString();
strlcpy(value, stdString.c_str(), valueLength); |
请注意,这仍然有缺陷。如果valueLength == 0或其长度小于字符串的长度,那么这将不起作用。最好是更改getproperty,同时返回某种类型的错误代码,这样就可以将此类故障传达给调用方。
- +1指出getproperty的缺陷。
- + 1。考虑使用strlcpy或strcpy_s,而不是strncpy,强制在库函数中终止空值。
- 如果我们试图将太多的字符写入值中会发生什么?换句话说,调用者指定的值长度小于要写入的字符串?
- @然后它会将结果截断为指定的大小。如果您希望支持任意大小,那么使用char**参数可能更合适。或者至少提供一个错误代码,指示缓冲区不够大,以便调用方可以使用较大的缓冲区进行回调。
- @是的,Billyoneal没有意识到这是一个可移植的API调用。转换
- 为了总结这个问题,专家可以解释双指针选项背后的逻辑和值吗?我不太明白双指针解决方案是如何工作的。
- @JakeliQuorBlues:双间接允许getproperty分配必要的空间,而不是写入已经由调用方分配的缓冲区。你可以这样使用:char *result; getproperty("name", &result);。getproperty将使用malloc之类的东西来分配空间,将其地址写入result中,然后将数据复制到它所指向的缓冲区。
- 请注意,在某些平台(如Windows)上使用这种双重间接寻址是危险的,在这种情况下,在一个模块中的分配在另一个模块中不能自由使用(每个CRT实例都有分配程序状态数据)。这比你想象的要频繁。
- 所以strlcpy不是标准函数。我可以安全地将它用于Windows和Linux部署吗?
- @Jakeliquorblues很沮丧。您可以使用strncat,如果dest缓冲区不够大,只需手动执行空终止。
一个简单的方法是:
1
| strcpy(value, stdString.c_str()); |
但您需要小心缓冲区溢出。理想情况下,这个函数的调用者还应该给出它提供的缓冲区的大小,以确保您不会写得超过它的末尾。一种方法是传递一个额外的参数缓冲区大小并使用
1 2
| strncpy(value, stdString.c_str(), buffer_size);
if (buffer_size > 0) value[buffer_size-1] = 0; |
否则,在复制之前先缩短stdstring。
如果您需要获得值的可修改副本,您应该自己分配内存,value的类型也应该是指向指针的指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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上):
1 2 3 4 5 6
| 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)这样的函数。
If this is going to target Windows then you generally need to free in the same DLL that you allocate. This is because the linkage with the c-runtime may be with some combination of static/dynamic, single/multi-threaded. In each case it affects how the heap is managed.
如果有很多C运行库,那就太糟糕了。原则上,一个定义规则说程序中应该只有一个malloc和一个free…拥有它们是件好事——)由于性能原因,可优先选用memcpy。另外,当您确实需要复制内存时,为什么要使用迭代器和算法copy而不是memcpy?它提供了什么样的"类型安全"?
- 嗯,C和C++标准都不承认DLLS/SOS的存在,所以ODR实际上不能在它们之间可靠地应用……(并且行为良好的DLLS/SOS只输出有限的API。ODR通常不是问题)。
- 你觉得这有点奇怪吗?DLL和SOS正在使用中,但超出了标准。?:-)实际上,它们都是对象文件,用于形成一个有效且不生病的程序…
- 同样,如果标准中没有提到dll,它仍然需要odr。由于"没有问题",使用malloc/free没有问题…此外,malloc和free的行为和使用由特定的标准声明定义。我认为,DLL可以有自己的内存管理器(您是否在本地管理DLL中独占的内存?):-),但malloc/free仍应在必要时可用…
- 有什么奇怪的?dlls/sos本身就是特定于平台的(就像实际的二进制格式,更不用说编译器生成的汇编程序),因此不能在标准中定义。至少对于DLL来说,假设它们是"just"对象文件是错误的,因为DLL不是链接的,而是在程序执行期间加载的,并且允许通过导出的入口点/函数进行访问。是的,该标准要求ODR,但是由于DLL没有链接,因此不会发生ODR冲突-DLL和Exe从根本上分离开来…
- 这是可笑的。请参阅上面关于malloc/free的内容,并尝试解释为什么在ODR没有中断并且实现也没有中断的情况下,它们不能按要求工作(或者向Microsoft询问它们实现的错误行为的原因)。DLL是库,并且是链接的。不管它们是链接的还是加载的,程序都需要不破坏ODR,它真正形成的方式(来自exes、dlls和其他ocx)不起作用。
- 例如,Windows API提供了globalalloc函数,在某些情况下,它可以作为自定义分配器的基础,但是…名称中存在"global"的事实并不能保证其唯一性(如果malloc被破坏,为什么globalalloc不可能是?……):-)然后到目前为止,我们将讨论此类自定义分配器的属性和约定…
- DLL是库,并且是链接的。这是错误的,您可能希望了解Windows实际如何处理DLL…DLL是导出特定函数集的黑盒,未导出的所有内容都对调用方隐藏=>此处不发生符号链接(这与Unix共享对象中使用的传统行为形成了对比!)=>当dll和exe定义类似"malloc"的符号时,不违反odr…
- (你知道字母d l l是什么意思吗?)图书馆是一个黑洞,可以导出一组特定的…符号…:-)未导出的所有内容(…)。如果存在多个定义,则不会违反ODR。大声笑。
- 我在这里完成了,你不知道DLL是如何在Windows上工作的,这是肯定的……没有静态库不是黑板,你会得到ODR与重复符号的冲突……