【问题标题】:what(): basic_string::_M_construct null not validwhat(): basic_string::_M_construct null 无效
【发布时间】:2020-10-15 15:31:46
【问题描述】:

我正在制作一个程序,我需要使用一个函数,该函数将字符串的标记存储在向量中。该功能无法正常工作,因此我在较小的程序上尝试了该功能。当然,我使用了字符串标记器功能。但它没有按预期工作。首先,这是代码:

#include 
#include 
#include 
使用命名空间标准;

主函数()
{
    向量 v;
    string input = "我的名字是阿曼库马尔";
    
    char* ptr = strtok((char*)input.c_str(), " ");
    v.push_back((字符串)ptr);

    而(指针)
    {
        ptr = strtok(NULL," ");
        v.push_back((字符串)ptr);
    }
    cout

现在的问题。我认为这个问题与命令有关:

(string)ptr

这个东西在第一次调用中完美运行,但在 while 循环中出现错误。如果我将其注释掉并打印 ptr,那么它可以正常工作,但是程序在 while 循环之后终止,甚至不执行

cout<<"coming out";

不理会向量的内容。但是同样,如果我也不打印 ptr,那么存储在向量中的第一个令牌“My”会被打印出来。我真的找不到导致这种情况的原因。任何建议都会有所帮助。

【问题讨论】:

  • 您似乎正在使用不允许的空指针调用字符串构造函数。
  • 这能回答你的问题吗? Assign a nullptr to a std::string is safe?
  • char* ptr = strtok((char*)input.c_str(), " "); 这可能是个问题。 c_str() 返回 const C 字符串,您不得更改它。但是您正在使用(char*)input.c_str() 删除常量并将其传递给strtokstrtok 修改字符串。这会导致未定义的行为。
  • 你不能修改input.c_str()的结果。使用std::string 函数和&lt;algorithm&gt; 代替旧的C 接口。
  • 简短而安全的版本:std::istringstream stream(input); std::copy(std::istream_iterator&lt;string&gt;(stream), std::istream_iterator&lt;string&gt;(), std::back_inserter(v));.

标签: c++ string vector c-strings strtok


【解决方案1】:

while(ptr)
{
    ptr = strtok(NULL," ");
    v.push_back((string)ptr);
}

对于最后一个标记ptr 将为空,从空指针构造std::string 是未定义的行为。试试:

while(ptr = strtok(NULL," "))
{
    v.push_back(string(ptr));
}

更多 c++ 解决方案:

#include <vector>
#include <string>
#include <iostream>
#include <sstream>

int main()
{
    std::vector<std::string> v;
    std::string input = "My name is Aman Kumar";
    
    std::stringstream ss(input);
    
    std::string word;
    while(ss >> word)
    {
        v.push_back(word);
    }
    std::cout << "Coming out\n";
    for(std::string& s:v)
    {
        std::cout << s << "\n";
    }
}

【讨论】:

  • 非常感谢。你能告诉我为什么在最后一个 for 循环中你使用 string& 而不是 string 吗? For 循环肯定有相同的对象而不是复制对象,对吧?
  • @ProfessorofStupidity 向量v 是同一个对象,但循环变量s 的作用与其他变量相同。
  • @ProfessorofStupidity 如果没有引用,您将在向量中复制不必要的字符串
【解决方案2】:

在尝试从中创建字符串之前,您不知道ptr 是否为nullptr(并使用nullptr 调用std::string 构造函数是UB)

您需要重新组织循环,例如像这样:

char* ptr = strtok(input.data(), " ");

while(ptr)
{
    v.push_back(std::string(ptr));
    ptr = strtok(NULL," ");
}

附带说明,不要在 C++ 中使用 C 风格的强制转换语法。它很可能隐藏问题,而 C++ 语法提供了更安全的替代方案。

在 C++ 中去掉 const 和修改结果是 UB,所以可以用 data 调用替换第一个转换(它在需要时返回指向非 const 的指针)。如果你没有C++11,那么无论如何这都是UB,因为字符串不能保证将内存存储在连续内存中,你需要使用different methods

【讨论】:

  • 抛弃常量不是问题(否则不可能与大量 C 代码交互)。导致未定义行为的是后续修改。
【解决方案3】:

您正在将 std::string 与 C-ish strtok 函数混合使用。这真是个坏主意。 std::string 或多或少与 const char * 可逆,但与可变的char * 不可逆。不说空指针问题。所以选择一种方法并坚持下去。

  1. C-ish 一:构建一个普通的 char 数组并存储 char * 的向量:

     char *cstr = strdup(input.c_str());
     ptr = strtok(cstr, " ");
     while(ptr) {
         v.push_back(ptr);
         ptr = strtok(NULL, " ");
     }
    
     cout<<"Coming out";
     for(char *s:v)
     {
         cout<<s<<endl;
     }
    
     free(cstr);         // always free what has been allocated...
    
  2. C++ 一,使用std::stringstream:

     std::stringstream fd(input);
     for(;;) {
         std::string ptr;
         fd >> ptr;
         if (! fd) break;
         v.push_back(ptr);
     }
    
     cout<<"Coming out";
     for(std::string s: v)
     {
         cout<<s<<endl;
     }
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-08
    • 2019-06-23
    • 2022-01-11
    • 1970-01-01
    • 2023-01-17
    • 1970-01-01
    • 2019-10-26
    • 2017-04-11
    相关资源
    最近更新 更多