【问题标题】:Passing the results of `std::string::c_str()` to `mkdtemp()` using `const_cast<char*>()`使用 `const_cast<char*>()` 将 `std::string::c_str()` 的结果传递给 `mkdtemp()`
【发布时间】:2016-03-11 16:02:13
【问题描述】:

好的,所以:我们都知道通常在任何地方使用const_cast&lt;&gt;() 非常糟糕,这实际上是一种编程战争罪。所以这是一个假设性问题,关于在特定情况下可能会有多糟糕。

也就是说:我遇到了一些类似这样的代码:

std::string temporary = "/tmp/directory-XXXXX";
const char* dtemp = ::mkdtemp(const_cast<char*>(temporary.c_str()));
/// `temporary` is unused hereafter

...现在,我遇到了很多关于如何获得对 std::string 实例的底层缓冲区的可写访问的描述(例如,qv https://stackoverflow.com/a/15863513/298171)——它们都有一个警告,是的,这些方法不是t 保证可以通过任何 C++ 标准工作,但实际上它们都可以。

考虑到这一点,我只是好奇使用const_cast&lt;char*&gt;(string.c_str()) 与其他已知方法(例如前面提到的&amp;string[0],&c)的比较...我问是因为我发现使用此方法的代码似乎有效在实践中很好,我想在尝试不可避免的const_cast&lt;&gt;()-free 重写之前,我会先看看专家的想法。

【问题讨论】:

  • 使用std::vector&lt;char&gt; 而不是std::string。您拥有没有所有转换的可写缓冲区。
  • 不是我要问的——我可以想出很多没有问题的方法。我很好奇是什么造成了问题的本质。
  • 顺便说一句:正确的实现是这样的:char temporary[] = "/tmp/directory-XXXXX"; char* dtemp = mkdtemp(temporary); 因为这会在堆栈上创建一个数组,该数组是用字符串文字的副本初始化的,所以您有权修改该副本,包括将其传递给mkdtemp()。没错,这里没有使用闪亮的 C++ 东西,但它比您遇到的版本更短、更简单、更正确:-)
  • @cmaster 绝对是的——虽然这会更明智并且不那么迂回,但我发布的 sn-p 是对实际生产代码的严重过度简化,这自然是够混乱的使这样一个直截了当的编辑变得不直截了当。但是,您确实正确地将静态分配给可写的基于堆栈的临时对象作为mkdtemp() 使用的理想素材。

标签: c++ stdstring mutable c-strings const-cast


【解决方案1】:
  • const 无法在硬件级别强制执行,因为在实践中,在非假设环境中,您只能将只读属性设置为完整的 4K 内存页面,并且会有大量页面在路上,这极大地减少 CPU 在TLB 中的查找缺失。

  • const 不会像 C99 中的 __restrict 那样影响代码生成。事实上,const,粗略地说,意思是“毒化所有对该数据的写入尝试,我想在这里保护我的不变量”

由于std::string 是一个可变字符串,它的底层缓冲区不能分配在只读内存中。所以const_cast&lt;&gt; 不应该在这里导致程序崩溃,除非您要更改底层缓冲区边界之外的一些字节或尝试deletefree()realloc() 某些东西。但是,缓冲区中字符的更改可能被归类为不变违规。因为在那之后您不使用 std::string 实例并且只是将其丢弃,所以这不应该引发程序崩溃,除非某些特定的 std::string 实现决定在销毁之前检查其不变量的完整性并在其中一些被破坏时强制崩溃.因为这样的检查不能在少于 O(N) 的时间内完成,而且std::string 是一个性能关键类,所以任何人都不太可能完成。

另一个问题可能来自Copy-on-Write 策略。因此,通过直接修改缓冲区,您可能会破坏其他一些与您的字符串共享缓冲区的std::string 实例。但几年前,大多数 C++ 专家得出结论,COW 太脆弱且太慢,尤其是在多线程环境中,因此现代 C++ 库不应使用它,而是尽可能坚持使用移动构造并避免堆流量小长度字符串(如果适用)。

【讨论】:

  • 嗯,我认为这有点误导。不允许丢弃const然后通过该指针写入缓冲区。你只能阅读。要获得对字符串内容的写入权限,请使用&amp;s[0]
  • @fish2000 它会导致未定义的行为,其中可能包括看起来按预期工作。或者换一种说法,编译器可能支持也可能不支持(如果不支持,您不会收到任何警告)
  • a) const 不能在硬件级别强制执行 b) const 不会像 C99 中的 __restrict 那样影响代码生成。事实上,const,粗略地说,意思是“毒化所有对这些东西的写尝试,这有望保护我的不变量”。
  • 关于检查析构函数中的不变量:std::string::size() 被定义为O(1) 复杂度,因此所有实现都需要保留大小或指向字符串末尾的指针。该信息足以在恒定时间内对字符串末尾的空字节进行完整性检查。因此,如果终止字节被覆盖,实现可能会强制崩溃。
  • @fish2000 我是这样看的:尾随 NUL 字节由实现隐式放置在那里,并且不能被合法的用户代码删除。因此,如果NUL 字节被覆盖,则程序已经进入了UB。在这种情况下,中止该过程是完全允许的做法,而且检查很便宜。诚然,这几乎是语言律师的观点,但我得出的结论是,在涉及 UB 时,最好听取语言律师的意见。有太多情况下,过去运行良好的代码因为编译器证明是 UB 而惨遭破坏。
猜你喜欢
  • 1970-01-01
  • 2013-08-31
  • 2015-09-26
  • 2013-09-21
  • 2011-09-21
  • 2020-12-11
  • 1970-01-01
相关资源
最近更新 更多