将 C 函数的 std::string 转换为 char* 时要注意什么?

What to watch out for when converting a std::string to a char* for C function?

提问人:Tony The Lion 提问时间:4/12/2011 最后编辑:sbiTony The Lion 更新时间:4/15/2011 访问量:1926

问:

我读过很多帖子,询问如何将 C++ 转换为 a 以将其传递给 C 函数的问题,似乎有很多关于这样做的警告。必须注意字符串是连续的,还有很多其他事情。关键是我从来没有真正理解过一个人需要注意的所有要点,为什么std::stringconst std::string&char*

我想知道是否有人可以总结一下从 a 到 a 的转换,这是传递给 C 函数所必需的?std::stringchar*

当 是引用并且它只是非常量引用时,以及 C 函数何时更改 以及何时不会更改它时,就会出现这种情况。std::stringconstchar*

C C 字符串 字符 C++-FAQ

评论

1赞 Cray 4/12/2011
也许这会有所帮助:programmedlessons.org/AssemblyTutorial/Chapter-20/ass20_2.htmlen.wikipedia.org/wiki/C_string
0赞 sbi 4/12/2011
这实际上是一个非常好的问题,它引起了很好的答案。我们是否应该将其作为常见问题解答条目
0赞 Tony The Lion 4/12/2011
@sbi:我认为这可能是个好主意,这并不罕见,而且我敢肯定我不是第一个也是最后一个对此感到疑惑或困惑的人。

答:

2赞 ltjax 4/12/2011 #1

当 C 函数改变 后面的字符串时,您可以同时用于 const 和非 const 实例。理想情况下,它应该是一个 ,但如果不是(因为遗留的 API),您可以合法地使用 . 但是,只要您不修改字符串,您就只能使用 From 中的指针!char*std::string::c_str()std::stringconst char*const_castc_str()

当 C 函数确实更改了 后面的字符串时,您唯一安全且可移植的使用方法是自己将其复制到临时缓冲区(例如从 )!确保事后释放临时内存 -- 或使用 ,这保证具有连续内存。char*std::stringc_str()std::vector

6赞 Konrad Rudolph 4/12/2011 #2

基本上,有三点很重要:

  • 根据仍然有效的标准,实际上并不能保证使用连续存储(据我所知,这是由于更改)。但事实上,所有当前的实现都可能使用连续存储。出于这个原因,(和)实际上可能会在内部创建字符串的副本......std::stringc_str()data()

  • (和 ) 返回的指针仅在未调用原始字符串上的非常量方法时有效。这使得当 C 函数挂在指针上时(而不是仅在实际函数调用期间使用它)时,它就不合适了。c_str()data()

  • 如果字符串有可能被修改,那么从字符串中抛弃恒定性不是一个好主意。您必须使用字符串的副本创建一个缓冲区,并将其传递给 C 函数。如果创建缓冲区,请记住添加 null 终止。c_str()

评论

0赞 Sjoerd 4/12/2011
c_str()必须是恒定时间,所以不能复制。但是允许一直保留单独的副本,所以你的第二点仍然成立。
1赞 Konrad Rudolph 4/12/2011
@Sjoerd我不确定;你能指出我在标准中这样说的段落吗?§21.3.6 对函数没有任何复杂性约束。
0赞 David Rodríguez - dribeas 4/12/2011
@Sjoerd:我刚刚重新检查了标准,但我找不到运行时要求,您能否提供关于标准中需要恒定时间操作的参考?.c_str().c_str()
1赞 sbi 4/12/2011
这错过了国际海事组织非常重要的方面,即返回的寿命。.c_str()
11赞 James Kanze 4/12/2011 #3

首先,const reference 还是 value 不会改变任何内容。

然后,您必须考虑函数的期望。那里 是函数可以对 或 a --- 的原始版本,用于 例如,使用了这些类型,并且可能仍然有 这样的代码。希望它是罕见的,在以下方面, 我假设 C 函数中的 引用终止的字符串。char*char const*memcpychar*'\0'

如果 C 函数采用 ,则可以将 的结果 ;如果它需要一个 ,它 取决于。如果它只需要一个,因为它的日期来自 C的前几天,事实上,它什么也没修饰,后面跟着一个是 适当。如果 C 函数使用 作为输出 参数,但是,事情变得更加困难。我个人 首选声明一个缓冲区,传递它,然后 将结果转换为 ,但都是已知的 使用连续缓冲区的实现,以及 该标准的下一个版本将需要它,所以正确 对第一个(使用 ,然后传递,然后 将字符串重新调整为生成的长度(已确定 使用,如有必要)也可以使用。char const*std::string::c_str()char*char*conststd::string::c_str()const_castchar*char[]std::stringstd::stringstd::stringstd::string::resize()&s[0]strlen(s.c_str())

最后(但对于使用 C 程序来说,这也是一个问题),您必须考虑任何生存期问题。最 函数采用或简单地使用 指针,并忘记它,但如果函数保存指针 在某个地方,为了以后使用,字符串对象必须至少存在 只要,在此期间不应修改其大小。 (同样,在这种情况下,我更喜欢使用 .char[]char*char const*char[]

评论

0赞 Matthew 7/13/2012
+1 谢谢!对于 C 函数获取 char* 并对其进行修改的情况,我过去曾使用过“hack”,但之后我从未进行过最终调整大小。我最近遇到了一个奇怪的问题,ostringstream 尝试使用生成的(未调整大小的)字符串,它静默地禁用了字符串流。对 C 函数使用常规 char[] 现在也是我的首选方法,它更简单、更安全。&s[0]s
1赞 Tometzky 4/12/2011 #4
  1. std:string 可以存储零个字节。这意味着当传递给 C 函数时,它可能会过早被截断,因为 C 函数将在第一个零字节处停止。例如,如果您尝试使用 C 函数来过滤或转义不需要的字符,这可能会产生安全隐患。

  2. std::string::c_str() 的结果有时会因更改字符串的操作(非常量成员函数)而失效。如果在首次使用 c_str() 然后修改字符串后尝试使用此指针,则诊断错误(“Heisenbugs”)将非常困难。

  3. 永远不要使用。 就不那么麻烦了。const_castgoto

评论

1赞 Konrad Rudolph 4/12/2011
@Tony Tometzky 想强调的是,甚至比使用 .不过我不同意。 有它的位置。我还没有找到有效的用法(我已经阅读了 Knuth 论文)。gotoconst_castconst_castgoto
4赞 Boaz Yaniv 4/12/2011 #5

[我会添加一条评论,但我没有足够的代表,所以很抱歉添加(尚未)另一个答案。

虽然当前标准确实不能保证 std::string 的内部缓冲区是连续的,但似乎几乎所有实现都使用连续缓冲区。此外,新的 C++0x 标准(即将获得 ISO 批准)要求在 std::string 中使用连续的内部缓冲区,甚至当前的 C++03 标准也要求在调用 data() 或 &str[0] 时返回连续缓冲区(尽管它不一定是以 null 结尾的)。有关详细信息,请参阅此处

不过,这仍然不能使写入字符串变得安全,因为该标准不会强制实现在调用 data()、c_str() 或运算符时实际返回其内部缓冲区,并且也不会阻止它们使用写入时复制等优化,这可能会使事情进一步复杂化(看来新的 C++0x 将禁止写入时复制)。话虽如此,如果你不关心最大的可移植性,你可以检查你的目标实现,看看它在里面实际做了什么。AFAIK,Visual C++ 2008/2010 总是返回真正的内部缓冲区指针,并且不执行写入时复制(它确实具有小字符串优化,但这可能不是问题)。

评论

0赞 MSalters 4/12/2011
您能指出为什么应该引用连续缓冲区的开头吗?&str[0]
1赞 Boaz Yaniv 4/12/2011
I actually learned that first from Herb Sutter: herbsutter.com/2008/04/07/… But reading the standard, my understanding is that str[pos] is (and must be) equivalent to data()[pos], and there fore &str[0] is equivalent to &data()[0] which is, in turn, equivalent to data(). And data() itself (as well as c_str()) is required to point to a contiguous buffer.
0赞 MSalters 4/12/2011
That's weird - returns a , but returns a non-const .data()const char*operator[]char&
0赞 Boaz Yaniv 4/12/2011
@MSalters This is what the C++03 standard has to say: " 1 Returns: If pos < size(), returns data()[pos]. Otherwise, if pos == size(), the const version returns charT(). Otherwise, the behavior is undefined." ----- I guess that it doesn't take const-correctness into account for the non-const version.const_reference operator[](size_type pos) const;reference operator[](size_type pos);
0赞 MSalters 4/12/2011
Ah, right - the non-contiguous doubts were with the iterator interface, not the index interface LWG 530