【问题标题】:Why does a C++ pointer to char act as a character string despite the lack of a null terminator?尽管缺少空终止符,为什么指向 char 的 C++ 指针仍充当字符串?
【发布时间】:2016-06-13 07:18:36
【问题描述】:

据我了解,C 风格的字符串,即使在 C++ 中使用而不是字符串类,也需要一个空终止字符:

This is a string.\0

据我了解,缺少空字符会导致程序继续读取内存中字符串之后的任何内容,直到找到空字符的二进制表示。这显然是未定义的行为。

在编写 dtoi 函数时(我想自己编写这个函数作为我正在做的不同实践项目的一部分进行实践——我知道已经有图书馆设施可以做到这一点),我发现了不同的行为(特别是在创建invalid_argument 异常的字符串)。

int dtoi(const char d){
    switch(d){ //using switch statement rather than d-'0' to support character sets with non-consecutive digits or digits that go from 9 to 0 rather than 0 to 9
        case '0':
            return 0;
        case '1':
            return 1;
        case '2':
            return 2;
        case '3':
            return 3;
        case '4':
            return 4;
        case '5':
            return 5;
        case '6':
            return 6;
        case '7':
            return 7;
        case '8':
            return 8;
        case '9':
            return 9;
        default:
            throw invalid_argument(((d == '\0') ? "null character" : &d) + string(" is not a valid digit character."));
    }
}

由于异常字符串开头的空字符导致它在第一个字符处结束(即,当空字符被传递给我的 dtoi 实现时),我决定让它显示文本“空字符”如果它是'\0',而不是直接插入字符。为了实现这一点,我使用了条件运算符。我不能使用(d == '\0') ? "null character" : d(注意它在结尾处是d而不是&d),因为条件运算符可能会返回指向字符串文字中第一个字符的指针或直接返回一个字符。为了看看发生了什么,我决定尝试&d,令我惊讶的是,它打印了在 exception.what() 中传递给函数的任何字符。我希望它提供一个指向传递字符的指针,但是,然后继续读入随机内存,直到找到一个空字符。我尝试了多次,并传入了多个不同的字符。为什么它的行为是这样的?我真的认为它是未定义的行为是正确的,它恰好在这里按预期工作吗?

【问题讨论】:

  • 因为未定义的行为。
  • 致任何投反对票的人:我不确定您为什么要这样做——这似乎是一个有效的问题。我在问一个特定的动作是否实际上是未定义的行为,或者我是否误解了 C++ 的工作原理。如果这在某种程度上无效,请告诉我而不是投反对票。
  • @juanchopanza 这听起来很像一个答案。
  • "不连续数字的字符集或从 9 到 0 而不是从 0 到 9 的数字" 没有这样的东西。
  • 好吧,这并不可怕,但它过于冗长。您可以将 20 行替换为 2:if ( d >= '0' && d <= '9' ) return d - '0';(带换行符)。我想大多数人更愿意阅读后者

标签: c++ c arrays string pointers


【解决方案1】:

关于 C 风格的字符串,您需要了解一件基本的事情,我们表示它们的方式(在末尾带有 '\0' 的字符数组)只是一种约定,没有类型(在 C 中)字符串。这意味着,从语言的角度来看,指向单个 char 的指针和指向 char 数组(可能是字符串)开头的指针之间没有区别。所以它是一个使用这样一个指针的函数来以一种好的方式解释它,并且对于那个文档是你的朋友。

由于您使用的是 C++,我强烈建议您仅使用 std::string 并保留 C 样式字符串以向后兼容 C 库,这样可以避免很多问题。

【讨论】:

    【解决方案2】:

    This operator+ 在这里使用(指向单个 char 的指针不是以空值终止的,这并不合适)。是的,绝对是undefined behavior

    lhs - 字符串、字符或指向空终止数组中第一个字符的指针

    只需将std::string 设为常用类型即可修复它:

    ((d == '\0') ? std::string("null character") : std::string(1, d))
    

    并且不要形成这样的switch-case 语句。

    【讨论】:

    • 感谢您的回答。不过我有一个问题,我的开关盒有什么问题?仅仅是因为标准将0-9定义为保证吗?如果是这样,我应该如何实现无效输入的异常?
    • @john01dav T.C. M.M解释道。 d - '0' 和一些 if-statements 会很好。
    【解决方案3】:

    是的,这是未定义的行为,因为您读取的是随机内存。你应该做什么而不是整个凌乱的开关和?: 运算符是这样的:

    #include <cctype>
    
    if(!std::isdigit(d))
    {
      std::string err_str;
    
      if(d == '\0')
      {
        err_str = "Null character";
      }
      else
      {
        err_str = std::string(1, d);
      }
      err_str += " is not a valid digit character.";
    
      throw invalid_argument(err_str);
    }
    
    return (int)d - '0';
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-12
      • 2015-05-19
      • 1970-01-01
      • 1970-01-01
      • 2020-06-02
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      相关资源
      最近更新 更多