【问题标题】:C++ const cast, unsure if this is secureC++ const cast,不确定这是否安全
【发布时间】:2012-05-04 20:20:13
【问题描述】:

这似乎是一个愚蠢的问题,但我真的需要澄清这一点:

这会给我的程序带来任何危险吗?

const_cast 还需要吗?

如果我更改输入指针值,它会安全地使用std::string,还是会产生未定义的行为?

目前唯一担心的是,每当我修改输入指针并使其无法使用时,这可能会影响字符串“some_text”。

std::string some_text = "Text with some input";

char * input = const_cast<char*>(some_text.c_str());

谢谢你给我一些提示,我想避免拍到自己的脚

【问题讨论】:

  • 一般来说,由于const_cast 消除了安全性,你应该尽可能避免它。
  • 感谢大家的贡献,我现在明白了,为了安全起见,我应该避免这种情况

标签: c++ const-cast


【解决方案1】:

它是 UB。 例如,您可以这样做:

size_t const size = (sizeof(int) == 4 ? 1024 : 2048);
int arr[size];

没有任何强制转换,编译器不会报告错误。但是这个代码是非法的。 士气是您每次都需要考虑采取行动。

【讨论】:

    【解决方案2】:

    作为一个邪恶行为的例子:与 gcc 的 Copy On Write 实现的交互。

    #include <string>
    #include <iostream>
    
    int main() {
        std::string const original = "Hello, World!";
        std::string copy = original;
    
        char* c = const_cast<char*>(copy.c_str());
        c[0] = 'J';
    
        std::cout << original << "\n";
    }
    

    ideone 正在行动。

    果冻,世界!

    问题?顾名思义,gcc 的std::string 实现在底层使用了一个引用计数的共享缓冲区。当一个字符串被修改时,实现会巧妙地检查当前缓冲区是否被共享,如果是,则在修改之前复制它,确保共享该缓冲区的其他字符串不受新写入的影响(因此名称,写时复制)。

    现在,使用你的邪恶程序,你通过 const 方法访问共享缓冲区(承诺不修改任何内容),但你确实修改了它!

    请注意,对于不使用写时复制的 MSVC 实现,行为会有所不同("Hello, World!" 将被正确打印)。

    这正是未定义行为的本质。

    【讨论】:

    • 未定义行为的本质:你的世界变成了果冻
    【解决方案3】:

    另一个原因是它使代码极难维护。举个例子:几年前,我不得不重构一些包含长函数的代码。作者编写了函数签名来接受 const 参数,但随后在函数中 const_casting 以删除 const 性。这破坏了函数给出的隐含保证,使得在代码的其余部分中很难知道参数是否已更改。

    简而言之,如果您可以控制字符串并且您认为需要更改它,请首先将其设为非 const。如果您不这样做,那么您将不得不复制并使用它。

    【讨论】:

      【解决方案4】:

      这是一件非常糟糕的事情。查看什么 std::string::c_str() does 并同意我的看法。

      其次,考虑一下为什么要对 std::string 的内部进行非常量访问。显然您想修改内容,否则您将使用 const char 指针。您还担心您不想更改原始字符串。为什么不写

      std::string input( some_text );
      

      然后你就有了一个 std::string ,你可以在不影响原始文件的情况下搞乱它,并且你有 std::string 功能,而不必使用原始 C++ 指针...

      【讨论】:

      • 如果 OP 需要 char*,这样做是不好的,因为新字符串与原始字符串存在完全相同的问题!
      【解决方案5】:

      简单的强制转换不会产生未定义的行为。然而,修改指向的数据将会。 (Also see ISO 14882:98 5.2.7-7)。

      如果你想要一个指向可修改数据的指针,你可以有一个

      std::vector<char> wtf(str.begin(), str.end());
      char* lol= &wtf[0];
      

      【讨论】:

      • 除了现在,你有一个非空终止的字符串。 char* c = str.c_str(); std::vector&lt;char&gt; foo(c, c + str.size()+1); 所没有的
      【解决方案6】:

      C++ reference网站:

      const char* c_str ( ) const;
      

      "生成一个以空字符结尾的字符序列(c-string),其内容与字符串对象相同,并将其作为指向字符数组的指针返回。

      自动添加终止空字符。

      返回的数组指向一个内部位置,该位置具有该字符序列所需的存储空间以及它的终止空字符,但该数组中的值不应在程序中修改,并且只能保证在下一次调用字符串对象的非常量成员函数。”

      【讨论】:

        【解决方案7】:

        std::string 在内部管理自己的内存,这就是它直接返回指向该内存的指针的原因,就像使用c_str() 函数一样。它确保它是恒定的,以便您的编译器在您尝试修改它时会警告您。

        以这种方式使用 const_cast 从字面上消除了这种安全性,并且如果您绝对确定内存不会被修改,那么这只是一种可以接受的做法。

        如果你不能保证这一点,那么你必须复制字符串并使用副本。;无论如何,这样做肯定更安全(您可以使用strcpy)。

        【讨论】:

          【解决方案8】:

          是的,会带来危险,因为

          1. input 指向任何 c_str 现在恰好是,但如果 some_text 改变或消失,你将留下一个指向垃圾的指针。只要字符串没有改变,c_str 的值就保证是有效的。甚至,正式地,前提是您也不要在其他字符串上调用 c_str()
          2. 为什么需要抛弃 const?你不打算写信给*input,是吗?这是一个禁忌!

          【讨论】:

          • 这实际上正是我想要做的,修改字符串(例如删除重复字符)我最大的问题是它实际上让我编译和运行它,但正是这让我打开了这个发布,因为将 const 扔掉似乎不合逻辑,之后我能够正确编写和修改它(很可能它已经向南但它对我不可见)
          • @OliverStutz 删除重复字符之类的事情可以通过调用内置的std::string 函数来完成。但如果你更喜欢旧的 C 函数,就一直使用旧的 C 并先创建一个 strcpy
          【解决方案9】:

          通过使用const_cast 抛弃其常量来修改固有的const 对象是一种未定义行为

          string::c_str() 返回const char *,即:指向常量 c 样式字符串的指针。从技术上讲,修改它会导致未定义的行为。

          注意,const_cast 的使用是当您有一个指向非常量数据的 const 指针并且您希望修改非常量数据时。

          【讨论】:

          • 我还没有找到一个可以在没有 const 的情况下执行 c_str() 操作的字符串选项,有没有首选的替代方法?
          • 可能是c_str 返回一个指向char 数组的指针,而不是const char。所以形式上你可以说的是,如果程序通过input修改对象,你不能保证程序是正确的,不能保证程序是不正确的。
          • @OliverStutz:您必须有另一个充分分配的(数组或指针)变量将字符串复制到它,然后修改复制到另一个变量的字符串。
          猜你喜欢
          • 2014-03-12
          • 1970-01-01
          • 1970-01-01
          • 2014-03-07
          • 1970-01-01
          • 2022-06-10
          • 2012-02-08
          • 2012-12-17
          相关资源
          最近更新 更多