我正在用C ++写一个函数,从理论上讲,它应该接受用户输入,并根据空格将这些输入拼接为段,并将这些段作为向量返回。
我目前正在做的是在输入字符串上使用strtok()以便用空格分隔单词。对于每个“单词”,我将其推入缓冲区向量。遍历每个单词后,我返回向量。
这是我到目前为止的代码:
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
std::vector<char*> tokenize(std::string input_, char const* delims=" \t\r\n\a")
{
char* input = (char*)input_.c_str();
std::vector<char*> tk_stream;
char* tk = strtok(input, delims);
while(tk != NULL) {
tk_stream.push_back(tk);
tk = strtok(NULL, delims);
}
return tk_stream;
}
int main(int argc, char** argv)
{
while (true) {
std::string input;
std::getline(std::cin, input);
if (input.empty()) {
continue;
}
std::vector<char*> tks = tokenize(input);
for (char* el : tks) {
std::cout << el << std::endl;
}
}
return 0;
}
那应该发生什么?好吧,如果我输入的是“ 1 2 3 4”,它应该在单独的行上打印每个数字。这实际上适用于该输入。但是,当输入字符串的长度较大时,例如“ 1 2 3 4 5 6 7 8 9”,输出将不同:
1 2 3 4 5 6 7 8 9
5
6
7
8
9
它缺少前4个数字!任何长度大于此长度的字符串也会发生这种情况,并且丢失数字的数量是恒定的。我还注意到较长的句子会发生这种情况。例如,“大家好,这是一个测试”给出:
hello everyone this is a test
0��
this
is
a
test
我已经对gdb进行了一些挖掘,发现了一些有趣的东西。输入“ 1 2 3 4 5 6 7 8 9”后,我在返回“ tk_stream”之前设置了一个断点,并检查了它的值:
(gdb) print tk_stream
$1 = std::vector of length 9, capacity 16 = {0x6176c0 "1", 0x6176c2 "2", 0x6176c4 "3", 0x6176c6 "4", 0x6176c8 "5", 0x6176ca "6", 0x6176cc "7", 0x6176ce "8", 0x6176d0 "9"}
这似乎是正确的。但是,当我从函数返回几行后,检查“ tks”的值(应包含“ tokenize”函数的返回值的向量);我收到这个:
(gdb) print tks
$2 = std::vector of length 9, capacity 16 = {0x6176c0 "", 0x6176c2 "a", 0x6176c4 "", 0x6176c6 "", 0x6176c8 "5", 0x6176ca "6", 0x6176cc "7", 0x6176ce "8", 0x6176d0 "9"}
缺少前4个条目,而第2个条目混乱。
因此,'tk_stream'向量的返回中必须发生某些事情。
这种异常行为的原因是什么?如何解决此问题,以便不删除向量的任何元素?
答案 0 :(得分:3)
您不想使用char*
之类的原始指针,而应使用std::string
。
类似的东西:
std::vector<std::string> tokenize(std::string input_, const std:string delims=" \t\r\n\a")
{
std::string input = input_;
std::vector<std::string> tk_stream;
// ...
答案 1 :(得分:1)
您正在按值将字符串传递到令牌化函数中。然后,您在该本地字符串对象上调用c_str()并将指针存储到该空间中的向量中。然后,您的函数将返回,并随之存储在本地字符串对象中。现在,这意味着您存储在向量中的所有指针现在都是悬空指针。抵制其中的任何一种都是未定义的行为。
由于所谓的“短字符串优化”,它对于短字符串(可能长度小于16个字符)“似乎可以工作”。 std :: string的许多实现在std :: string对象本身内部都有一个较小的缓冲区(常见大小为16个字节,但标准未定义)。一旦字符串变得更长,std :: string将动态分配一个缓冲区来保存字符串。当使用短字符串时,悬空的指针指向堆栈,并且您的数据尚未被覆盖。使用长字符串时,指针指向内存中的任意位置,该位置可能已被其他内容覆盖。
哦,要修复,请通过引用传递您的std :: string:const std::string & input_
。