2012-01-06 4 views
1

내 프로그램에 문제가 있습니다. 작은 수의 가장자리에 대해서는 완벽하게 작동하지만 방향 그래프의 가장자리가 15000 개가되면 1 분 런타임 후에 세그먼트 오류가 발생합니다. 디버거는 벡터 푸시 백 메소드에 의해 throw됩니다. 누군가가 코드에 무엇이 잘못되었는지, 어떻게 피하는 지 알고 있습니까?std :: vector :: push_back이 세그먼트 화 오류를 throw합니다.

result.push_back (tmpResult)에서 dfs 프로 시저에 오류가 발생했습니다.

#include <cstdlib> 
#include <iostream> 
#include <vector> 

using namespace std; 

typedef struct { 
    unsigned int endNode;  // Number of dest node 
    bool used;     // true, if edge was used in dfs 
} EdgeType; 

typedef struct { 
    unsigned int startNode;  // Number of source node 
    vector<EdgeType> edge;  // Outgoing edges from node 
} NodeType; 

typedef struct { 
    unsigned int startNode; 
    unsigned int endNode; 
} ResultType; 


bool loadInput(vector<NodeType>& graph, unsigned int& numEdges); 
void dfs(vector<NodeType>& graph, unsigned int i, unsigned int numEdges, vector<ResultType>& result); 

int main(int argc, char** argv) { 
    vector<NodeType> graph; 
    vector<ResultType> result; 
    unsigned int numEdges; 

    result.reserve(300000); 

    // Generate oriented multigraph (3 nodes, 150000 edges) 
    numEdges = 150000; 
    NodeType tmpNode; 
    EdgeType tmpEdge; 

    for (unsigned int i = 0; i < 50000; i++) { 
     tmpEdge.used = false; 
     tmpEdge.endNode = 1; 
     tmpNode.edge.push_back(tmpEdge);  
    } 
    tmpNode.startNode = 0; 
    graph.push_back(tmpNode); 
    tmpNode.edge.clear(); 

    for (unsigned int i = 0; i < 50000; i++) { 
     tmpEdge.used = false; 
     tmpEdge.endNode = 2; 
     tmpNode.edge.push_back(tmpEdge);  
    } 
    tmpNode.startNode = 1; 
    graph.push_back(tmpNode); 
    tmpNode.edge.clear(); 

    for (unsigned int i = 0; i < 50000; i++) { 
     tmpEdge.used = false; 
     tmpEdge.endNode = 0; 
     tmpNode.edge.push_back(tmpEdge);  
    } 
    tmpNode.startNode = 2; 
    graph.push_back(tmpNode); 
    tmpNode.edge.clear(); 

    cout << "numEdges: " << numEdges << endl; 

    // Find way 
    for (unsigned int i = 0; i < graph.size(); i++) { 
     dfs(graph, i, numEdges, result); 
    } 

    // No way found 
    cout << "-1" << endl; 

    return 0; 
} 

void dfs(vector<NodeType>& graph, unsigned int i, unsigned int numEdges, vector<ResultType>& result) { 
    // Way was found, print it and exit program (bad style, only for testing) 
    if (numEdges == result.size()) { 
     cout << graph.size() << endl; 
     vector<ResultType>::iterator it; 
     for (it = result.begin(); it != result.end(); it++) { 
      cout << (*it).startNode << " " << (*it).endNode << endl; 
     } 
     cout << "0 0" << endl; 
     exit(0); 
    } 
    // For each outgoing edge do recursion 
    for (unsigned int j = 0; j < graph[i].edge.size(); j++) { 
     if (i >= graph.size()) return; 
     if (!graph[i].edge[j].used) { 
      graph[i].edge[j].used = true; 
      ResultType tmpResult; 
      tmpResult.startNode = graph[i].startNode; 
      tmpResult.endNode = graph[i].edge[j].endNode; 
      result.push_back(tmpResult); 
      dfs(graph, graph[i].edge[j].endNode, numEdges, result); 
      result.pop_back(); 
      graph[i].edge[j].used = false; 
     } 
    } 
} 

내 프로그램 나를 위해 목표는 각 에지는 한 번만 사용 중심의 그래프에서 방법을 찾아야하는 것입니다.

+0

좁힐 수 없습니까? –

+1

'graph [i]'를 사용하여 벡터에 접근해서는 안됩니다. 'graph.at (i)'를 사용하는 것이 훨씬 안전합니다. 왜냐하면 그래프가 충분히 크고 지금은 예외를 던질 것이기 때문입니다. '[i]'의 위험은 배열의 범위를 벗어나 액세스하려고 시도하고 메모리가 손상되어 seg 결함 또는 유사한 오류를 일으킬 수 있다는 것입니다. 어떤 이들은'.at (i)'가 더 느리지 만 무시할 것이라고 주장하려고 시도 할 수도 있습니다 :-) 실제 프로그램에서 여러분의 프로파일은 대개 무시할 만하다는 것을 확인합니다. 최적화하기 전에 정확성에 집중하십시오. –

+0

@Aaron : 범위를 벗어난 색인 생성은 프로그래밍 오류, 즉 버그입니다. 'operator []'대신'.at()'를 사용하면 버그가 수정되지 않고 단지 다르게 나타납니다. 조언은 실제로 버그가 _fix_되어야하며, 버그가 발생했을 때 예외를 throw하지 않아야합니다. – ildjarn

답변

6

dfs은 반복적으로 호출합니다. numEdges을 늘리면 재귀 수준이 증가하므로 결과적으로 numEdges이 증가하면 스택 오버플로가 충분히 발생합니다 (플랫폼에서 Segfault로 나타남).

큰 스택 크기 (컴파일러 관련)로 프로그램을 빌드하거나이 시나리오에서는 재귀를 사용하지 마십시오.

+0

+1 예외가 발생했기 때문에 그냥 메모리 부족 일 수 있습니다. 스택 공간이 부족하면 그냥 죽어 야합니다. –

+0

@VladLazarenko : 질문에 세분화 오류 (예외는 아님) –

+0

@Vlad : 예외가 발생하지 않으며 디버거가 올바르지 않습니다. 실제로 문제를 야기하는 선 ('dfs'에 대한 재귀 호출)보다 먼저 선을 표시합니다. – ildjarn

0

push_back이 throw되는 경우 프로그램에서 메모리가 부족한 것이 원인 일 가능성이 큽니다. 예외를 catch하지 않으므로 기본 예외 처리기가 호출되고 응용 프로그램이 종료됩니다. gdb에서 catch throw 명령을 사용하여 모든 "throw"문을 중지 할 수 있습니다.

+1

그는 segfault를 얻습니다. 실제 예외는 없습니다. – ildjarn

+0

@ildjarn : 음, OP는 "디버거가 .... 던져 버린다"고 말합니다. 나는 GDB가 신호에 대해 말하는 것을 상상할 수 없습니다. "프로그램 수신 신호 ..."와 같이 말입니다. –

+1

OP는 실제로 "* segmentation fault ...가 throw됩니다"*라고 말하면서 분명히 무의미합니다. ; -] – ildjarn

1

스택 오버플로를 유발할 가능성이 너무 큽니다. 대부분의 플랫폼에서 스택의 크기는 고정되어 있습니다. 당신은 그것을 더 크게 만들 수 있을지도 모르지만, 당신은 아마도 임의로 큰 그래프를 지원할 수 없을 것입니다.

아마도 반복 알고리즘을 사용하여 재귀를 대체하고 역 추적 할 때 복원해야하는 상태 (예 : std::stack)를 유지할 수 있습니다. 그래프 크기에 대한 유일한 제한은 사용 가능한 메모리입니다.

+0

예, 여기 있습니다. 고맙습니다. 더 큰 스택에서는 괜찮습니다. 재귀없이 프로그램을 다시 작성해야합니다. – user1134632

관련 문제