0

因此,我正在创建一个函数,仅当字符串是向量中尚未包含的唯一字符串时,才会将新字符串添加到字符串向量中。这是我的代码:

void CityMapper::addToVector(string& s)
{
    bool newWord = true;
    if(numAirports > 0)
    {
        for(int i = 0; i < numAirports; i++)
        {
            if(airportNames[i].compare(s) == 0)
                newWord = false;
        }
    }
    if(newWord == true)
    {
        airportNames.pushBack(s);
        numAirports++;
    }
}

airportNames 是字符串的向量。当我运行该程序时,它在 Valgrind 中出现以下错误:

Process Terminating with default action of signal 11 (SIGSEGV)
Access not within mapped region at address 0x0

在此之前,此错误出现在 Valgrind 终端中:

Invalid Read of Size 8

两者都发生在 string.compare() 行。有谁知道为什么会发生这种情况?我还尝试了参数中没有 & 符号的函数。

编辑:我接受了 Derek 的建议并做了他所有的改变。现在程序以不同的方法进行段错误,我从文件中读取字符串。这是该函数的代码:

void CityMapper::getCities()
{
    ifstream fin;
    fin.open(flightDataFile);
    fin >> numAirports;
    string tempCity1, tempCity2, tossout;
    while(getline(fin, tempCity1, ','))
    {
        fin.ignore(1);
        getline(fin, tempCity2, ',');
        fin.ignore(1);
        getline(fin, tossout, '\n');
        addToVector(tempCity1);
        addToVector(tempCity2);
    }
}

以下是来自 Valgrind 的错误消息:

==8357== Use of uninitialised value of size 8
==8357==    at 0x4EF158B: std::basic_string<char, std::char_traits<char>,     
std::allocator<char> >::basic_string(std::string const&) (in /usr/lib/x86_64-linux-
gnu/libstdc++.so.6.0.17)
==8357==    by 0x402214: CityMapper::getCities() (in 
/home/charlie/NetBeansProjects/Lab4/Lab4)
==8357==    by 0x401EB7: CityMapper::run() (in /home/charlie/NetBeansProjects/Lab4/Lab4)
==8357==    by 0x4050A0: main (in /home/charlie/NetBeansProjects/Lab4/Lab4)
==8357== 
==8357== Invalid read of size 4
==8357==    at 0x4EF158B: std::basic_string<char, std::char_traits<char>, 
std::allocator<char> >::basic_string(std::string const&) (in /usr/lib/x86_64-linux-
gnu/libstdc++.so.6.0.17)
==8357==    by 0x402214: CityMapper::getCities() (in   
/home/charlie/NetBeansProjects/Lab4/Lab4)
==8357==    by 0x401EB7: CityMapper::run() (in /home/charlie/NetBeansProjects/Lab4/Lab4)
==8357==    by 0x4050A0: main (in /home/charlie/NetBeansProjects/Lab4/Lab4)
==8357==  Address 0xfffffffffffffff8 is not stack'd, malloc'd or (recently) free'd
==8357== 
==8357== 
==8357== Process terminating with default action of signal 11 (SIGSEGV)
==8357==  Access not within mapped region at address 0xFFFFFFFFFFFFFFF8
==8357==    at 0x4EF158B: std::basic_string<char, std::char_traits<char>, 
std::allocator<char> >::basic_string(std::string const&) (in /usr/lib/x86_64-linux-
gnu/libstdc++.so.6.0.17)
==8357==    by 0x402214: CityMapper::getCities() (in 
/home/charlie/NetBeansProjects/Lab4/Lab4)
==8357==    by 0x401EB7: CityMapper::run() (in /home/charlie/NetBeansProjects/Lab4/Lab4)
==8357==    by 0x4050A0: main (in /home/charlie/NetBeansProjects/Lab4/Lab4)
4

3 回答 3

6

compare不会导致分段错误。这是您尝试调用它的对象,它不存在。作为一般规则:永远不要存储冗余信息(numAirports在这种情况下)。如果您必须存储冗余信息,您至少应该检查一致性。在您在上面发布的代码中,格式错误的输入文件会使您的应用程序崩溃。只需numAirports完全删除,您不需要它。

相反,请使用以下内容:

void CityMapper::addUnique( const string& name )
{
    if ( std::find( airportNames.begin(), airportNames.end(), name )
         == airportNames.end() )
    {
        // Name does not yet exist -> add it
        airportNames.push_back( name );
    }
}

注意事项:

  • 该方法已重命名以反映其语义。您不应在函数名称(向量)中编码数据类型。
  • 签名已更改为const参考。该方法不会更改此参数,这就是以编译器可以验证的方式记录的方式。
  • 参数名称已更改为name。毕竟,这就是它所代表的。
  • 循环已替换为库代码 ( std::find)。不要试图重新发明轮子。

正如评论中所指出的,如果没有令人信服的理由使用std::vector你可能应该看看std::setor std::unordered_set。两个容器都存储独特的对象,因此您不必编写自己的addUnique.

于 2013-10-28T16:57:30.563 回答
1

如果您必须std::vector为此使用 a (而不是 a std::unordered_set):

void CityMapper::addToVector(const string& s) // you aren't changing the string, pass it by const-reference
{
    std::vector<std::string>::iterator it = std::find(airportNames.begin(), airportNames.end(), s);
    if (it == airportNames.end()) // not found, so unique
    {
        airportNames.push_back(s);
    }
}

std::vector有一个 size 成员(所以numAirports是不必要的),并且使用该std::find函数消除了额外的条件检查。

或者,您可以使用std::unordered_setorstd::set并完全避免该std::find操作(尽管这会使用更多内存):

void CityMapper::addToVector(const string& s)
{
    std::set<std::string> airports(airportNames.begin(), airportNames.end());
    airports.insert(s);
    airportNames.assign(airports.begin(), airports.end());
}

更好的解决方案是将数据简单地存储在一个集合中:

void CityMapper::addUnique(const string& s)
{
    // assume airportNames is defined as a std::set<std::string>
    airportNames.insert(s);
}

但是对于您最初的问题: seg 错误可能是由于您没有使用std::vector的实际大小,而是存储了一个单独的numAirports值(这可能没有保持同步,如果您进行更改,则没有必要)上面提到的),因此当您尝试访问未分配给向量(并且可能根本未分配)的内存时,您正在调用未定义的行为。所有这些都随着更好的代码而消失。

编辑

使用您的附加信息,还应注意您的getCities功能可以大大简化:

struct line_reader : std::ctype<char>
{
    line_reader() : std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table()
    {
        static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());
        rc['\n'] = std::ctype_base::space;
        rc[','] = std::ctype_base::space;
        rc[' '] = std::ctype_base::alpha; // allowing city names with spaces
        return &rc[0];
    }
};

void CityMapper::getCities()
{
    ifstream fin(flightDataFile);
    line_reader reader;
    fin.imbue(std::locale(std::locale(), &reader));
    // if using a set
    std::copy(std::istream_iterator<std::string>(fin), std::istream_iterator<std::string>(), std::inserter(airportNames, airportNames.begin()));  

    // or, if using a vector
    // NOTE:  to keep this exception safe 
    // (e.g. keep the values in the actual vector unique even if something happens after we've read in the file data, 
    // pull the values into a temp vector and then swap
    std::vector<std::string> temp;
    std::copy(std::istream_iterator<std::string>(fin), std::istream_iterator<std::string>(), std::back_inserter(temp));
    std::sort(temp.begin(), temp.end());
    temp.erase(std::unique(temp.begin(), temp.end()), temp.end());
    std::swap(airportNames, temp);
}
于 2013-10-28T16:56:19.023 回答
0

把代码改成这样。我更改了一些我正在使用的东西airportNames.size(),它们返回了 airportNames 向量中的元素数量。这将防止在向量上调用 pop_back 或 remove 但未更新 numAirports 时出现任何错误。这是段错误的最可能原因。

我还修正了我希望你的airportNames.push_back()电话打错的地方。你有airportNames.pushBack()哪个语法不正确。

最后只是一个风格的东西,但newWord == true只是说if( newWord)两个陈述都评估为真是多余的。

void CityMapper::addToVector(const string& s)
{
    bool newWord = true;
    for(int i = 0; i < airportNames.size(); ++i)
    {
        if(s == airportNames[i])
        newWord = false;
        break;
    }
    if(newWord)
    {
        airportNames.push_back(s);
    }
}
于 2013-10-28T16:46:51.480 回答