依赖于空std::string的索引0是不好的吗?

50
std::string my_string = "";
char test = my_string[0];

我注意到这个程序不会崩溃,在我的所有测试中test的值都为0。

我能否信赖它始终为0?还是说这是随意的?

这是糟糕的编程吗?

编辑: 从某些评论中,我了解到有些人对此的用处存在误解。

它的目的不是为了检查字符串是否为空。 而是不需要检查字符串是否为空。

情况是有一个可能为空的字符串。 我只关心这个字符串的第一个字符(如果它不为空)。

在我看来,检查字符串是否为空,然后再查看第一个字符,效率会更低。

if (! my_string.empty())
    test = my_string[0];
else
    test = 0;

相反,我可以只查看第一个字符,而不需要检查字符串是否为空。

test = my_string[0];

24
请使用 std::string::empty - 101010
4
你可能在使用一份古老的参考资料,该参考资料给出了C++98版的规则,或者是查询了错误的函数。 - Ben Voigt
1
“the” 在线参考资料是什么,@Logicrat? - Lightness Races in Orbit
14
注:一个以0为首字符的字符串并不一定是空字符串! - user1084944
3
@Ahmed: std::string x = "123"; x[0] = 0; assert(x[0] == 0 && !x.empty());这段代码创建了一个名为“x”的字符串变量,其值为“123”。然后,将字符串中的第一个字符更改为ASCII码为0的空字符。最后,使用assert函数检查第一个字符是否为0且字符串不为空。 - user1084944
显示剩余5条评论
2个回答

71

C++14

不会的,你可以依赖它。

21.4.5.2 (或[string.access])中,我们可以找到:

如果 pos < size(),则返回:*(begin() + pos)。否则,返回一个类型为charT且值为charT()的对象引用,修改该对象会导致未定义行为。

换句话说,当pos == size()时(即两者都为0时),该运算符将返回对一个默认构造的字符类型的引用,禁止您修改

它没有针对空的(或0大小的)字符串进行特殊处理,并且对于每个长度都是相同的。


C++03

并且最有可能 C++98 也是如此。

情况各异。

这里是来自官方ISO / IEC 14882的21.3.4.1

如果 pos < size(),则返回 data()[pos]。否则,如果pos == size(),则const版本返回charT()。否则,行为是未定义的。


6
请注意,在 C++11 之前,非 const 版本的 operator[] 在这种情况下将导致未定义行为(即使您不修改生成的引用)。 - interjay
这是一个很棒的回答。你能提供一下你的信息来源吗? - Benilda Key
1
@BenKey:这是C++标准自身使用的编号系统。 - Ben Voigt
2
@BenKey 对于第一份报价,我使用了一个非常有用的在线渲染(我认为)最新的C++14草案。对于第二份报价,它是原始的ISO PDF文件。如Ben V.所指出的那样,两个数字代表该文件中的章节。 - Bartek Banachewicz
1
是的,但在这里我们正在查看 pos == size()(两者都等于0)。 - Toby Speight
显示剩余4条评论

33

@Bartek Banachewicz的答案解释了哪些情况下可以做出您的假设。我想补充说

这是糟糕的编程。

为什么?有几个原因:

  1. 你必须成为一名语言律师才能确定这不是一个错误。如果没有这个页面,我不知道答案,坦白地说 - 我认为你真的不应该费心去知道。
  2. 没有字符串作为以空字符结尾的字符序列的直觉的人将不知道您试图做什么,直到他们阅读标准或询问他们的朋友。
  3. 以不好的方式违反了最小惊讶原则
  4. 违背了“写出你的意思”的原则,即让代码表达问题域概念。
  5. 有点像使用魔数(在这种情况下,0是否构成魔数是有争议的)。

我应该继续吗?... 我几乎可以肯定在几乎每一个方面你都有更好的选择。我甚至猜测你做过其他"坏事",以便让自己想要这样做。

永远记住:其他人,他们不会来向你咨询,迟早需要维护这段代码。考虑他们,不要只考虑自己能够理解。此外,十年后,谁能保证你还记得自己的技巧呢?你可能会成为那个困惑的维护者...


3
这并不是糟糕的编程。这种行为已经被定义好了,就像在许多语言中 s[-2] 返回字符串中倒数第二个字符一样。是的,可能有些 C++ 程序员不知道这种行为已经被定义好了,需要加上注释。但如果 s[0] 已经足够,我不会再添加任何代码。 - kevin cline
3
@kevincline:事物被定义并不意味着它必须被使用。实际上,对于边缘情况需要特定定义的行为,而且本可以有所不同,往往最好避免采用。此外,简洁很好,但是:1.还有其他方法可以实现它;2.您仍然必须平衡简洁和清晰度,不能仅仅为前者而牺牲后者。 - einpoklum
@kevincline:s[0]或s[-2]都没问题,这不是问题所在。问题在于在空字符串上使用s[0]、s[-2]或其他什么。这很令人困惑:如果你知道字符串为空,为什么还要尝试访问它的字符?干脆别访问。 - einpoklum
3
某些东西并不因为一个人或几个人使用它而变得习惯用语。此外,我认为一个好的习惯用语最初不会令人困惑。 - einpoklum
4
定义行为或非定义行为:如果我看到类似于原帖中前两行的代码,我会感到困惑。请记住,即使在 C++14 中,您也绝不能修改返回的引用,否则将再次面临未定义的行为。这绝对是不好的、糟糕的风格。 - TobiMcNamobi
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接