将std :: string转换为char * for C函数时需要注意什么?
我已经阅读了许多post,询问有关如何将C ++ std::string
或const std::string&
转换为char*
以将其传递给C函数的问题,而且似乎有很多关于这样做的警告。 人们必须要注意字符串是连续的和许多其他的东西。 关键是我从来没有真正理解需要注意的所有要点以及为什么 ?
我想知道是否有人可以总结关于从std::string
转换为传递给C函数所需的char*
的警告和挫折?
这当std::string
是一个const
引用时,它只是一个非const引用,当C函数改变char*
时它不会改变它。
首先,const引用或值是否不会改变任何东西。
然后,您必须考虑该function的期望。 函数可以使用char*
或char const*
进行不同的事情 – 例如, memcpy
的原始版本使用了这些类型,并且可能仍然存在这样的代码。 希望是罕见的,在下文中,我将假设C函数中的char*
引用'\0'
终止的字符串。
如果C函数采用char const*
,则可以将std::string::c_str()
的结果传递给它; 如果它需要一个char*
,它取决于。 如果它只需要一个char*
因为它的历史可以追溯到C的前期,实际上它没有修改, std::string::c_str()
后面跟一个const_cast
是合适的。 但是,如果C函数使用char*
作为out参数,则事情变得更加困难。 我个人更喜欢声明一个char[]
缓冲区,传递它,然后将结果转换为std::string
,但std::string
所有已知实现都使用连续的缓冲区,标准的下一个版本将需要它,所以首先正确标量std::string
(使用std::string::resize()
,然后传递&s[0]
,然后将字符串重新strlen(s.c_str())
为结果长度(如果需要,使用strlen(s.c_str())
确定)也可以使用。
最后(但这也是使用char[]
C程序的一个问题),你必须考虑任何生命周期问题。 大多数使用char*
或char const*
只是使用指针而忘记它,但是如果函数将指针保存在某个地方以供以后使用,则字符串对象必须至少存活一段时间,并且在此期间不应修改其大小。期。 (同样,在这种情况下,我更喜欢使用char[]
。)
基本上,有三点很重要:
-
根据当前的标准,
std::string
实际上并不保证使用连续存储(据我所知,这是由于更改)。 但实际上,所有当前的实现都可能使用连续存储。 因此,c_str()
(和data()
)实际上可以在内部创建字符串的副本 … -
只要没有调用原始字符串上的非const方法,
c_str()
(和data()
)返回的指针就是有效的。 当C函数挂起指针时(这与在实际函数调用期间仅使用它相反),这使得它的使用不合适。 -
如果根本没有机会修改字符串,那么从
c_str()
不是一个好主意。 您必须使用字符串的副本创建一个缓冲区,并将其传递给C函数。 如果您创建缓冲区,请记住添加空终止。
[我会添加评论,但我没有足够的代表,所以很抱歉添加(还)另一个答案。]
虽然当前标准确实不能保证std :: string的内部缓冲区是连续的,但实际上所有实现似乎都使用连续的缓冲区。 此外,新的C ++ 0x标准(即将由ISO批准)需要std :: string中的连续内部缓冲区,甚至当前的C ++ 03标准要求在调用data()时返回连续的缓冲区或&str [0](虽然它不一定是空终止的)。 有关详细信息,请参见此处
这仍然不能使写入字符串安全,因为标准不会强制实现在调用data(),c_str()或operator时实际返回其内部缓冲区 ,并且它们都不会阻止使用优化像copy-on-write,这可能会使事情进一步复杂化(似乎新的C ++ 0x将禁止禁止写入时写入)。 话虽这么说,如果你不关心最大的可移植性,你可以检查你的目标实现,看看里面实际做了什么。 AFAIK,Visual C ++ 2008/2010总是返回真正的内部缓冲区指针,并且不进行写时复制(它确实有小字符串优化,但这可能不是一个问题)。
当C函数不改变char*
后面的字符串时,可以对const和非const std::string
实例使用std::string::c_str()
。 理想情况下,它将是一个const char*
,但如果不是(因为遗留API),您可以合法地使用const_cast
。 但是你可以只使用c_str()
的指针,只要你没有修改字符串!
当C函数确实改变了char*
后面的字符串时,使用std::string
唯一安全且可移植的方法是自己将它复制到临时缓冲区(例如来自c_str()
)! 确保之后释放临时内存 – 或使用std::vector
,它保证具有连续内存。
-
std:string可以存储零字节。 这意味着当传递给C函数时,它可能会过早被截断,因为C函数将在第一个零字节处停止。 如果您尝试使用C函数来过滤掉或转义不需要的字符,这可能会产生安全隐患。
-
std :: string :: c_str()的结果有时会因更改字符串的操作(非const成员函数)而失效。 如果在第一次使用c_str()然后修改字符串后尝试使用此指针,将导致很难诊断错误(“Heisenbugs”)。
-
永远不要使用
const_cast
。goto
不那么麻烦。