字符串函数的奇怪问题

时间:2010-03-12 06:07:44

标签: c++ string

我在使用以下函数时遇到了一个奇怪的问题,该函数在某个点之后返回一个字符串,其中包含所有字符:

string after(int after, string word) {
    char temp[word.size() - after];
    cout << word.size() - after << endl; //output here is as expected
    for(int a = 0; a < (word.size() - after); a++) {
        cout << word[a + after]; //and so is this
        temp[a] = word[a + after];
        cout << temp[a]; //and this
    }
    cout << endl << temp << endl; //but output here does not always match what I want
    string returnString = temp;
    return returnString;
}

问题是,当返回的字符串是7个字符或更少时,它可以正常工作。当返回的字符串是8个字符或更多字符时,它会在预期输出结束时开始喷出废话。例如,行

cout << after(1, "12345678") << endl;
cout << after(1, "123456789") << endl;

输出:

7
22334455667788
2345678
2345678
8
2233445566778899
23456789�,�D~
23456789�,�D~

我该怎么做才能解决这个错误,是否有任何默认的C ++函数可以帮我解决这个问题?

4 个答案:

答案 0 :(得分:6)

使用std::string::substr库函数。

std::string s = "12345678";
std::cout << s.substr (1) << '\n'; // => 2345678
s = "123456789";
std::cout << s.substr (1) << '\n'; // 23456789

答案 1 :(得分:4)

如果将字符复制到字符串中但忘记在末尾添加空字符以终止字符串,则会出现您所描述的行为。尝试在循环后向末尾添加空字符,并确保为空字符分配足够的空间(多一个字符)。或者,更好的是,使用不只接受char *而且接受长度的string constructor overload

或者,甚至更好std::string::substr - 它会更容易,也可能更有效率。

string after(int after, string word) { 
  return word.substr (after);
}

顺便说一下,你不需要一个after方法,因为string类已经存在你想要的内容。

现在,要回答您关于为什么只出现在第8个及以后的字符上的具体问题,了解"C" strings如何工作非常重要。 “C”字符串是一个字节序列,由null(0)字符终止。库函数(比如用于将temp复制到带string的{​​{1}}实例中的字符串构造函数)将从第一个字符(temp [0])开始读取并将继续读取直到最后,“结束”是第一个空字符,而不是内存分配的大小。例如,如果char *长度为6个字符但是你填满了所有6个字符,那么读取该字符串到“结尾”的库函数将读取前6个字符然后继续(超过分配的结尾)内存!)直到找到空字符或程序崩溃(例如由于尝试访问无效的内存位置)。

有时候你可能会很幸运:如果temp长度为6个字符,并且在分配结束后内存中的第一个字节恰好是零,那么一切都会正常工作。但是,如果分配结束后的字节恰好是非零,那么你会看到垃圾字符。虽然它不是随机的(通常每次都会有相同的字节,因为它们被以前的方法调用操作填充,从程序的运行到运行都是一致的),但是如果你访问未初始化的内存则没有办法知道你会在那里找到什么。在bounds checking environment(例如Java或C#或C ++的字符串类)中,读取超出分配范围的尝试将引发异常。但是“C”字符串不知道它们的结局在哪里,使它们容易受到你所看到的问题的影响,或者更像buffer overflows这样的邪恶问题。

最后,你可能会问一个逻辑后续问题:为什么到了8个字节呢?由于您正在尝试访问未分配但未初始化的内存,因此RAM中的内容是该RAM的前一个用户留在那里的内容。在32位和64位计算机上,内存通常以4或8字节块的形式分配。因此,该存储器位置的先​​前用户可能在那里存储8个字节的零(例如,一个64位整数零)零。但是内存中的下一个位置与前一个用户留下了不同的东西。因此你的垃圾字符。

故事的道德:当使用“C”字符串时,要非常小心你的null终止符和缓冲区长度!

答案 2 :(得分:2)

您的字符串临时值未NULL终止。你在循环结束时要求temp[a] = '\0';。您还需要分配word.size() - after + 1个字符以容纳NULL字符。

答案 3 :(得分:0)

您不是在终止char数组。 C风格的字符串(即char数组)最后需要有一个空字符(即'\0'),因此使用它们的函数知道何时停止。

我认为这基本上是你的after()函数,模拟了一些索引的捏造:

string after(int after, string word) {
  return word.substring(after);
}