【问题标题】:Help regarding a core dump problem involving string class关于涉及字符串类的核心转储问题的帮助
【发布时间】:2011-03-11 10:01:42
【问题描述】:

我正在执行代码审查以解决一个很少发生的实时环境问题。它在调试环境中不可重现,因此唯一的调查手段是来自实时环境和代码分析的核心转储。以下是情况摘要:
核心转储:

(gdb) bt
#0 in strlen () from /lib/libc.so.6
#1 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string$base () from libstdc++.so.6
#2 in CustomStr::CustomStr()

代码在 Std::String 类之上有一个包装类,类似于:

class CustomStr: public string
{
    //Some custom members here

};

This custom class has constructors:
CustomStr::CustomStr(const char *str):string(str)
{
   //Some derived class inits
}

CustomStr::CustomStr(const CustomStr& str) : string(str.c_str())
{
    //Some derived class inits   
}

我认为这两个构造函数都有问题,如果传递一个指向 NULL 的指针,同样会传递给 String 构造函数,当它在内部调用 strlen() 来确定长度时,会发生未定义的行为(UB)。 我认为正确的实现方法是在调用字符串构造函数之前检查 NULL,例如:

CustomStr::CustomStr(const char *str)
{
   if(str!= NULL)
       string(str);
   //Some derived class inits
}

CustomStr::CustomStr(const CustomStr& str)
{
    if(str!= NULL)
       string(str.c_str());
    //Some derived class inits   
}

我的问题是:

  1. 问题(我认为是)和建议的解决方案看起来是否有效?
  2. 字符串构造函数是否检查 NULL?我认为这应该是因为它在内部调用 strlen() 它将在 NULL 上显示 UB。
  3. 除了 NULL 检查之外,如何检查是否通过了有效的 const char*?(NOn NULL 终止的 const char* 等)

【问题讨论】:

    标签: c++ string undefined-behavior


    【解决方案1】:

    const char* 的构造函数不检查空指针,如果需要,由调用者检查。您必须知道参数是否可能为空。

    另一个构造函数可以使用字符串的复制构造函数,而不是将 c_str() 传递给它。它既可以使您免于重新计算长度,也可以用于空字符串。

    【讨论】:

      【解决方案2】:

      C++ 标准要求传递给std::string(const char*) 的指针不能是空指针(c++0x 草案 n3092 中的第 21.4.2 节)。所以字符串类本身不会检查那个条件,传入一个 null 意味着你的代码不符合。

      CustomStr(const char*) 的修复看起来不错,但 CustomStr(const std::string&amp;) 的修复不是。引用不能为空。 (如果编译完成,我会感到惊讶。)

      现在有一种方法可以检查随机 char* 是否为“有效”,但 null 检查除外。

      【讨论】:

        【解决方案3】:

        对核心转储的进一步调查应该可以让您非常准确地了解导致崩溃的原因。特别是,究竟抛出了什么异常?如果它是访问冲突(又名 segfault),那么转储还应该准确地告诉您正在访问的错误地址,这反过来会告诉您您的问题是从 NULL 指针读取还是其他什么。

        指向std::string 构造函数的NULL 指针很可能是您的问题,但您不应该只是假设——核心转储的目的是消除此类调试中的猜测。

        在任何情况下,是的,std::string 如果你从 NULL 指针构造它就会爆炸,因此你应该在将任何指针传递给字符串构造函数之前对它们进行 NULL 检查。 例如,

        class CustomStr : public string
        {
          CustomStr(const char *str) : string( str ? str : "<INVALID>" ) { .. }  
        };
        

        将是一种简单的解决方法。

        【讨论】:

        • 这背后的想法是,我从来没有传递过空指针:-),不应该从检查中获得性能损失。你不能把它强加给每个人,只是为了拯救一些新手。如果需要检查,可以在调用构造函数时自己添加。
        • 为了好奇,我上周就核心转储做了一个一小时的会议讲座:bit.ly/hPCmVW
        • @Bo Persson:当然,您可以将检查设置为仅用于调试的断言,或者只是解雇传递 NULL 指针的人,等等。重要的是查看转储并确保这确实是问题所在,而不仅仅是假设。 (虽然老实说——我说这是我团队的官方 Perf Nazi——在这种情况下进行 NULL 检查的性能损失非常小,即使在 RISC 芯片上也是如此,因为你可以将其作为无分支条件移动来执行大约三个流水线周期。)
        • 是的,但是如果您向 std::string 构造函数添加三个指令,那么有人会发布一个基准,“证明”C++ 本质上比 C 慢。:-)
        • @Bo Persson:我很清楚会这样做的人。不过,再一次,如果您使用的是std::string,那么您已经输掉了性能战。 =P
        【解决方案4】:

        std::string(const char*) 的参数应该是一个指向以 \0 结尾的字符串的指针。 NULL 不指向任何东西,当然也不是以空字符结尾的字符串。因此,传递 NULL 是违反先决条件的。

        调试版本应该断言指针不为 NULL,因为std::string 是一个新手会使用的类。它不应该在运行时检查,因为没有有效的程序传递 NULL。

        像往常一样,在 C++ 中,没有办法测试指针是否“有效”。如果一个可以回答这个问题的机制是强制性的,那么 C++ 将招致显着的性能损失。

        【讨论】:

          【解决方案5】:

          您不应该从标准库类派生,它们没有虚拟析构函数,它们不应该是基类。

          也许这不是您遇到的问题,但很高兴知道。不要这样做。

          您可以将 std::string 作为包装类的组合。或者,正如我所发现的,对于 std::string 类根本不使用任何包装器,这会导致更多问题而不是解决一些问题。

          如果您需要一些特定功能,例如“修剪”等,您可以使用一些外部功能。

          很高兴知道在boost库中存在很多字符串操作函数。

          【讨论】:

          • 同意从容器类派生通常不是一个好主意,但非虚拟析构函数是一个问题只有如果您通过指向的指针删除 CustomStr标准::字符串。低风险,是我的猜测。
          猜你喜欢
          • 1970-01-01
          • 2011-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多