【问题标题】:Initializing a char * with an expression does not work用表达式初始化 char * 不起作用
【发布时间】:2017-01-22 06:54:38
【问题描述】:

以下代码产生了不正确的输出:

string my_string="My_First_Text";
char * my_pointer=(char *)(my_string+"My_Second_Text").c_str();

为什么?当我初始化my_pointer 时,我认为不需要my_pointer=new char[100]。如果这个假设不成立,那为什么?

【问题讨论】:

  • 你要听从你的警告。哦,我明白了,你把警告扔掉了:你现在独自一人,这看起来不是一件好事。
  • 顺便说一句,代码不会产生任何输出,更不用说任何不正确的输出了。
  • This is what the compiler told you。但是由于某种原因,您认为您比编译器更了解并抛弃了错误。
  • @PeterA.Schneider, @Paul:禁用的警告与问题无关。将const添加到第二行并没有更多警告,但问题仍然存在。
  • @Ben 你是对的。我认为所有答案都指出了造成问题的临时问题。

标签: c++ string pointers temporary-objects


【解决方案1】:

注意my_string+"My_Second_Text"是一个临时的std::string,在表达式后会立即销毁。这意味着my_pointer 将立即悬空,因为它应该指向的 char 数组已与临时 std::string 的销毁一起被销毁;请注意,返回的 char 数组属于 std::string,它不是独立的。然后,对悬空指针的尊重将导致 UB。

string my_string="My_First_Text";
char * my_pointer=(char *)(my_string+"My_Second_Text").c_str();
// the temporary constructed from my_string+"My_Second_Text" has been destroyed
// my_pointer is dangled now; deference on it is UB

使用命名变量而不是临时变量会很好。例如

string my_string = "My_First_Text";
my_string += "My_Second_Text";
const char * my_pointer = my_string.c_str();

BTW:std::basic_string::c_str 的返回类型是const char*,任何修改都是UB。所以试图将其显式转换为char* 是危险的。

写入通过c_str() 访问的字符数组是未定义的行为。

【讨论】:

  • 如果对象确实是 const(字符串的 char 数组显然不是,因为它是动态分配的),则修改指向 const 的指针指向的存储只是 UB。 C++ 2012 标准,7.1.6.1:int i=2; const int *cip; cip = &i; int* ip; ip = const_cast<int*>(cip); *ip = 4; 很好。
  • @PeterA.Schneider 这里的情况比较复杂,涉及到std::string。无论如何,std::basic_string::c_str 表示不应修改返回的数组。 “写入通过 c_str() 访问的字符数组是未定义的行为。”
  • 真(标准 21.4.7.1)。但原因不是 constness ;-)。
  • @songyuanyao 我完全理解我的错误,现在我知道如何以正确的方式解决它。但是我也可以通过使用const char * my_pointer 而不是char* my_pointer 来解决这个问题吗?换句话说,通过使用const char * my_pointer,我可以强制编译器将my_string+"My_Second_Text" 的结果存储在某处并且不要在; 的末尾销毁它吗?
  • @Admia 不,因为my_pointer 会随着临时std::string 的销毁而变得悬空,所以它仍然是UB。 (UB 意味着,即使它在某些情况下看起来工作得很好,但你根本不能依赖它。它只是 C++ 世界中的怪物。)
【解决方案2】:

除了将c_str (const char*) 转换为char*(这不是一个好主意)之外,“my_pointer”使用临时初始化,并在计算表达式后被破坏。意思是,就在最后一个';'之后在您的代码中。
这意味着 my_pointer 指向的内存不再有效,并且会产生意想不到的结果。

【讨论】:

    【解决方案3】:

    (my_string+"My_Second_Text").c_str() 是一个临时值,将在程序运行时动态创建和销毁,而不会保存在内存中。

    指向它会导致未定义的行为,因为指向的内存没有定义。使用strcpy 或变量赋值代替临时值将字符串赋值给char *

    【讨论】:

      【解决方案4】:

      临时对象可能会导致难以理解的问题。

      Bjarne 的 C++ 书籍的相关摘录:

      void f(string& s1, string& s2, string& s3)
      {
        const char∗ cs = (s1+s2).c_str();  // << Code similar to OP's example
        cout << cs;
        if (strlen(cs=(s2+s3).c_str())<8 && cs[0]=='a') {
        // cs used here
        }
      }
      

      你的第一反应可能是“但不要那样做!”我同意。 但是,确实会编写此类代码,因此值得了解它是如何编写的 解释。

      创建一个临时字符串对象来保存s1 + s2。下一个, 从该对象中提取指向 C 样式字符串的指针。然后——在 表达式的结尾——临时对象被删除。然而, c_str() 返回的 C 风格字符串被分配为 临时对象持有s1 + s2,并且不保证存储 在那个临时被破坏之后存在。因此, cs 指向 释放的存储空间。

      输出操作cout &lt;&lt; cs 可以作为 预料之中,但那将是纯粹的运气。编译器可以检测并警告 针对这个问题的许多变体。

      附带说明,在 C++ 中使用适当的转换而不是 C 风格的转换。阅读

      When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?

      【讨论】:

      • 你应该渲染“但不要那样做!”粗体、大字体、斜体。
      • 这里不需要 const 演员表吗?
      • 是的@Peter,需要 const_cast 。概括了我对选角的回答。
      • @SauravSahu 我为我的GNU g++ 编译器设置了-W -Wall 选项。但是编译器并没有警告我这个错误。
      【解决方案5】:

      您的代码具有未定义的行为。 + 运算符返回一个新的临时字符串对象,该对象将在 (my_string+"My_Second_Text").c_str() 表达式之外被销毁,因此对该字符串的任何引用都将悬空,并且通过它们进行访问具有未定义的行为。

      c_str() 返回一个指向string 的内部字符数组的 const 指针。您不能也不能通过该指针操纵string

      (my_string+"My_Second_Text") 的结果存储在新变量中或使用append 函数将新字符串附加到现有字符串对象中。

      std::string new_string = my_string + "My_Second_Text";
      const char* char_pointer = new_string.c_str();
      
      my_string.append("My_Second_Text");
      const char* char_pointer = my_string.c_str();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-26
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-09
        相关资源
        最近更新 更多