首页 > 解决方案 > 尝试读取/写入 Graphviz DAG 值的工作证明会导致段错误

问题描述

更新:在使用 -g 编译并检查有问题的每一行之后,它现在已经设法在系统级头文件中进行段错误:

Program received signal SIGSEGV, Segmentation fault.
__gnu_cxx::new_allocator<char*>::construct<char*, char* const&> (this=0x7fffffffdec0, 
__p=0x5555557702f0, __args#0=<error reading variable>)
at /usr/include/c++/8.1.1/ext/new_allocator.h:136
136             { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }

这是我以前从未见过的。

所以我有一个非常简单的 C++ 程序,它至少尝试同时写入和读取文件,以确保 DAG 中绝对没有重复值(更改了变量名以保护个人交易秘密):

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <vector>

struct parsedline {
    unsigned long long pre_edge;
    unsigned long long post_edge;
};

std::vector<parsedline> readfile(std::string name) {
    //create array of parsedlines for later use
    std::vector<parsedline> edges;

    //count lines and create array of lines
    int linecount = 0;
    std::vector<char*> lines;
    std::ifstream dag(name);
    std::string ul;

    while (std::getline(dag, ul)) {

        //convert to c_str immediately to avoid errors with atoi
        std::istringstream thisline;
        thisline.str(ul);
        lines[linecount] = const_cast<char*>(thisline.str().c_str());
        ++linecount;

    }

    //drop block definition in graphviz file representing DAG
    int max_readable_line = sizeof(lines) - 2;
    std::vector<char*> readable_lines;

    for (int i = 0; i < max_readable_line; i++) {

        //shift index to skip line 1
        int rlindex = i+1;
        readable_lines[i] = lines[rlindex];

    }

    //read integers from each line, store them in array of parsedline objects
    for (int j = 0; j < max_readable_line; j++) {

        int edge_begin, edge_end;
        if (std::sscanf(readable_lines[j], "   %d -> %d", &edge_begin, &edge_end)) {
            edges[j].pre_edge = edge_begin;
            edges[j].post_edge = edge_end;
        }

    }
    return edges;
}

int main (int argc, char **argv) {

    unsigned long long minperrank = atoi(argv[1]);
    unsigned long long maxperrank = atoi(argv[2]);
    unsigned long long minranks = atoi(argv[3]);
    unsigned long long maxranks = atoi(argv[4]);
    double edgechance = atof(argv[5]);
    std::string filedest(argv[6]);

    unsigned long long i,j,k,l,nodes = 0;
    std::srand(time(NULL));

    unsigned long long ranks = minranks + (std::rand() % (maxranks - minranks + 1));

    std::ofstream dagout(filedest, std::ofstream::out);
    std::vector<parsedline> lines = readfile(filedest);

    dagout << "digraph {" << std::endl;

    for(i = 0; i < ranks; i++) {
        unsigned long long newnodes = minperrank + (rand() % (maxperrank - minperrank + 1));

        for (j = 0; j < nodes; j++) {

            for (k = 0; k < newnodes; k++) {

                unsigned long long checkval = std::rand() % 100;

                if (checkval < edgechance) {

                    for (l = 0; l < sizeof(lines); l++) {

                        //each new member must be a nonce
                        unsigned long long pree = lines[l].pre_edge;
                        unsigned long long poste = lines[l].post_edge;
                        if (checkval != pree) {

                            if (checkval != poste) {

                                dagout << "   " << j << " -> " << k+nodes << ";" << std::endl;

                            }

                        }

                    }

                }

            }

        }

        nodes += newnodes;

    }

    dagout << "}" << std::endl;
    return 0;
}

这编译得很好,但是当我尝试运行它时:

[realkstrawn93@archlinux Desktop]$ ./gendag 10 10 10 10 0.1 test.gv
Segmentation fault (core dumped)
[realkstrawn93@archlinux Desktop]$ 

这是 gdb 的输出——它说它在 readfile 函数中,但没有具体说明它在该函数中的位置:

[realkstrawn93@archlinux Desktop]$ gdb gendag
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gendag...(no debugging symbols found)...done.
(gdb) r 10 10 10 10 0.1 test.gv
Starting program: /home/realkstrawn93/Desktop/gendag 10 10 10 10 0.1 test.gv

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555675 in readfile(std::__cxx11::basic_string<char, 
std::char_traits<char>, std::allocator<char> >) ()

我需要知道段错误在该函数中的确切位置。

标签: c++linuxgraphvizdirected-acyclic-graphs

解决方案


//count lines and create array of lines
int linecount = 0;
std::vector<char*> lines;  <--
std::ifstream dag(name);
std::string ul;

while (std::getline(dag, ul)) {

    //convert to c_str immediately to avoid errors with atoi
    std::istringstream thisline;
    thisline.str(ul);
    lines[linecount] = const_cast<char*>(thisline.str().c_str()); <--
    ++linecount;

}

lines[linecount]在初始化之前访问它。尽管向量被初始化为空,但它的元素没有被初始化。利用

lines.push_back(const_cast<char*>(thisline.str().c_str()));

[]如果您只需要将一个元素附加到向量的末尾,而不是访问器运算符 ( )。

否则,如果您必须按位置访问元素,请首先通过将参数传递给构造函数进行初始化,如

std::vector<char*> lines(max_lines);

这将初始化向量以包含max_lines空对象。

当您的程序访问不属于它的内存时,就会发生分段错误。由于向量总是在堆上分配,因此访问超出其末尾的任何数据都会导致段错误。


推荐阅读